athena-python-docx 0.27.0__tar.gz → 0.29.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/.gitignore +2 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/AGENTS.md +178 -49
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/PKG-INFO +1 -1
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/__init__.py +1 -1
- athena_python_docx-0.29.0/docx/_batching.py +137 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/_buffer.py +122 -12
- athena_python_docx-0.29.0/docx/_capabilities.py +38 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/_http_doc.py +347 -22
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/_run_order.py +149 -3
- athena_python_docx-0.29.0/docx/_tracked.py +29 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/bookmarks.py +13 -9
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/client.py +7 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/commands.py +72 -16
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/comments.py +131 -44
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/document.py +107 -39
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/enum/shape.py +6 -2
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/errors.py +19 -3
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/fields.py +17 -3
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/sdt.py +19 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/section.py +90 -79
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/shape.py +52 -4
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/table.py +239 -56
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/text/hyperlink.py +4 -4
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/text/paragraph.py +130 -16
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/text/run.py +179 -30
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/pyproject.toml +1 -1
- athena_python_docx-0.29.0/scripts/round_trip_smoke.py +426 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/METHODOLOGY.md +49 -1
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/ab_probe_cases.py +21 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +1 -0
- athena_python_docx-0.29.0/tests/fidelity/op_snapshots/54_empty_everything.json +6 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +2 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +1 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +1 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +1 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +1 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +7 -0
- athena_python_docx-0.29.0/tests/fidelity/op_snapshots/rw16_header_footer_tables.json +15 -0
- athena_python_docx-0.29.0/tests/fidelity/op_snapshots/rw17_header_letterhead_filled.json +21 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/real_world_cases.py +34 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fixtures/field_reads.json +160 -1
- athena_python_docx-0.29.0/tests/fixtures/note_reference_reads.json +263 -0
- athena_python_docx-0.29.0/tests/fixtures/ruby_reads.json +204 -0
- athena_python_docx-0.29.0/tests/fixtures/symbol_reads.json +163 -0
- athena_python_docx-0.29.0/tests/live/__init__.py +0 -0
- athena_python_docx-0.29.0/tests/live/baseline.json +262 -0
- athena_python_docx-0.29.0/tests/live/reader.py +243 -0
- athena_python_docx-0.29.0/tests/live/runner.py +742 -0
- athena_python_docx-0.29.0/tests/live/sdk_driver.py +56 -0
- athena_python_docx-0.29.0/tests/live/stock_driver.py +65 -0
- athena_python_docx-0.29.0/tests/live/test_live_runner.py +439 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/parity/intentional_deviations.json +88 -0
- athena_python_docx-0.29.0/tests/test_batch_chunking.py +379 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_cell_fastpath_runs.py +5 -1
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_cell_paragraphs_listed.py +1 -0
- athena_python_docx-0.29.0/tests/test_comment_paragraphs.py +184 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_comments.py +17 -9
- athena_python_docx-0.29.0/tests/test_content_control_value.py +75 -0
- athena_python_docx-0.29.0/tests/test_empty_hyperlinks.py +91 -0
- athena_python_docx-0.29.0/tests/test_export_docx_clean.py +138 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_field_reads_python_docx_parity.py +6 -1
- athena_python_docx-0.29.0/tests/test_field_refresh.py +32 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_field_runs.py +21 -3
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_http_transport.py +5 -1
- athena_python_docx-0.29.0/tests/test_inline_page_break.py +70 -0
- athena_python_docx-0.29.0/tests/test_inline_shape_type.py +95 -0
- athena_python_docx-0.29.0/tests/test_inline_shapes_floating.py +60 -0
- athena_python_docx-0.29.0/tests/test_note_reference_reads_python_docx_parity.py +240 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_parity_misc.py +2 -2
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_phase_b_headers_footers.py +110 -2
- athena_python_docx-0.29.0/tests/test_read_run_ranges.py +153 -0
- athena_python_docx-0.29.0/tests/test_repeated_node_read.py +119 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_revisions.py +55 -1
- athena_python_docx-0.29.0/tests/test_round_trip_smoke_script.py +115 -0
- athena_python_docx-0.29.0/tests/test_ruby_reads_python_docx_parity.py +192 -0
- athena_python_docx-0.29.0/tests/test_run_edges.py +138 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_run_identity.py +1 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_run_mark_inheritance.py +88 -42
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_document_audit.py +44 -54
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_header_footer.py +32 -17
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_misc.py +7 -13
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_table_audit.py +93 -0
- athena_python_docx-0.29.0/tests/test_symbol_reads_python_docx_parity.py +122 -0
- athena_python_docx-0.29.0/tests/test_table_read_reuse.py +266 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_text_control_characters.py +11 -5
- athena_python_docx-0.29.0/tests/test_tracked_revisions_offsets.py +170 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_wire_contract.py +6 -1
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/uv.lock +1 -1
- athena_python_docx-0.27.0/docx/_batching.py +0 -88
- athena_python_docx-0.27.0/scripts/round_trip_smoke.py +0 -231
- athena_python_docx-0.27.0/tests/fidelity/op_snapshots/54_empty_everything.json +0 -4
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/CLAUDE.md +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/DOCX_EXEC_LAB.md +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/README.md +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/_athena_extension.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/_citations.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/_execution.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/_http.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/_image_utils.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/_postproc.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/_ptc.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/_references.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/_table_styles.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/_timeouts.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/api.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/blkcntnr.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/charts.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/drawing/__init__.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/enum/__init__.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/enum/base.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/enum/dml.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/enum/section.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/enum/style.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/enum/table.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/enum/text.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/exceptions.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/footnotes.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/image/__init__.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/image/exceptions.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/image/image.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/math.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/opc/__init__.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/opc/coreprops.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/oxml/__init__.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/revisions.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/session.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/settings.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/shared.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/styles/__init__.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/styles/_table.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/styles/latent.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/styles/style.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/styles/styles.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/text/__init__.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/text/font.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/text/pagebreak.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/text/parfmt.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/text/tabstops.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/toc.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/docx/typing.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/scripts/docx_exec_lab.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/scripts/docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/scripts/dump_wire_fixtures.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/scripts/release.sh +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/scripts/smoke_test_block_not_found.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/scripts/validate_find_replace_asset.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/__init__.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/conftest.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/README.md +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/__init__.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/ab_probe_runner.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/auto_gen_cases.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/binary_round_trip.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/cases.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/complex_cases.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/coverage_report.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/extract.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/extreme_cases.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/fake_session.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/firm_templates/README.md +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/firm_templates/__init__.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/firm_templates/_runner.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/firm_templates/extractor.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/local_runner.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/mega_cases.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshot.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/ours_spec.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/parity_crawl.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/parity_diff.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/round_trip_tests.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/stock_spec.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/parity/README.md +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/parity/__init__.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/parity/baseline_gaps.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/parity/compare.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/parity/introspect.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/parity/reports/gap_report.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/parity/run_parity.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/parity/snapshots/athena_latest.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/parity/test_parity_gap.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_add_section.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_add_section_extract_items.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_athena_extension_creates_session_open.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_athena_extensions_contract.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_athena_extensions_registry.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_batching_perf.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_block_not_found_error.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_buffer.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_cell_add_table_not_supported.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_cell_borders_spec.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_cell_inner_format_stash.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_cell_inner_run_format_stash.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_cell_inner_run_guard.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_cell_merge.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_cell_text_plain_fastpath.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_cell_text_replace_semantics.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_citations.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_collapsed_range_format.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_color_format_theme.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_command_dataclasses.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_commands.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_core_properties.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_document_asset_id_property.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_document_clear.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_document_create.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_document_create_from_template.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_document_factory_validation.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_docx_exec_lab.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_drawing_image.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_enum_parity.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_execution_scope.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_find_replace_session_open.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_hyperlink_coalescing.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_image_url_data_uri.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_inline_shape_alt_text.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_inline_shape_size.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_insert_deferred.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_iter_inner_content.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_list_styles.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_merged_cell_secondary_slot.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_merged_cells.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_missing_modules.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_oxml_shim.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_page_break.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_paragraph_runs_hyperlinks.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_paragraph_text_len_cache.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_parity_round2.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_pdf_export_and_page_renders.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_phase_a_behavior.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_phase_c_tables.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_positional_cell_id.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_postproc_ref_restore.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_pr19766_review_fixes.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_ptc.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_python_docx_api_parity.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_restart_numbering.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_run_add_picture_anchor.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_run_formatting_wire.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_run_table_style_objects.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_run_text_python_docx.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_run_text_refreshes_paragraph.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_sdk_reads_served.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_section_read_lengths.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_add_picture.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_add_run.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_comments_get.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_document_element.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_enum_section.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_font_audit.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_hyperlink.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_inline_shape.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_paragraph_style.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_parfmt.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_row_col_cell.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_run_add_break.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_run_style.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_run_style_strict.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_run_text.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_run_underline.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_section_audit.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_section_dimensions.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_section_onoff.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_settings.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_shared_audit.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_style.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_styles.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_table_cell.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_table_dimensions.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_silent_stub_table_layout.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_smoke_integration.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_style_acceptance.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_style_font.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_style_formatting_writes.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_style_metadata_writes.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_style_names.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_style_setters_contract.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_table_set_cell_perf.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_table_style_id_resolution.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_temporarily_unavailable.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_uv_lock_version.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_validate_find_replace_asset_script.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_xml_attr_guard.py +0 -0
- {athena_python_docx-0.27.0 → athena_python_docx-0.29.0}/tests/test_zod_wire_contract.py +0 -0
|
@@ -25,6 +25,13 @@ Before adding or modifying any API surface:
|
|
|
25
25
|
|
|
26
26
|
Run `uv run pytest tests/test_python_docx_api_parity.py -v -s` to verify parity.
|
|
27
27
|
|
|
28
|
+
The API ratchet sees shape only. For behaviour, `uv run --extra dev python -m
|
|
29
|
+
tests.live.runner --cases <name>` runs a battery case through stock
|
|
30
|
+
python-docx 1.2.0 and through the SDK against a live in-memory DOCX Studio
|
|
31
|
+
and compares what each reads, field by field (`tests/fidelity/METHODOLOGY.md`
|
|
32
|
+
§ L9). CI fails on a live gap `tests/live/baseline.json` does not hold, and
|
|
33
|
+
on a baseline gap that is gone.
|
|
34
|
+
|
|
28
35
|
### Intentionally omitted (current SDK limitations)
|
|
29
36
|
|
|
30
37
|
These standard python-docx members don't apply to a DOCX Studio-backed SDK:
|
|
@@ -33,12 +40,14 @@ These standard python-docx members don't apply to a DOCX Studio-backed SDK:
|
|
|
33
40
|
- `Document.part`, `Paragraph.part`, `Run.part` — package part access
|
|
34
41
|
- `Document.settings` — Word app settings (not surfaced by DOCX Studio)
|
|
35
42
|
- `InlineShape.chart` — charts are not currently surfaced by the SDK.
|
|
36
|
-
- `Comment.
|
|
37
|
-
`Comment
|
|
38
|
-
`
|
|
39
|
-
`
|
|
40
|
-
|
|
41
|
-
`
|
|
43
|
+
- `Comment.add_table` / `Comment.tables` — a DOCX Studio comment holds
|
|
44
|
+
paragraphs only. `Comment` is a `docx.blkcntnr.BlockItemContainer`, as
|
|
45
|
+
`_Cell` and the header/footer proxies are: `paragraphs`,
|
|
46
|
+
`add_paragraph`, `iter_inner_content` and `text` work as in python-docx
|
|
47
|
+
1.2.0 (the paragraphs list the comment's story, `blocks.list` with
|
|
48
|
+
`in: {storyType: "comment"}`, and `add_paragraph` sends
|
|
49
|
+
`CommentsParagraphAppend`), while `tables` is empty and `add_table`
|
|
50
|
+
raises `CommentsNotImplementedError`.
|
|
42
51
|
- **`_Cell.add_table(rows, cols)`** — surface preserved for parity
|
|
43
52
|
(signature matches python-docx, no `width` kwarg) but raises
|
|
44
53
|
:class:`docx.errors.NestedTableNotSupportedError` (subclass of
|
|
@@ -181,22 +190,33 @@ not file-backed. Each is documented in the relevant docstring.
|
|
|
181
190
|
on the other studios: `Workbook.workbook_id` (xlsx) and
|
|
182
191
|
`Presentation.deck_id` (pptx).
|
|
183
192
|
|
|
184
|
-
- **`Document.save(path_or_stream=None)`** — argument is optional
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
193
|
+
- **`Document.save(path_or_stream=None)`** — the argument is optional.
|
|
194
|
+
Stock python-docx requires it and `TypeError`s on no-arg, but writes
|
|
195
|
+
are always in place against the Y.Doc, and forcing the upstream
|
|
196
|
+
signature broke every agent invocation that reflexively called
|
|
197
|
+
`doc.save()`, so the no-arg form flushes and writes no file. With a
|
|
198
|
+
path (`str` or `os.PathLike`) or a binary stream, `save` also writes
|
|
199
|
+
the document's `.docx` there, as python-docx does, through
|
|
200
|
+
`export_docx` (0.29.0+; earlier versions raised
|
|
201
|
+
`LocalSaveTargetNotSupportedError`, which stays importable).
|
|
202
|
+
|
|
203
|
+
- **`Document.track_revisions: bool`** — when `True`, subsequent batches
|
|
204
|
+
are sent with `changeMode: "tracked"`, and docx-studio records their text
|
|
205
|
+
edits as Word revisions (`w:ins` / `w:del`): run and paragraph text,
|
|
206
|
+
`find_replace` in the body, new paragraphs and headings (an inserted
|
|
207
|
+
paragraph mark), tabs and line breaks, run formatting (`w:rPrChange`) and
|
|
208
|
+
`paragraph_format` properties (`w:pPrChange`). The replaced text stays, deleted,
|
|
209
|
+
and the new text follows it, so the SDK keeps its paragraph lengths and
|
|
210
|
+
run ranges that way (`docx/_tracked.py`): a tracked `run.text = value`
|
|
211
|
+
leaves the run over `value`, after its deleted text, and the paragraph
|
|
212
|
+
grows by `len(value)`. Every batch says so to docx-studio
|
|
213
|
+
(`client.capabilities: ["trackedAuthoring"]`); a docx-studio refuses a
|
|
214
|
+
tracked edit from an SDK that does not. A batch holding an edit it cannot
|
|
215
|
+
record yet (paragraph styles, tables, pictures, lists, sections, notes,
|
|
216
|
+
page breaks, header/footer/comment text) is refused (HTTP 422,
|
|
217
|
+
`TrackedChangesUnsupported`) and applies nothing; reads, exports,
|
|
218
|
+
comments and `TrackChangesDecide` run either way
|
|
219
|
+
(`DOCX_STUDIO_LIMITATIONS.md` § 32).
|
|
200
220
|
python-docx upstream has no top-level toggle (the closest is
|
|
201
221
|
hand-edited `<w:trackChanges/>` on `Document.settings.element`).
|
|
202
222
|
We expose this as a Document-level property because every DOCX Studio
|
|
@@ -212,10 +232,12 @@ not file-backed. Each is documented in the relevant docstring.
|
|
|
212
232
|
because the upstream package has no track-changes API at all.
|
|
213
233
|
|
|
214
234
|
- **`Document.user_name: str | None` / `Document.user_email: str |
|
|
215
|
-
None`** —
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
235
|
+
None`** — the name sent with each batch, which DOCX Studio uses as the
|
|
236
|
+
author of a comment that names none. It is not a tracked change's author:
|
|
237
|
+
DOCX Studio stamps those with the signed-in user agora's access check
|
|
238
|
+
names, so a script cannot choose who a redline's changes are by.
|
|
239
|
+
Separate from `Document.core_properties.last_modified_by`, the
|
|
240
|
+
`docProps/core.xml` metadata the SDK reads and writes as python-docx
|
|
219
241
|
does.
|
|
220
242
|
|
|
221
243
|
- **`Document.revisions: Revisions`** — collection of tracked changes
|
|
@@ -271,9 +293,10 @@ Issue numbers reference `python-openxml/python-docx`.
|
|
|
271
293
|
`"FILENAME"`, `"NUMWORDS"`, `"REF"`, `"SEQ"`, `"TOC"`, and the
|
|
272
294
|
generic `"FIELD"` escape hatch. python-docx has no field API
|
|
273
295
|
whatsoever; issue #498 (page numbers) and #31 (23 comments) demand
|
|
274
|
-
this. `Fields` is sequence-like; `Fields.refresh_all(scope?)`
|
|
275
|
-
|
|
276
|
-
|
|
296
|
+
this. `Fields` is sequence-like; `Fields.refresh_all(scope?)` updates
|
|
297
|
+
every unlocked REF and SEQ result as Word's F9 does (PAGE, PAGEREF
|
|
298
|
+
and TOC results depend on pages and stay). Routes through
|
|
299
|
+
`CreateField` / `FieldsRefresh` / `FieldsList`.
|
|
277
300
|
|
|
278
301
|
- **`Document.bookmarks: Bookmarks`** + **`Paragraph.add_bookmark
|
|
279
302
|
(name)`** + **`Document.add_bookmark(name, *, paragraph=None,
|
|
@@ -291,7 +314,9 @@ Issue numbers reference `python-openxml/python-docx`.
|
|
|
291
314
|
`FootnotesGet` / `FootnotesPatch` / `FootnotesDelete`. The
|
|
292
315
|
parallel **`Document.endnotes: Endnotes`** + **`Run.add_endnote
|
|
293
316
|
(text, *, custom_mark=None)`** mirrors the same machinery for
|
|
294
|
-
end-of-section notes.
|
|
317
|
+
end-of-section notes. Both list the notes a reference reaches, as
|
|
318
|
+
Word and the export do: a note whose reference was deleted in the
|
|
319
|
+
editor stays in the package for undo, and is not listed.
|
|
295
320
|
|
|
296
321
|
- **`Document.add_toc(*, levels=(1, 3), style_id=None,
|
|
297
322
|
include_hyperlinks=True, title=None, tab_leader="dot", at=None)
|
|
@@ -418,6 +443,8 @@ Issue numbers reference `python-openxml/python-docx`.
|
|
|
418
443
|
inspection. Use this when an agent needs to sample or inspect runs
|
|
419
444
|
without issuing one ``getNodeById`` query per paragraph through
|
|
420
445
|
``doc.paragraphs`` + ``paragraph.runs``. Routes through `IterRuns`.
|
|
446
|
+
These three see a footnote or endnote only while a reference reaches
|
|
447
|
+
it, as `Document.footnotes` / `Document.endnotes` list them.
|
|
421
448
|
|
|
422
449
|
- **`Paragraph.list_id` / `Paragraph.list_level` /
|
|
423
450
|
`Paragraph.list_format` / `Paragraph.list_number`** — list-item
|
|
@@ -600,12 +627,10 @@ Issue numbers reference `python-openxml/python-docx`.
|
|
|
600
627
|
|
|
601
628
|
- **`Document.export_docx(path=None, *, include_revisions=False)
|
|
602
629
|
-> bytes`** (0.11.4+) — export the document's current state as
|
|
603
|
-
``.docx`` bytes, optionally writing to ``path
|
|
604
|
-
|
|
605
|
-
``
|
|
606
|
-
|
|
607
|
-
diff against ``python-docx`` output that doesn't require a
|
|
608
|
-
human-driven Olympus UI action. Routes through ``ExportDocx`` →
|
|
630
|
+
``.docx`` bytes, optionally writing to ``path``; ``Document.save(path_or_stream)``
|
|
631
|
+
writes its file through it. Parity test harnesses and CI workflows
|
|
632
|
+
use it to diff against ``python-docx`` output without a human-driven
|
|
633
|
+
Olympus UI action. Routes through ``ExportDocx`` →
|
|
609
634
|
apps/api's backend export path. apps/api
|
|
610
635
|
writes the session state to a temp ``.docx``, reads bytes back,
|
|
611
636
|
base64-encodes for the wire, and unlinks the temp in a
|
|
@@ -614,6 +639,26 @@ Issue numbers reference `python-openxml/python-docx`.
|
|
|
614
639
|
the current backend export path has no ``includeRevisions`` support,
|
|
615
640
|
so honoring the flag isn't possible yet.
|
|
616
641
|
|
|
642
|
+
``export_docx(path=None, *, include_revisions=False, clean=False,
|
|
643
|
+
clear_title=False)`` (0.29.0+): called with ``clean=True`` it returns a
|
|
644
|
+
clean copy for sending outside the firm instead, from
|
|
645
|
+
``GET /docs/:id/export.docx?clean=1`` (VIEW gate): docx-studio imports
|
|
646
|
+
the export into an in-memory room, accepts every tracked change and
|
|
647
|
+
deletes every comment through the command runner, exports that room and
|
|
648
|
+
leaves out the author, last-modified-by, revision, company, manager,
|
|
649
|
+
template, editing-time and hyperlink-base properties, rsids, document
|
|
650
|
+
variables, custom properties, Custom XML (a content control bound to it
|
|
651
|
+
keeps its content, not its binding), the attached template and the
|
|
652
|
+
thumbnail; ``clear_title=True`` drops the title too. The live document is
|
|
653
|
+
not changed. The SDK refuses (``DocxUnsupportedCommandError``) an answer
|
|
654
|
+
without ``X-Docx-Clean-Copy: 1``, the plain export an older docx-studio
|
|
655
|
+
sends, and raises ``DocxError`` with docx-studio's reason when a header,
|
|
656
|
+
footer or note still holds a tracked change (the engine accepts the
|
|
657
|
+
body's only; ``DOCX_STUDIO_LIMITATIONS.md`` § 31). The in-process
|
|
658
|
+
post-export rewrites are applied to a clean copy as to a plain export,
|
|
659
|
+
and the copy is refused (``DocxError``) if they put revision, comment or
|
|
660
|
+
rsid markup back into ``word/document.xml``.
|
|
661
|
+
|
|
617
662
|
Template seeding: ``Document.create()`` (no ``docx=``) already
|
|
618
663
|
produces an exportable asset because ``POST /docs/empty`` seeds
|
|
619
664
|
from the default Athena template, so ``export_docx`` works
|
|
@@ -904,6 +949,18 @@ architecture or a DOCX Studio constraint:
|
|
|
904
949
|
but DOCX Studio's ProseMirror table node can't represent one; the SDK
|
|
905
950
|
raises eagerly rather than failing opaquely on the wire.
|
|
906
951
|
|
|
952
|
+
- **`Document.add_table` writes Athena's brand table.** Owner decision
|
|
953
|
+
(2026-10-12): keep the brand defaults. The table's layout is fixed, so
|
|
954
|
+
`Table.autofit` reads `False` (python-docx leaves autofit on); its columns
|
|
955
|
+
share 9,360 twips, where python-docx shares the section's text width; and
|
|
956
|
+
in the fixed layout a column width the script sets sizes the column's
|
|
957
|
+
cells too, where python-docx sets the `w:gridCol` alone. It also draws
|
|
958
|
+
borders and a zebra fill, which no python-docx read sees. Recorded as
|
|
959
|
+
`live_deviations` for the script cases' `tables.autofit`, `tables.widths`
|
|
960
|
+
and `tables.cell_format` in `tests/parity/intentional_deviations.json`;
|
|
961
|
+
an imported table's gaps stay gaps, and so do the two cases whose
|
|
962
|
+
`cell_format` also differs for another cause (`except`).
|
|
963
|
+
|
|
907
964
|
- **`_Cell.merge` raises `InvalidSpanError` for every span that would cut
|
|
908
965
|
through a merged cell.** It merges what python-docx merges and refuses
|
|
909
966
|
what python-docx refuses (an L- or T-shaped pair of corners), but where
|
|
@@ -942,6 +999,18 @@ architecture or a DOCX Studio constraint:
|
|
|
942
999
|
Keryx attributes the edit to the signed-in user (and agent), and
|
|
943
1000
|
nothing reads a comment's author as identity.
|
|
944
1001
|
|
|
1002
|
+
- **A comment's paragraphs are in Word's comment style, which the export
|
|
1003
|
+
defines.** python-docx 1.2.0's `add_comment` and `Comment.add_paragraph`
|
|
1004
|
+
write `w:pStyle CommentText` (Word's built-in "annotation text", Comment
|
|
1005
|
+
Text in Word's UI) without defining the style in `styles.xml`, so
|
|
1006
|
+
python-docx reads those paragraphs as Normal. docx-studio writes the same
|
|
1007
|
+
`w:pStyle` and defines the style as Word does: `Comment.paragraphs[i].style`
|
|
1008
|
+
reads `Comment Text` (a built-in the document lacks reads back as that
|
|
1009
|
+
built-in, as above), and the export adds Word's definition of the style and
|
|
1010
|
+
of its linked `Comment Text Char` to `styles.xml`, so python-docx reads the
|
|
1011
|
+
exported paragraph as `annotation text`. Owner decision, 2026-10-12.
|
|
1012
|
+
Recorded in `tests/parity/intentional_deviations.json` (`live_deviations`).
|
|
1013
|
+
|
|
945
1014
|
- **`ParagraphFormat.line_spacing_rule = None` warns-and-preserves on a
|
|
946
1015
|
clear** rather than raising (upstream accepts `None` to clear, but DOCX
|
|
947
1016
|
Studio's merging mutation ops have no single-attribute clear). It emits a
|
|
@@ -959,7 +1028,24 @@ architecture or a DOCX Studio constraint:
|
|
|
959
1028
|
given. `Run.add_tab()` and `Run.add_break()` take the run's formatting, as
|
|
960
1029
|
upstream appends them to the run's `w:r`; `Run.add_break(WD_BREAK.COLUMN)`
|
|
961
1030
|
sends a column break (`breakType: "column"`), which docx-studio writes after
|
|
962
|
-
the paragraph's content. `DOCX_STUDIO_LIMITATIONS.md` §26.
|
|
1031
|
+
the paragraph's content. `DOCX_STUDIO_LIMITATIONS.md` §26. `Run.text` and
|
|
1032
|
+
`Paragraph.text` leave a ruby's base text out (`w:ruby`, a phonetic guide
|
|
1033
|
+
over its base text), as upstream reads only a run's own text elements, while
|
|
1034
|
+
the base text keeps its room in the text-address space (`ruby` spans on the
|
|
1035
|
+
read's run; `tests/test_ruby_reads_python_docx_parity.py`). That holds while
|
|
1036
|
+
the paragraph's runs are as its records hold them, in the body, a table cell,
|
|
1037
|
+
a header, a footer or a comment; a paragraph with a link or a tracked change
|
|
1038
|
+
reads its runs from their marks, the base text included. A write to the run
|
|
1039
|
+
(its text, an append, its formatting) makes the base text ordinary text,
|
|
1040
|
+
which then reads as text: docx-studio keeps no guide through an edit, where
|
|
1041
|
+
upstream keeps the `w:ruby` through all but a text write.
|
|
1042
|
+
|
|
1043
|
+
- **`CoreProperties.modified` is the time of the last save.** The asset is a
|
|
1044
|
+
live Word document, and docx-studio stamps `dcterms:modified` when it saves
|
|
1045
|
+
a change to the package: a section, style or property write, and the export
|
|
1046
|
+
of a changed document. Word stamps it on every save; upstream leaves the
|
|
1047
|
+
value it read until the script sets it. Owner decision, 2026-10-12. Recorded
|
|
1048
|
+
in `tests/parity/intentional_deviations.json` (`live_deviations`).
|
|
963
1049
|
|
|
964
1050
|
- **Tracked changes read as Word's final view.** `Paragraph.text`, `runs`,
|
|
965
1051
|
`iter_inner_content()`, `Hyperlink.text` / `runs` and `_Cell.text` include
|
|
@@ -973,23 +1059,45 @@ architecture or a DOCX Studio constraint:
|
|
|
973
1059
|
Markup) show insertions and hide deletions, as these reads do.
|
|
974
1060
|
`doc.revisions` lists every change (`tests/test_run_text_python_docx.py`).
|
|
975
1061
|
|
|
1062
|
+
- **A citation link Athena added reads as its text, as the export writes it**
|
|
1063
|
+
(owner decision #40122). Athena's citation flow stamps the links it writes
|
|
1064
|
+
with the `Athena citation` tooltip. These links only navigate inside Athena,
|
|
1065
|
+
so the export leaves them out and keeps their text: no `w:hyperlink`, and no
|
|
1066
|
+
citation highlight on that text.
|
|
1067
|
+
- **The reads match the export.** The run reads with no `hyperlink` and no
|
|
1068
|
+
highlight in `Paragraph.runs`, `Paragraph.hyperlinks`, a cell's paragraphs
|
|
1069
|
+
and docx-studio's markdown read.
|
|
1070
|
+
- **Where upstream differs.** Upstream reads the room's `w:hyperlink` as a
|
|
1071
|
+
link.
|
|
1072
|
+
- **Which links are Athena's** is decided by the room's provenance, not by
|
|
1073
|
+
the tooltip alone. A link with the tooltip that came in the uploaded file
|
|
1074
|
+
(the room's source package holds it) is a link everywhere, as Word keeps
|
|
1075
|
+
it.
|
|
1076
|
+
- **Tests:** `applier.test.ts` › `applier — citations`.
|
|
1077
|
+
|
|
976
1078
|
- **A field's runs read as python-docx reads them; some writes to them
|
|
977
1079
|
differ.** `Paragraph.text` includes a complex field's displayed result and
|
|
978
1080
|
`Paragraph.runs` holds its begin, field code, separate, result and end runs
|
|
979
1081
|
(each with its own formatting), as python-docx reads the `w:r` children of
|
|
980
|
-
a `w:p`; a simple field (`w:fldSimple`) reads as nothing
|
|
981
|
-
|
|
1082
|
+
a `w:p`; a simple field (`w:fldSimple`) reads as nothing (one in a tracked
|
|
1083
|
+
change, which the export writes as a complex field, reads that field's
|
|
1084
|
+
runs), a result run of a note reference reads `""`, and a field in a link
|
|
1085
|
+
is the link's, as in python-docx. Each paragraph of a field that spans
|
|
982
1086
|
paragraphs (a table of contents) holds the field character runs it has
|
|
983
1087
|
(`tests/test_field_reads_python_docx_parity.py`, against stock python-docx
|
|
984
1088
|
on the same `.docx`; `DOCX_STUDIO_LIMITATIONS.md` § 27). A field takes no
|
|
985
1089
|
room in the text-address space, so its runs stand at one offset. Writes:
|
|
986
1090
|
- setting a result run's `.text` changes the field's displayed result,
|
|
987
|
-
as in python-docx (`Replace` with a `field` target)
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1091
|
+
as in python-docx (`Replace` with a `field` target): the export keeps
|
|
1092
|
+
the result's runs where the new text allows (text still there stays in
|
|
1093
|
+
its run, new text goes in the run whose text it replaced; another result
|
|
1094
|
+
run's tab or Word character stays as the source wrote it, and a note
|
|
1095
|
+
reference stays where it is, where python-docx's write of its run would
|
|
1096
|
+
delete it), but writes no result run left with no text, where
|
|
1097
|
+
python-docx keeps an empty `w:r`, and a result run read before the
|
|
1098
|
+
field's result changed (through another read's run, or by a
|
|
1099
|
+
collaborator) raises `FieldRunWriteError` rather than write over that
|
|
1100
|
+
change, where python-docx's run is live;
|
|
993
1101
|
- formatting a result run formats the field's whole result (`FormatApply`
|
|
994
1102
|
with a `field` target), from its source formatting, where python-docx
|
|
995
1103
|
formats that run alone;
|
|
@@ -1006,10 +1114,22 @@ architecture or a DOCX Studio constraint:
|
|
|
1006
1114
|
`add_footnote`, `add_endnote`, `add_math` and `add_cross_reference` on
|
|
1007
1115
|
any of a field's runs raise `FieldRunWriteError`; python-docx appends to
|
|
1008
1116
|
that `w:r`.
|
|
1009
|
-
A field that `Run.add_field` adds is a
|
|
1010
|
-
|
|
1011
|
-
Against a docx-studio that predates `fieldRun` reads, a
|
|
1012
|
-
runs and no text, as before (`tests/test_field_runs.py`).
|
|
1117
|
+
A field that `Run.add_field` adds is a complex field, as Word's Insert
|
|
1118
|
+
Field writes one, so its paragraph reads its runs, as python-docx reads the
|
|
1119
|
+
exported file. Against a docx-studio that predates `fieldRun` reads, a
|
|
1120
|
+
field reads as no runs and no text, as before (`tests/test_field_runs.py`).
|
|
1121
|
+
|
|
1122
|
+
- **`Font.name = "…"` also clears the theme fonts of the two slots it
|
|
1123
|
+
sets.** python-docx writes the name to `w:rFonts`' `w:ascii` and `w:hAnsi`
|
|
1124
|
+
and leaves a `w:asciiTheme` / `w:hAnsiTheme` beside them; Word reads a
|
|
1125
|
+
theme font over the name in its slot (ISO/IEC 29500-1 §17.3.2.26), so
|
|
1126
|
+
after python-docx's write Word still shows the theme font. The SDK sets the
|
|
1127
|
+
same two slots and docx-studio drops their theme fonts with them, so Word
|
|
1128
|
+
shows the font that was set. The run's other slots (`w:eastAsia`, `w:cs`
|
|
1129
|
+
and their theme fonts) and its `w:hint` stay as they were, as in
|
|
1130
|
+
python-docx. The export writes exactly the slots the run's fonts hold
|
|
1131
|
+
(#42248). Recorded in `tests/parity/intentional_deviations.json`
|
|
1132
|
+
(`_comment_behavioral_deviations`).
|
|
1013
1133
|
|
|
1014
1134
|
### If you need a deviation
|
|
1015
1135
|
|
|
@@ -1052,6 +1172,15 @@ This is a **thin HTTP client** that mimics the sync python-docx API.
|
|
|
1052
1172
|
the context-manager exit drain explicitly; `docx.flush_all()` is
|
|
1053
1173
|
the executor-postamble hook that flushes every live buffer in the
|
|
1054
1174
|
process.
|
|
1175
|
+
- A flush of more than 500 commands (`MAX_BATCH_COMMANDS`,
|
|
1176
|
+
docx-studio's request cap) goes out as consecutive requests, and
|
|
1177
|
+
docx-studio applies each all or nothing: when one fails, the requests
|
|
1178
|
+
before it are saved and it and every later one are not. The
|
|
1179
|
+
`DocxError` (0.29.0+) says how many of the flush's commands were
|
|
1180
|
+
applied and names the failed one by its flush index; it carries
|
|
1181
|
+
`unapplied_commands` and `failed_command`, and `partial_applied`
|
|
1182
|
+
holds the saved requests' results. A retry re-applies the unapplied
|
|
1183
|
+
edits, never the whole flush.
|
|
1055
1184
|
- Execution scope: executor sandboxes may keep Python globals
|
|
1056
1185
|
between tool calls so imports, constants, and helper functions can be
|
|
1057
1186
|
reused. Live SDK objects are different. The executor calls
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: athena-python-docx
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.29.0
|
|
4
4
|
Summary: Drop-in replacement for python-docx that connects to Athena's DOCX Studio collaborative document stack
|
|
5
5
|
Project-URL: Homepage, https://athenaintelligence.ai
|
|
6
6
|
Author-email: Athena Intelligence <engineering@athenaintelligence.ai>
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""Sync→async bridge. Runs a single event loop in a background thread
|
|
2
|
+
and dispatches coroutines from sync callers.
|
|
3
|
+
|
|
4
|
+
This is the only place in the SDK that uses threading.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import concurrent.futures
|
|
11
|
+
import contextvars
|
|
12
|
+
import threading
|
|
13
|
+
from typing import Any, Coroutine, TypeVar
|
|
14
|
+
|
|
15
|
+
from docx._timeouts import DEFAULT_OP_TIMEOUT_SECONDS
|
|
16
|
+
from docx.errors import OperationTimeoutError
|
|
17
|
+
|
|
18
|
+
T = TypeVar("T")
|
|
19
|
+
|
|
20
|
+
_loop: asyncio.AbstractEventLoop | None = None
|
|
21
|
+
_thread: threading.Thread | None = None
|
|
22
|
+
_lock = threading.Lock()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class _Progress:
|
|
26
|
+
"""What the thread waiting on a call (:func:`run_sync`) and the call share:
|
|
27
|
+
the steps the call has finished, and whether the waiter gave up."""
|
|
28
|
+
|
|
29
|
+
__slots__ = ("abandoned", "steps")
|
|
30
|
+
|
|
31
|
+
def __init__(self) -> None:
|
|
32
|
+
self.steps = 0
|
|
33
|
+
self.abandoned = False
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
_progress: contextvars.ContextVar[_Progress | None] = contextvars.ContextVar(
|
|
37
|
+
"docx_sdk_call_progress", default=None
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def note_progress() -> None:
|
|
42
|
+
"""A step of the running call finished: one batch of a flush that goes
|
|
43
|
+
out in several (``HttpClient.execute_batch``). :func:`run_sync` gives the
|
|
44
|
+
call its ``timeout`` again, so each batch has the budget its request has."""
|
|
45
|
+
progress = _progress.get()
|
|
46
|
+
if progress is not None:
|
|
47
|
+
progress.steps += 1
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def call_abandoned() -> bool:
|
|
51
|
+
"""True once the caller of the running call timed out
|
|
52
|
+
(:class:`OperationTimeoutError`): a call of several steps starts no more."""
|
|
53
|
+
progress = _progress.get()
|
|
54
|
+
return progress is not None and progress.abandoned
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _ensure_loop() -> asyncio.AbstractEventLoop:
|
|
58
|
+
"""Return the persistent background event loop. Starts it lazily."""
|
|
59
|
+
global _loop, _thread
|
|
60
|
+
|
|
61
|
+
with _lock:
|
|
62
|
+
if _loop is not None and _loop.is_running():
|
|
63
|
+
return _loop
|
|
64
|
+
|
|
65
|
+
loop = asyncio.new_event_loop()
|
|
66
|
+
loop_ready = threading.Event()
|
|
67
|
+
|
|
68
|
+
def _run() -> None:
|
|
69
|
+
asyncio.set_event_loop(loop)
|
|
70
|
+
loop_ready.set()
|
|
71
|
+
loop.run_forever()
|
|
72
|
+
|
|
73
|
+
thread = threading.Thread(
|
|
74
|
+
target=_run,
|
|
75
|
+
name="docx-sdk-loop",
|
|
76
|
+
daemon=True,
|
|
77
|
+
)
|
|
78
|
+
thread.start()
|
|
79
|
+
loop_ready.wait(timeout=5)
|
|
80
|
+
_loop = loop
|
|
81
|
+
_thread = thread
|
|
82
|
+
return loop
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def run_sync(
|
|
86
|
+
coro: Coroutine[Any, Any, T],
|
|
87
|
+
*,
|
|
88
|
+
timeout: float | None = DEFAULT_OP_TIMEOUT_SECONDS,
|
|
89
|
+
) -> T:
|
|
90
|
+
"""Run a coroutine on the background loop and block until it returns.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
coro: The coroutine to execute.
|
|
94
|
+
timeout: Maximum seconds to wait, counted again whenever the call
|
|
95
|
+
finishes a step (:func:`note_progress`). ``None`` blocks
|
|
96
|
+
indefinitely (use sparingly — prefer the default so stalls
|
|
97
|
+
surface early).
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
OperationTimeoutError: if the coroutine neither completes nor
|
|
101
|
+
finishes a step within `timeout` seconds. The call then starts
|
|
102
|
+
no further step (:func:`call_abandoned`).
|
|
103
|
+
The coroutine's exception, re-raised in the calling thread.
|
|
104
|
+
"""
|
|
105
|
+
loop = _ensure_loop()
|
|
106
|
+
progress = _Progress()
|
|
107
|
+
# The call runs in a copy of this thread's context (call_soon_threadsafe
|
|
108
|
+
# and the task each copy the current one), so it finds `progress`.
|
|
109
|
+
token = _progress.set(progress)
|
|
110
|
+
try:
|
|
111
|
+
future = asyncio.run_coroutine_threadsafe(coro, loop)
|
|
112
|
+
finally:
|
|
113
|
+
_progress.reset(token)
|
|
114
|
+
seen = 0
|
|
115
|
+
while True:
|
|
116
|
+
try:
|
|
117
|
+
return future.result(timeout=timeout)
|
|
118
|
+
except concurrent.futures.TimeoutError as exc:
|
|
119
|
+
if progress.steps > seen:
|
|
120
|
+
seen = progress.steps
|
|
121
|
+
continue
|
|
122
|
+
progress.abandoned = True
|
|
123
|
+
future.cancel()
|
|
124
|
+
raise OperationTimeoutError(timeout_seconds=timeout) from exc
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def shutdown() -> None:
|
|
128
|
+
"""Stop the background event loop. For test teardown."""
|
|
129
|
+
global _loop, _thread
|
|
130
|
+
with _lock:
|
|
131
|
+
if _loop is None:
|
|
132
|
+
return
|
|
133
|
+
_loop.call_soon_threadsafe(_loop.stop)
|
|
134
|
+
if _thread is not None:
|
|
135
|
+
_thread.join(timeout=5)
|
|
136
|
+
_loop = None
|
|
137
|
+
_thread = None
|