python-docx: Word Document Manipulation

SkillFiles & storage

Manipulate Word .docx files with python-docx, including handling split placeholders across runs and conditional sections.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the python-docx: Word Document Manipulation skill

What this skill tells your AI

The instructions your AI receives, as published by cxcscmu/skilllearnbench in skills/b1-one-shot-claude-opus-4-6/offer-letter-generator/python-docx/SKILL.md and read by ahel’s review.

Installation

pip install python-docx

Core Concepts

Split Placeholders Across Runs

Word often splits text into multiple XML runs due to spell-check, formatting changes, or editing history. A placeholder like {{COMPANY_NAME}} may be stored as separate runs: ['{{COMP', 'ANY_NAME}}'].

Solution: Join all run texts in a paragraph, perform replacements on the joined text, then redistribute back to runs (clearing extra runs).

import re
from docx import Document

def replace_in_paragraph(paragraph, replacements):
    """Replace placeholders in a paragraph, handling split runs."""
    full_text = ''.join(run.text for run in paragraph.runs)
    if not any(key in full_text for key in replacements):
        return
    for key, value in replacements.items():
        full_text = full_text.replace(key, value)
    # Put all text in the first run, clear the rest
    if paragraph.runs:
        paragraph.runs[0].text = full_text
        for run in paragraph.runs[1:]:
            run.text = ''

Replacing in All Document Locations

Documents have paragraphs in the body, tables (including nested tables), headers, and footers.

def replace_all_in_doc(doc, replacements):
    # Body paragraphs
    for p in doc.paragraphs:
        replace_in_paragraph(p, replacements)

    # Tables (including nested)
    def process_table(table):
        for row in table.rows:
            for cell in row.cells:
                for p in cell.paragraphs:
                    replace_in_paragraph(p, replacements)
                for nested in cell.tables:
                    process_table(nested)

    for table in doc.tables:
        process_table(table)

    # Headers and footers
    for section in doc.sections:
        for p in section.header.paragraphs:
            replace_in_paragraph(p, replacements)
        for p in section.footer.paragraphs:
            replace_in_paragraph(p, replacements)

Conditional Sections

For {{IF_X}}...{{END_IF_X}} patterns:

  • If condition is true: remove the markers, keep the content
  • If condition is false: remove markers and content entirely
def handle_conditional(paragraph, tag, keep):
    full_text = ''.join(run.text for run in paragraph.runs)
    start_tag = '{{IF_' + tag + '}}'
    end_tag = '{{END_IF_' + tag + '}}'
    if start_tag in full_text:
        if keep:
            full_text = full_text.replace(start_tag, '').replace(end_tag, '')
        else:
            # Remove everything between and including tags
            pattern = re.escape(start_tag) + '.*?' + re.escape(end_tag)
            full_text = re.sub(pattern, '', full_text)
        if paragraph.runs:
            paragraph.runs[0].text = full_text
            for run in paragraph.runs[1:]:
                run.text = ''

Saving

doc.save('output.docx')

Signals

GitHub stars
83
Forks
5
Last commit
Jul 2026

ahel review

  • K1binfo
    installs-packages

Automated review, not a security audit. Ruleset v1+k2.

Advanced
Catalog kind
skill
Gateway key
python-docx
Source
github.com/cxcscmu/skilllearnbench