WolfSDKWolfXLWolfPPTWolfDocx

WolfDocx - migration playbook

Move one python-docx workflow to WolfDocx

List the python-docx calls one workflow uses, map each to its WolfDocx equivalent below, and keep python-docx for the calls marked unsupported. Migrate the narrowest path first and prove the output before you widen it.

Two entry points

DocumentBuilder.open gives python-docx-shaped access: paragraphs, runs, tables, and cell text. A Document session applies guarded edits that refuse stale or ambiguous targets, and one save publishes them. Both are imported from wolfdocx and require WolfDocx Commercial access, described at the end of this page.

Call mapping

python-docx calls and their WolfDocx equivalents
python-docxWolfDocx
Document("in.docx")DocumentBuilder.open("in.docx") for python-docx-shaped reads and edits, or Document("in.docx") from wolfdocx for a guarded edit session
Document()DocumentBuilder()
document.paragraphs, paragraph.textDocumentBuilder.paragraphs, DocumentParagraph.text (read only)
paragraph.runs, run.text (read)DocumentParagraph.runs, DocumentRun.text
run.text = valueDocument.set_run_text(paragraph, run, text, expected_old_text=...). Here the paragraph and run indexes start at 0. Paragraphs count only direct body paragraphs, so a paragraph inside a table is not counted, and runs count only direct runs of the paragraph, so a run inside a hyperlink is not counted. The other session methods below count paragraphs from 1.
paragraph.text = valueNot supported. DocumentParagraph.text has no setter. Use set_run_text or find_replace.
Custom loop to replace text across runsDocument.find_replace(pattern, replacement). The pattern is a regular expression, so wrap literal text in re.escape. A pattern that matches nothing is refused. For reviewed, hash-bound edits see replace text in a Word document.
document.add_paragraph(text, style)DocumentBuilder.write_paragraph(text, style=...) or Document.append_paragraph(text, style=...)
paragraph.add_run(text, bold=True)DocumentParagraph.add_run(text, bold=True)
document.add_heading(text, level)Not supported. No add_heading method. Pass a heading style to write_paragraph(text, style="Heading 1").
document.add_table(rows, cols)DocumentBuilder.add_table(rows, cols) or DocumentBuilder.insert_table(rows); in a session, Document.insert_table(paragraph, rows, grid_widths), which places the table after paragraph number paragraph, counting from 1
document.tables, table.rows, table.cell(r, c).textDocumentBuilder.tables, DocumentTable.rows, DocumentTable.cell(r, c).text. Indexes start at 0.
cell.text = valueDocumentTableCell.text = value through the builder, or Document.set_cell_text(table=, row=, cell=, text=, expected_old_text=) in a session. Session coordinates start at 1. See update table cells in a Word document.
table.add_row()Document.insert_table_row(table, row, cell_texts=[...]) inserts after the row you name (table and row count from 1). Document.delete_table_row(table, row) removes one.
Fill {{placeholder}} text in a templateDocument.find_replace for single values. To repeat a placeholder table row once per record, see fill a Word template table.
document.add_picture(path, width, height)DocumentBuilder.append_image(path, width=, height=) with sizes in EMU. There is no add_picture method.
document.core_properties.title = valueDocumentBuilder.set_core_properties(title=...)
document.save(path)DocumentBuilder.save(path) (a .docx path) or Document.save(path), which refuses an existing destination and returns a report with passed and changed_parts
paragraph.style = value, paragraph.alignment = valueNot supported. Builder paragraphs have no style or alignment property. In a session use Document.format_paragraph(paragraph, style=..., alignment=...), where paragraph counts from 1.
run.bold = True, run.font.size = Pt(12), run.font.color.rgb = valueNot supported. Builder runs are read only and have no font object. Pass bold=True, font_size=, or color= to add_run, or call Document.format_text(paragraph, start, end, bold=True) on existing text, where start and end are character offsets. font_size is written to w:sz unchanged, so it counts half-points.
from docx.shared import Inches, Pt, RGBColorThere is no wolfdocx.shared. Inches(1) is an integer in EMU, so it works for width= and height= on append_image. Pt and RGBColor have no counterpart. Pass half-point integers to font_size and hex strings such as "FF0000" to color.
table.style = valueNot supported. Set the style when you create the table: DocumentBuilder.add_table(rows, cols, style="Table Grid").
table.add_column(width)Document.insert_table_column(table, column, cell_texts=[...]) inserts after the column you name.
paragraph.insert_paragraph_before(text); remove a paragraph through its XML elementDocument.insert_paragraph(paragraph, text, position="before") and Document.delete_paragraph(paragraph). Here paragraph counts from 1, unlike set_run_text.
section.header.paragraphs[0].text = valueDocument.edit_header_footer("header", "create", paragraphs=[{"text": value}])
document.add_page_break()Not supported. There is no page-break method. DocumentBuilder.add_section() and Document.insert_section_break(after) start a new section on a new page.
document.inline_shapes, document.stylesNot supported. DocumentBuilder has neither property.

