Skip to content

Package layout

docx_plus/
├── __init__.py              # top-level re-exports (DocxPlusError, __version__)
├── py.typed                 # PEP 561 marker — the package ships its own types
├── core/                    # foundation primitives — every capability depends on these
│   ├── __init__.py          # re-exports the public surface
│   ├── errors.py            # DocxPlusError — the base of every typed error
│   ├── borders.py           # Border, border_attrs — shared by page, table, cell borders
│   ├── ns.py                # W, W14, W15, R, MC, A, XML constants + NSMAP / BUILD_NSMAP + qn()
│   ├── oxml.py              # el(), sub(), xpath(), remove(),
│   │                        # build_complex_field, insert_before_first_anchor
│   ├── ids.py               # IdRegistry, ParaIdRegistry, _IdRegistryBase, DuplicateIdError
│   └── parts.py             # get_or_create_part, PartSpec,
│                            # COMMENTS/COMMENTS_EXTENDED/FOOTNOTES/ENDNOTES_SPEC
├── styles/                  # inspect, modify, theme
│   ├── __init__.py          # re-exports every public symbol from the submodules
│   ├── inspect.py           # resolve_effective_formatting + ResolvedFormatting + FormattingSource
│   │                        # + resolve_paragraph_spacing (contextualSpacing / the applied gap)
│   ├── modify.py            # create_style, modify_style, apply_style, delete_style,
│   │                        # ensure_style, find_matching_style, remap_styles, list_styles,
│   │                        # StyleProxy, StyleInfo, _BUILTIN_STYLES table
│   ├── sweep.py             # iter_resolved_paragraphs, ResolvedParagraph, ResolvedRun — v0.6
│   └── theme.py             # ThemeColors, load_theme, resolve_theme_color,
│                            # apply_theme_tint, apply_theme_shade, apply_lum_mod, apply_lum_off
├── controls/                # content controls (SDTs)
│   ├── __init__.py          # re-exports the public surface
│   ├── builder.py           # FormBuilder, MissingNamespaceError, DropdownItem
│   └── read.py              # ControlValue, list_controls, read_controls, set_control_value,
│                            # clear_control, WRITABLE_TYPES, ControlNotFoundError,
│                            # DuplicateTagError, ValueNotInListError, ControlTypeError
├── fields/                  # complex field insertion, reads, update flag
│   ├── __init__.py          # re-exports the public surface
│   ├── simple.py            # add_page_number_field, add_date_field, add_field,
│   │                        # PageFieldName Literal
│   ├── read.py              # read_fields, FieldInstance — v0.6
│   └── update.py            # mark_fields_dirty
├── protection/              # document-level protection enforcement
│   ├── __init__.py          # re-exports the public surface
│   └── document.py          # protect_document, unprotect_document, is_protected,
│                            # ProtectionMode Literal
├── comments/                # anchored, threaded comments — v0.2 / v0.4
│   ├── __init__.py          # re-exports the public surface
│   ├── anchor.py            # add_comment, edit_comment, delete_comment, clear_all_comments,
│   │                        # CommentRef, CommentTarget, CommentNotFoundError
│   ├── read.py              # read_comments, AnchoredComment
│   ├── threads.py           # reply_to_comment, resolve_comment, reopen_comment,
│   │                        # read_threads, CommentThread — v0.4
│   ├── _extended.py         # commentsExtended.xml thread graph (internal) — v0.4
│   ├── _ids.py              # commentsIds.xml durable ids (internal) — v0.5
│   ├── people.py            # people.xml author presence — v0.5
│   └── registry.py          # CommentIdRegistry
├── layout/                  # page-layout extras — v0.2
│   ├── __init__.py          # re-exports the public surface
│   ├── columns.py           # set_columns
│   ├── breaks.py            # insert_section_break, SectionStartType
│   ├── settings.py          # enable/disable_distinct_even_odd_headers
│   ├── line_numbering.py    # set_line_numbering, LineNumberRestart
│   └── borders.py           # set_page_borders, Border
├── bookmarks/               # bookmarks + REF/PAGEREF cross-references — v0.2
│   ├── __init__.py          # re-exports the public surface
│   ├── anchor.py            # add_bookmark, delete_bookmark, BookmarkRef, BookmarkTarget
│   ├── crossref.py          # add_cross_reference, CrossReferenceKind
│   ├── read.py              # read_bookmarks, BookmarkInfo
│   └── registry.py          # BookmarkIdRegistry
├── notes/                   # footnotes + endnotes — v0.2
│   ├── __init__.py          # re-exports the public surface
│   ├── write.py             # add_footnote, add_endnote, edit_footnote, edit_endnote,
│   │                        # FootnoteRef, EndnoteRef, NoteNotFoundError
│   ├── read.py              # read_footnotes, read_endnotes, NoteContent
│   └── registry.py          # FootnoteIdRegistry, EndnoteIdRegistry
├── numbering/               # custom list definitions — v0.5
│   ├── __init__.py          # re-exports the public surface
│   ├── define.py            # LevelDefinition, define_list_definition, define_bullet_list,
│   │                        # define_numbered_list, InvalidLevelError, MAX_LEVELS
│   ├── apply.py             # apply_list, remove_list, restart_list,
│   │                        # ListDefinitionNotFoundError
│   ├── read.py              # read_list_definitions, ListDefinition, ListLevel
│   └── registry.py          # NumIdRegistry, AbstractNumIdRegistry
├── revisions/               # tracked changes (w:ins / w:del) — v0.3
│   ├── __init__.py          # re-exports the public surface
│   ├── mark.py              # mark_insertion, mark_deletion, RevisionRef,
│   │                        # RevisionTarget, RevisionNotFoundError
│   ├── read.py              # read_revisions, TrackedChange, RevisionType
│   ├── accept.py            # accept_revision, reject_revision,
│   │                        # accept_all_revisions, reject_all_revisions
│   ├── settings.py          # enable_track_changes, disable_track_changes
│   └── registry.py          # RevisionIdRegistry
├── publishing/              # long-document publishing — v0.2
│   ├── __init__.py          # re-exports the public surface
│   ├── toc.py               # add_toc
│   ├── captions.py          # add_caption
│   ├── figures.py           # add_table_of_figures
│   └── _validate.py         # shared caption/label validation (internal)
├── tables/                  # table borders, shading, merging — v0.5
│   ├── __init__.py          # re-exports the public surface
│   ├── borders.py           # set_table_borders, set_cell_borders
│   ├── shading.py           # Shading, set_table_shading, set_row_shading,
│   │                        # set_cell_shading, shading_attrs
│   ├── merge.py             # merge_cells, unmerge_cell, normalize_horizontal_merges,
│   │                        # InvalidMergeError
│   └── read.py              # read_table_formatting, TableFormatting, CellFormatting
├── lint/                    # audit formatting, describe the repair — v0.6
│   ├── __init__.py          # re-exports the public surface
│   ├── models.py            # Finding, Issue, Location, Fix, FixOperation, Rule,
│   │                        # LintContext, RuleKind, Severity, FixOp, FixSafety
│   ├── registry.py          # rule decorator, all_rules, select_rules
│   ├── engine.py            # lint — one sweep, then every selected rule
│   ├── plan.py              # plan_fixes, FixPlan, PlannedFix, FixConflict
│   ├── profile.py           # Profile, RuleSettings, InvalidProfileError
│   └── rules/               # the twenty registered rules, by subject
│       ├── formatting.py    # style-drift, redundant-direct-formatting, ...
│       ├── typography.py    # double-space, trailing-whitespace, ...
│       ├── structure.py     # heading-level-skip, manual-list, ...
│       ├── styles.py        # unused-styles, duplicate-styles
│       ├── _common.py       # container adjacency, the one `paragraph._p` reach
│       └── references.py    # broken-cross-reference, caption-manual-numbering
├── cli/                     # docx-plus console entry point — v0.3
│   ├── __init__.py          # build_parser, main (console_scripts entry point)
│   ├── __main__.py          # python -m docx_plus.cli shim
│   ├── inspect.py           # inspect subcommand — effective formatting dump
│   ├── restyle.py           # restyle subcommand — remap_styles onto canonical ids
│   ├── controls.py          # controls subcommand — list / set / clear control values
│   ├── comments.py          # comments subcommand — list / resolve / reopen threads
│   ├── skill.py             # skill subcommand — path / list / show / install — v0.5
│   ├── lint.py              # lint subcommand — report findings — v0.6
│   ├── plan.py              # plan subcommand — describe the repair — v0.6
│   └── _io.py               # CliError + shared load/save/output helpers
├── skill/                   # packaged agent skill (Markdown, ships in the wheel) — v0.5
│   ├── SKILL.md             # entry point: frontmatter + capability map
│   └── reference/           # one topic file per capability, loaded on demand
├── examples/                # runnable demo scripts
│   ├── inspect_document.py, restyle_existing.py, build_form.py, populate_form.py
│   ├── add_comments.py, multi_column_layout.py, bookmarks_and_xrefs.py,
│   │   footnotes_and_endnotes.py     # v0.2 demos
│   ├── publishing_layout.py            # v0.2 expansion demo
│   ├── track_changes.py                # v0.3 demo
│   ├── threaded_comments.py            # v0.4 demo
│   ├── table_formatting.py, custom_numbering.py   # v0.5 demos
│   └── lint_document.py                # v0.6 demo
└── _testing/                # internal test helpers (not public API)
    ├── __init__.py
    └── ooxml_asserts.py     # assert_ids_unique, assert_style_defined,
                             # count_controls, assert_protected, assert_field_dirty

The flat structure is deliberate. Each capability (styles/, controls/, …) sits as a sibling of core/, never deeper. There is no _internal/ hidden layer; _testing/ is the only underscore-prefixed package, and it is explicitly excluded from the public surface (docx_plus/_testing/** ignores Google-docstyle in pyproject.toml).

The dependency rule that keeps it flat — no capability imports another — is invariant 1, and cli/ and lint/ are its two documented exceptions.