Index bases differ by call. Builder paragraphs, runs, and cell(r, c) start at 0, and so do the paragraph and run arguments of set_run_text. Every other session argument that names a paragraph, table, row, cell, column, or section starts at 1.

Verified sample

The sample assumes a template.docx with a {{customer}} placeholder in the first paragraph, a second paragraph whose second run reads Draft, and a table with a header row and two data rows.

import re
from wolfdocx import Document, DocumentBuilder

# Read the way python-docx reads: paragraphs, tables, cell text.
doc = DocumentBuilder.open("template.docx")
print([p.text for p in doc.paragraphs])
print(doc.tables[0].cell(1, 1).text)

# Edit in one guarded session, then save a new file.
with Document("template.docx") as session:
    found = session.find_replace(re.escape("{{customer}}"), "Acme Corp")
    assert found.status == "applied", found.code

    cell = session.set_cell_text(table=1, row=2, cell=2, text="12", expected_old_text="10")
    assert cell.status == "applied", cell.code

    row = session.insert_table_row(1, 3, cell_texts=["LATAM", "30"])
    assert row.status == "applied", row.code

    session.set_run_text(1, 1, "Final", expected_old_text="Draft")
    report = session.save("report.docx")

assert report.passed, report.code
print(report.changed_parts)

When the edits touch only the body, report.changed_parts lists word/document.xml and the other package parts keep their original bytes.

Not supported

  • Assigning paragraph.text or run.text on builder objects. Use set_run_text or find_replace in a session.
  • The python-docx method names add_heading, add_paragraph, add_picture, and add_page_break on DocumentBuilder, and the inline_shapes and styles properties. Use the mapped calls above where one exists.
  • Assigning style, alignment, bold, font, or a table style on builder objects. The table above names the call to use instead.
  • set_cell_text edits one simple cell. It refuses a cell it cannot edit and reports the reason in code.
  • WolfDocx does not lay out pages and makes no general Word compatibility promise. See the python-docx alternative overview for when to keep python-docx.

Migration checklist

  1. List every python-docx call in the workflow and mark each as mapped or unsupported using the table.
  2. Freeze one representative template and keep the original file unchanged. Write every test result to a new path.
  3. Replace one code path and keep python-docx for the calls that stay unsupported.
  4. Assert the intended text and table values by reopening the saved file with DocumentBuilder.open.
  5. Compare ZIP parts before and after, and check that changed_parts matches the parts you meant to edit.
  6. Run the migrated path on a small cohort of real files and keep the old path available for rollback.

Get WolfDocx

WolfDocx Commercial is an organization-scoped subscription. Commercial package access uses the authenticated distribution channel. The package credential is issued on the checkout success page after payment and can be reissued through https://wolfxl.com/recover. There is no public free package.

Review the scoped before-and-after memo files in the reporting walkthrough, or ask about your document before purchasing if compatibility is uncertain.

Related tasks