athena-python-docx 0.21.7__tar.gz → 0.22.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.21.7/CLAUDE.md → athena_python_docx-0.22.0/AGENTS.md +37 -3
- athena_python_docx-0.22.0/CLAUDE.md +5 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/DOCX_EXEC_LAB.md +1 -1
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/PKG-INFO +3 -3
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/README.md +1 -1
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/__init__.py +1 -1
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/_buffer.py +2 -2
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/_http.py +1 -1
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/_http_doc.py +2 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/_postproc.py +21 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/_ptc.py +74 -8
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/_timeouts.py +3 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/client.py +5 -1
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/commands.py +23 -2
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/document.py +39 -70
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/section.py +5 -8
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/table.py +78 -1
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/text/paragraph.py +14 -9
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/text/run.py +3 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/pyproject.toml +1 -1
- athena_python_docx-0.22.0/scripts/release.sh +14 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/README.md +1 -1
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/__init__.py +1 -1
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/ab_probe_cases.py +1 -1
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/binary_round_trip.py +1 -1
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/cases.py +1 -1
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/fake_session.py +1 -1
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/local_runner.py +1 -1
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/parity/reports/GAP_ANALYSIS.md +30 -3
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/parity/reports/gap_report.json +1785 -3
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/parity/snapshots/athena_latest.json +854 -1143
- athena_python_docx-0.22.0/tests/test_athena_extension_creates_session_open.py +64 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_athena_extensions_registry.py +2 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_citations.py +120 -0
- athena_python_docx-0.22.0/tests/test_document_clear.py +277 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_execution_scope.py +1 -1
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_ptc.py +105 -1
- athena_python_docx-0.22.0/tests/test_run_mark_inheritance.py +88 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_wire_contract.py +1 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/uv.lock +1 -1
- athena_python_docx-0.21.7/scripts/publish.sh +0 -37
- athena_python_docx-0.21.7/scripts/release.sh +0 -84
- athena_python_docx-0.21.7/tests/test_document_clear.py +0 -132
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/.gitignore +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/_athena_extension.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/_batching.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/_citations.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/_execution.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/_image_utils.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/_references.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/_table_styles.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/api.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/bookmarks.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/charts.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/comments.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/enum/__init__.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/enum/section.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/enum/shape.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/enum/style.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/enum/table.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/enum/text.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/errors.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/exceptions.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/fields.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/footnotes.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/math.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/opc/__init__.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/opc/coreprops.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/oxml/__init__.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/revisions.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/sdt.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/session.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/settings.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/shape.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/shared.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/styles/__init__.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/styles/style.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/styles/styles.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/text/__init__.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/text/font.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/text/hyperlink.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/text/pagebreak.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/text/parfmt.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/text/tabstops.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/toc.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/docx/typing.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/scripts/docx_exec_lab.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/scripts/docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/scripts/dump_wire_fixtures.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/scripts/round_trip_smoke.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/scripts/smoke_test_block_not_found.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/scripts/validate_find_replace_asset.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/__init__.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/conftest.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/METHODOLOGY.md +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/ab_probe_runner.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/auto_gen_cases.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/complex_cases.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/coverage_report.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/extract.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/extreme_cases.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/firm_templates/README.md +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/firm_templates/__init__.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/firm_templates/_runner.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/firm_templates/extractor.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/mega_cases.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshot.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/ours_spec.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/parity_crawl.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/parity_diff.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/real_world_cases.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/round_trip_tests.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/stock_spec.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/parity/README.md +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/parity/__init__.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/parity/baseline_gaps.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/parity/compare.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/parity/intentional_deviations.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/parity/introspect.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/parity/run_parity.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/parity/test_parity_gap.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_add_section_extract_items.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_athena_extensions_contract.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_batching_perf.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_block_not_found_error.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_buffer.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_cell_add_table_not_supported.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_cell_fastpath_runs.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_cell_inner_format_stash.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_cell_inner_run_format_stash.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_cell_inner_run_guard.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_cell_text_plain_fastpath.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_cell_text_replace_semantics.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_collapsed_range_format.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_command_dataclasses.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_commands.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_comments.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_document_asset_id_property.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_document_create.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_document_create_from_template.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_document_factory_validation.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_docx_exec_lab.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_find_replace_session_open.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_http_transport.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_hyperlink_coalescing.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_image_url_data_uri.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_insert_deferred.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_iter_inner_content.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_list_styles.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_merged_cell_secondary_slot.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_merged_cells.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_oxml_shim.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_paragraph_text_len_cache.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_parity_misc.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_parity_round2.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_phase_a_behavior.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_phase_b_headers_footers.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_phase_c_tables.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_positional_cell_id.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_postproc_ref_restore.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_pr19766_review_fixes.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_python_docx_api_parity.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_revisions.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_run_add_picture_anchor.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_add_picture.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_add_run.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_comments_get.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_document_audit.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_document_element.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_enum_section.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_font_audit.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_header_footer.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_hyperlink.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_inline_shape.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_misc.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_paragraph_style.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_parfmt.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_row_col_cell.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_run_add_break.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_run_style.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_run_style_strict.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_run_text.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_run_underline.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_section_audit.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_section_dimensions.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_section_onoff.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_settings.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_shared_audit.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_style.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_styles.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_table_audit.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_table_cell.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_table_dimensions.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_silent_stub_table_layout.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_smoke_integration.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_style_acceptance.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_style_font.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_style_setters_contract.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_table_set_cell_perf.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_table_style_id_resolution.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_temporarily_unavailable.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_validate_find_replace_asset_script.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_xml_attr_guard.py +0 -0
- {athena_python_docx-0.21.7 → athena_python_docx-0.22.0}/tests/test_zod_wire_contract.py +0 -0
|
@@ -367,8 +367,8 @@ Issue numbers reference `python-openxml/python-docx`.
|
|
|
367
367
|
list metadata. Routes through `NumberingGet`.
|
|
368
368
|
|
|
369
369
|
- **`Document.to_pdf(*, quality=None, destination=None,
|
|
370
|
-
include_revisions=False) -> bytes`** — render to PDF
|
|
371
|
-
|
|
370
|
+
include_revisions=False) -> bytes`** — render to PDF. python-docx
|
|
371
|
+
issue #113 was closed as
|
|
372
372
|
wontfix upstream but the request keeps recurring. Routes through
|
|
373
373
|
`ExportPDF` (DOCX Studio handles the actual conversion).
|
|
374
374
|
|
|
@@ -447,6 +447,40 @@ Issue numbers reference `python-openxml/python-docx`.
|
|
|
447
447
|
only when you already have a DOCX editor anchor id. For normal writing,
|
|
448
448
|
cite the ``Paragraph`` proxy you just created or found.
|
|
449
449
|
|
|
450
|
+
- **Table-cell citations** (0.22.0+): **`_Cell.add_citation(source, *,
|
|
451
|
+
anchor=None, citation_string=None, excerpt=None, display_value=None)
|
|
452
|
+
-> str`** + **`_Cell.clear_citations()`**, and ``Paragraph.add_citation``
|
|
453
|
+
/ ``Paragraph.clear_citations`` on cell paragraphs (before 0.22.0 they
|
|
454
|
+
raised ``ValueError`` "top-level DOCX paragraphs only"). The SDK models a
|
|
455
|
+
cell as one paragraph addressed by the synthetic cell id
|
|
456
|
+
(``tc:table:row:col``; ``cell.paragraphs[0]`` stands for the whole cell),
|
|
457
|
+
so a cell citation sends that id. The docx-studio applier resolves it to
|
|
458
|
+
the cell's paragraph ids: one paragraph with text → ``pm_node``, several
|
|
459
|
+
→ ``pm_node_range`` from the first to the last; a cell paragraph with no
|
|
460
|
+
paraId (or a repeated one) gets a new one. A horizontally merged cell
|
|
461
|
+
resolves to its master cell from any grid position it covers. A
|
|
462
|
+
paragraph that ``cell.add_paragraph(text)`` returned already carries its
|
|
463
|
+
own paraId and is cited alone. To cite one paragraph of a cell (or a
|
|
464
|
+
paragraph in a nested table), pass the ``data-id`` a Word read shows for
|
|
465
|
+
it: ``doc.add_citation(..., destination_anchor=PmNodeAnchor(node_id=...))``.
|
|
466
|
+
``_Cell.clear_citations()`` removes every citation inside the cell.
|
|
467
|
+
Write the cell text first, then cite it:
|
|
468
|
+
|
|
469
|
+
```python
|
|
470
|
+
table = doc.add_table(rows=2, cols=2)
|
|
471
|
+
table.cell(1, 0).text = "Revenue"
|
|
472
|
+
table.cell(1, 1).text = "$12.4M"
|
|
473
|
+
table.cell(1, 1).add_citation(
|
|
474
|
+
"/dashboard/spaces/?asset_ids=asset_sheet&type=spreadsheet"
|
|
475
|
+
"&anchor=sheet_range&sheet_id=0&range=C4",
|
|
476
|
+
excerpt="$12.4M",
|
|
477
|
+
)
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
Needs a docx-studio API that resolves cell ids (the same release train);
|
|
481
|
+
an older API stores the record under the cell id, where nothing renders
|
|
482
|
+
it and the next edit retires it.
|
|
483
|
+
|
|
450
484
|
- **`Document.export_docx(path=None, *, include_revisions=False)
|
|
451
485
|
-> bytes`** (0.11.4+) — export the document's current state as
|
|
452
486
|
``.docx`` bytes, optionally writing to ``path``. Closes the gap
|
|
@@ -769,7 +803,7 @@ This is a **thin HTTP client** that mimics the sync python-docx API.
|
|
|
769
803
|
drain pending ops before running; the 100 ms idle timer fires on
|
|
770
804
|
inactivity so collab updates stream live; `Document.save()` and
|
|
771
805
|
the context-manager exit drain explicitly; `docx.flush_all()` is
|
|
772
|
-
the
|
|
806
|
+
the executor-postamble hook that flushes every live buffer in the
|
|
773
807
|
process.
|
|
774
808
|
- Execution scope: executor sandboxes may keep Python globals
|
|
775
809
|
between tool calls so imports, constants, and helper functions can be
|
|
@@ -6,7 +6,7 @@ shortens the debug loop by running local SDK code directly against a
|
|
|
6
6
|
chosen DOCX Studio API, capturing stdout/stderr/errors/timings, and
|
|
7
7
|
optionally exporting the result to `.docx` for assertions.
|
|
8
8
|
|
|
9
|
-
Use this before waiting on a full Agent ->
|
|
9
|
+
Use this before waiting on a full Agent -> executor sandbox -> preview cycle when
|
|
10
10
|
debugging SDK behavior such as large-document replacement, table fills,
|
|
11
11
|
unsupported OOXML internals, or export fidelity.
|
|
12
12
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: athena-python-docx
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.22.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>
|
|
@@ -76,7 +76,7 @@ an Olympus Word document embed preview pane. See `DOCX_EXEC_LAB.md`.
|
|
|
76
76
|
|
|
77
77
|
## Environment variables
|
|
78
78
|
|
|
79
|
-
Required when connecting to DOCX Studio (set by Athena backend
|
|
79
|
+
Required when connecting to DOCX Studio (set by the Athena backend in the `execute_word_document_code` executor sandbox):
|
|
80
80
|
|
|
81
81
|
- `ATHENA_DOCX_BASE_URL` — DOCX Studio API base URL
|
|
82
82
|
- `ATHENA_DOCX_API_KEY` — Athena API key or service-issued DOCX Studio token
|
|
@@ -51,7 +51,7 @@ an Olympus Word document embed preview pane. See `DOCX_EXEC_LAB.md`.
|
|
|
51
51
|
|
|
52
52
|
## Environment variables
|
|
53
53
|
|
|
54
|
-
Required when connecting to DOCX Studio (set by Athena backend
|
|
54
|
+
Required when connecting to DOCX Studio (set by the Athena backend in the `execute_word_document_code` executor sandbox):
|
|
55
55
|
|
|
56
56
|
- `ATHENA_DOCX_BASE_URL` — DOCX Studio API base URL
|
|
57
57
|
- `ATHENA_DOCX_API_KEY` — Athena API key or service-issued DOCX Studio token
|
|
@@ -20,7 +20,7 @@ guard the pending list with an RLock and treat the timer flush as a
|
|
|
20
20
|
best-effort coalesce — if the loop thread races us we just flush twice
|
|
21
21
|
(or zero times; the next loop-thread call drains it).
|
|
22
22
|
|
|
23
|
-
`flush_all` is a process-wide hook used by
|
|
23
|
+
`flush_all` is a process-wide hook used by Athena's sandbox executor to
|
|
24
24
|
make sure pending writes hit Keryx before the sandbox is suspended. It walks
|
|
25
25
|
a weak-ref registry so dead Workbook instances don't keep their buffers
|
|
26
26
|
alive.
|
|
@@ -199,7 +199,7 @@ def _unregister(buffer: "CommandBuffer") -> None:
|
|
|
199
199
|
def flush_all(*, strict: bool = False) -> None:
|
|
200
200
|
"""Flush every live CommandBuffer in this process.
|
|
201
201
|
|
|
202
|
-
Used by
|
|
202
|
+
Used by Athena's sandbox executor after user code returns, so
|
|
203
203
|
buffered mutations make it to Keryx before the sandbox is suspended.
|
|
204
204
|
Safe to call when no Buffers exist (no-op).
|
|
205
205
|
|
|
@@ -5,7 +5,7 @@ bootstrap and upload calls on urllib so ``Document.create()`` can provision
|
|
|
5
5
|
Word document assets without pulling in extra runtime dependencies.
|
|
6
6
|
|
|
7
7
|
We use ``urllib`` from stdlib to avoid pulling ``httpx`` / ``requests``
|
|
8
|
-
into the runtime — this code runs in
|
|
8
|
+
into the runtime — this code runs in sandboxes where every MB
|
|
9
9
|
matters, and the existing SDK already keeps its dep tree tight.
|
|
10
10
|
"""
|
|
11
11
|
|
|
@@ -46,6 +46,7 @@ from docx.commands import (
|
|
|
46
46
|
BookmarksList,
|
|
47
47
|
ChartsGet,
|
|
48
48
|
ChartsList,
|
|
49
|
+
ClearBody,
|
|
49
50
|
ClearParagraphAlignment,
|
|
50
51
|
ClearParagraphIndentation,
|
|
51
52
|
ClearParagraphSpacing,
|
|
@@ -1002,6 +1003,7 @@ _OP_TO_COMMAND: dict[str, type[Command]] = {
|
|
|
1002
1003
|
"sdt.delete": ContentControlsDelete,
|
|
1003
1004
|
"sections.set_columns": SetSectionColumns,
|
|
1004
1005
|
"find_replace": FindReplace,
|
|
1006
|
+
"clear_body": ClearBody,
|
|
1005
1007
|
"find_text": FindText,
|
|
1006
1008
|
"iter_runs": IterRuns,
|
|
1007
1009
|
"export_pdf": ExportPDF,
|
|
@@ -497,6 +497,27 @@ class PostExportOps:
|
|
|
497
497
|
or bool(self._row_cant_split_ops)
|
|
498
498
|
)
|
|
499
499
|
|
|
500
|
+
def discard_body_ops(self) -> None:
|
|
501
|
+
"""Drop every rewrite that addresses body content by position.
|
|
502
|
+
|
|
503
|
+
Cell/table formatting, row heights, captured REF paragraphs and
|
|
504
|
+
header/footer relocations are all keyed by body-relative indices
|
|
505
|
+
(``table_doc_index``, ``source_paragraph_index``, body node ids).
|
|
506
|
+
Once the body story has been replaced they point at nodes that no
|
|
507
|
+
longer exist — and would silently re-target whatever the caller
|
|
508
|
+
creates next at the same index. Document-wide rewrites
|
|
509
|
+
(``font_override``) do not address the body and are kept.
|
|
510
|
+
"""
|
|
511
|
+
self._cell_format_ops.clear()
|
|
512
|
+
self._cell_run_format_ops.clear()
|
|
513
|
+
self._cell_hyperlink_ops.clear()
|
|
514
|
+
self._cell_shading_ops.clear()
|
|
515
|
+
self._table_borders_ops.clear()
|
|
516
|
+
self._captured_refs.clear()
|
|
517
|
+
self._row_height_ops.clear()
|
|
518
|
+
self._row_cant_split_ops.clear()
|
|
519
|
+
self._header_footer_relocations.clear()
|
|
520
|
+
|
|
500
521
|
def add_row_cant_split(
|
|
501
522
|
self,
|
|
502
523
|
*,
|
|
@@ -23,6 +23,11 @@ Emits are **synchronous, best-effort** HTTP POSTs:
|
|
|
23
23
|
- Snapshot the URL at ``emit_begin`` time and carry it to ``emit_end``
|
|
24
24
|
so late end events can't be misattributed if a new sandbox run
|
|
25
25
|
swapped in a different URL between begin and end.
|
|
26
|
+
- Reuse one keep-alive connection per endpoint instead of a new TCP (and
|
|
27
|
+
TLS) connection per event. An idle connection the server has closed is
|
|
28
|
+
detected before sending and replaced; a send is never retried (the
|
|
29
|
+
registry does not deduplicate events). When a proxy applies to the URL
|
|
30
|
+
(``*_proxy`` env vars) each event goes through urllib as before.
|
|
26
31
|
|
|
27
32
|
This module is *intentionally byte-identical* across the docx / pptx /
|
|
28
33
|
xlsx SDKs — the three SDKs have no shared base. Duplicating ~100 LOC
|
|
@@ -31,10 +36,14 @@ of stdlib code is cheaper than spinning up a 4th release pipeline.
|
|
|
31
36
|
|
|
32
37
|
from __future__ import annotations
|
|
33
38
|
|
|
39
|
+
import http.client
|
|
34
40
|
import json
|
|
35
41
|
import os
|
|
42
|
+
import select
|
|
43
|
+
import threading
|
|
36
44
|
import time
|
|
37
45
|
import urllib.error
|
|
46
|
+
import urllib.parse
|
|
38
47
|
import urllib.request
|
|
39
48
|
import uuid
|
|
40
49
|
from typing import Any
|
|
@@ -46,6 +55,58 @@ _HTTP_TIMEOUT = 2.0
|
|
|
46
55
|
# original run's endpoint even if ATHENA_PTC_URL changed since.
|
|
47
56
|
_call_url: dict[str, str] = {}
|
|
48
57
|
|
|
58
|
+
# (scheme, host:port) -> an idle keep-alive connection. Checked out while a
|
|
59
|
+
# POST uses it, so no two threads ever share one.
|
|
60
|
+
_idle: dict[tuple[str, str], http.client.HTTPConnection] = {}
|
|
61
|
+
_idle_lock = threading.Lock()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _dropped(conn: http.client.HTTPConnection) -> bool:
|
|
65
|
+
"""An idle connection the server has closed (or that has unexpected data
|
|
66
|
+
waiting) reads as ready: never send on it."""
|
|
67
|
+
sock = conn.sock
|
|
68
|
+
if sock is None:
|
|
69
|
+
return True
|
|
70
|
+
try:
|
|
71
|
+
return bool(select.select([sock], [], [], 0)[0])
|
|
72
|
+
except (OSError, ValueError):
|
|
73
|
+
return True
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _post_keepalive(url: str, raw: bytes) -> None:
|
|
77
|
+
parts = urllib.parse.urlsplit(url)
|
|
78
|
+
if parts.scheme not in ("http", "https") or not parts.hostname:
|
|
79
|
+
raise ValueError(f"unsupported PTC URL: {url!r}")
|
|
80
|
+
target = (parts.path or "/") + (f"?{parts.query}" if parts.query else "")
|
|
81
|
+
key = (parts.scheme, parts.netloc)
|
|
82
|
+
with _idle_lock:
|
|
83
|
+
conn = _idle.pop(key, None)
|
|
84
|
+
if conn is not None and _dropped(conn):
|
|
85
|
+
conn.close()
|
|
86
|
+
conn = None
|
|
87
|
+
if conn is None:
|
|
88
|
+
factory = (
|
|
89
|
+
http.client.HTTPSConnection if parts.scheme == "https" else http.client.HTTPConnection
|
|
90
|
+
)
|
|
91
|
+
conn = factory(parts.netloc, timeout=_HTTP_TIMEOUT)
|
|
92
|
+
# Never retried: a failure after the request left may still have been
|
|
93
|
+
# processed, and the registry does not deduplicate events.
|
|
94
|
+
try:
|
|
95
|
+
conn.request("POST", target, body=raw, headers={"Content-Type": "application/json"})
|
|
96
|
+
response = conn.getresponse()
|
|
97
|
+
response.read()
|
|
98
|
+
except BaseException:
|
|
99
|
+
conn.close()
|
|
100
|
+
raise
|
|
101
|
+
if response.will_close:
|
|
102
|
+
conn.close()
|
|
103
|
+
return
|
|
104
|
+
with _idle_lock:
|
|
105
|
+
spare = _idle.pop(key, None)
|
|
106
|
+
_idle[key] = conn
|
|
107
|
+
if spare is not None:
|
|
108
|
+
spare.close()
|
|
109
|
+
|
|
49
110
|
|
|
50
111
|
def _send(url: str, body: dict[str, Any]) -> None:
|
|
51
112
|
"""Synchronously POST one PTC event. Best-effort; never raises.
|
|
@@ -63,14 +124,19 @@ def _send(url: str, body: dict[str, Any]) -> None:
|
|
|
63
124
|
key = "args" if body.get("phase") == "begin" else "result"
|
|
64
125
|
body[key] = {"__truncated__": True}
|
|
65
126
|
raw = json.dumps(body).encode("utf-8")
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
127
|
+
parts = urllib.parse.urlsplit(url)
|
|
128
|
+
proxy = urllib.request.getproxies().get(parts.scheme)
|
|
129
|
+
if proxy and not urllib.request.proxy_bypass(parts.hostname or ""):
|
|
130
|
+
req = urllib.request.Request(
|
|
131
|
+
url,
|
|
132
|
+
data=raw,
|
|
133
|
+
method="POST",
|
|
134
|
+
headers={"Content-Type": "application/json"},
|
|
135
|
+
)
|
|
136
|
+
urllib.request.urlopen(req, timeout=_HTTP_TIMEOUT).close()
|
|
137
|
+
else:
|
|
138
|
+
_post_keepalive(url, raw)
|
|
139
|
+
except (urllib.error.URLError, OSError, ValueError, http.client.HTTPException):
|
|
74
140
|
pass # never propagate — PTC is best-effort
|
|
75
141
|
|
|
76
142
|
|
|
@@ -19,6 +19,9 @@ _LONG_RUNNING_COMMAND_TYPES: frozenset[str] = frozenset(
|
|
|
19
19
|
# ordinary 60s budget committing a table batch even when the Python code
|
|
20
20
|
# uses the fast no-read cell path.
|
|
21
21
|
"TableSetCell",
|
|
22
|
+
# ``Document.clear()`` replaces the whole body story in one write;
|
|
23
|
+
# on a large document that single command can outlast the 60s budget.
|
|
24
|
+
"ClearBody",
|
|
22
25
|
}
|
|
23
26
|
)
|
|
24
27
|
|
|
@@ -36,7 +36,7 @@ if TYPE_CHECKING:
|
|
|
36
36
|
|
|
37
37
|
def _log_info(msg: str) -> None:
|
|
38
38
|
"""Minimal stderr logger; we avoid loguru to keep the SDK runtime
|
|
39
|
-
dependency tree small (this code runs inside
|
|
39
|
+
dependency tree small (this code runs inside sandboxes)."""
|
|
40
40
|
import sys
|
|
41
41
|
|
|
42
42
|
print(f"[docx-sdk] {msg}", file=sys.stderr)
|
|
@@ -84,6 +84,10 @@ class Session:
|
|
|
84
84
|
from docx._postproc import PostExportOps
|
|
85
85
|
|
|
86
86
|
self._post_export_ops: PostExportOps = PostExportOps()
|
|
87
|
+
# Bumped by ``Document.clear()``. ``Table`` proxies remember the
|
|
88
|
+
# generation they were created in, so a proxy from before a clear
|
|
89
|
+
# can never be re-anchored to a table created after it.
|
|
90
|
+
self._body_generation: int = 0
|
|
87
91
|
|
|
88
92
|
@property
|
|
89
93
|
def asset_id(self) -> str:
|
|
@@ -136,6 +136,10 @@ class Insert(Command):
|
|
|
136
136
|
# on the DOCX Studio call (renamed in transit so it doesn't collide with our
|
|
137
137
|
# discriminator).
|
|
138
138
|
value_type: str | None = None
|
|
139
|
+
# Text mode only. ``True`` continues the formatting of the run at the
|
|
140
|
+
# insertion point (``Run.add_text``); omitted means the inserted text is
|
|
141
|
+
# a fresh, unformatted run (``Paragraph.add_run``).
|
|
142
|
+
inherit_marks: bool | None = None
|
|
139
143
|
placement: str | None = None
|
|
140
144
|
content: dict[str, Any] | None = None
|
|
141
145
|
|
|
@@ -1543,6 +1547,18 @@ class FindReplace(Command):
|
|
|
1543
1547
|
max_replacements: int | None = None
|
|
1544
1548
|
|
|
1545
1549
|
|
|
1550
|
+
@dataclass
|
|
1551
|
+
class ClearBody(Command):
|
|
1552
|
+
"""Replace the whole body story with a single empty paragraph.
|
|
1553
|
+
|
|
1554
|
+
Athena extension backing :meth:`Document.clear`. One server-side
|
|
1555
|
+
write instead of a ``Replace`` per paragraph and per table-cell
|
|
1556
|
+
paragraph; headers, footers and notes are untouched and the final
|
|
1557
|
+
section's page setup survives. Returns ``{nodeId}`` of the new
|
|
1558
|
+
empty paragraph.
|
|
1559
|
+
"""
|
|
1560
|
+
|
|
1561
|
+
|
|
1546
1562
|
@dataclass
|
|
1547
1563
|
class FindText(Command):
|
|
1548
1564
|
"""Search document text and return bounded match context.
|
|
@@ -1576,8 +1592,8 @@ class IterRuns(Command):
|
|
|
1576
1592
|
|
|
1577
1593
|
# ---------------------------------------------------------------------------
|
|
1578
1594
|
# PDF Export (Athena extension — python-docx issue #113 keeps getting
|
|
1579
|
-
# re-asked despite being closed wontfix. DOCX Studio renders
|
|
1580
|
-
#
|
|
1595
|
+
# re-asked despite being closed wontfix. DOCX Studio renders the document
|
|
1596
|
+
# to bytes the client can download or persist.)
|
|
1581
1597
|
# ---------------------------------------------------------------------------
|
|
1582
1598
|
|
|
1583
1599
|
|
|
@@ -1759,6 +1775,9 @@ _RESPONSE_BEARING_TYPES: frozenset[str] = frozenset(
|
|
|
1759
1775
|
"ExportDocx",
|
|
1760
1776
|
# FindReplace returns a replacement count for the caller.
|
|
1761
1777
|
"FindReplace",
|
|
1778
|
+
# ClearBody replaces the whole body; Document.clear awaits it so a
|
|
1779
|
+
# rejected clear raises instead of leaving stale content behind.
|
|
1780
|
+
"ClearBody",
|
|
1762
1781
|
}
|
|
1763
1782
|
)
|
|
1764
1783
|
|
|
@@ -1852,6 +1871,7 @@ for _cls, _issue, _desc in [
|
|
|
1852
1871
|
(ContentControlsDelete, "python-docx#155", "SDT delete"),
|
|
1853
1872
|
(SetSectionColumns, "python-docx#167", "Section multi-column layout"),
|
|
1854
1873
|
(FindReplace, "python-docx#30", "Formatting-preserving find/replace"),
|
|
1874
|
+
(ClearBody, None, "Document.clear — bulk body reset"),
|
|
1855
1875
|
(FindText, "python-docx#980", "Fast document text search"),
|
|
1856
1876
|
(IterRuns, "python-docx#980", "Run stream for run-isolation"),
|
|
1857
1877
|
(ExportPDF, "python-docx#113", "PDF export"),
|
|
@@ -1990,6 +2010,7 @@ __all__ = [
|
|
|
1990
2010
|
"ContentControlsDelete",
|
|
1991
2011
|
"SetSectionColumns",
|
|
1992
2012
|
"FindReplace",
|
|
2013
|
+
"ClearBody",
|
|
1993
2014
|
"FindText",
|
|
1994
2015
|
"IterRuns",
|
|
1995
2016
|
"ExportPDF",
|
|
@@ -965,6 +965,7 @@ class Document:
|
|
|
965
965
|
from docx.commands import CreateTOC
|
|
966
966
|
from docx.toc import TableOfContents
|
|
967
967
|
|
|
968
|
+
self._ensure_open()
|
|
968
969
|
cmd = CreateTOC(
|
|
969
970
|
at=at or {"kind": "documentEnd"},
|
|
970
971
|
levels=[levels[0], levels[1]],
|
|
@@ -1010,6 +1011,7 @@ class Document:
|
|
|
1010
1011
|
from docx.charts import Chart
|
|
1011
1012
|
from docx.commands import CreateChart
|
|
1012
1013
|
|
|
1014
|
+
self._ensure_open()
|
|
1013
1015
|
cmd = CreateChart(
|
|
1014
1016
|
chart_type=chart_type,
|
|
1015
1017
|
data=data,
|
|
@@ -1078,6 +1080,7 @@ class Document:
|
|
|
1078
1080
|
mime = sniff_content_type(img_bytes, fallback_path=image_path or "")
|
|
1079
1081
|
kwargs["src"] = f"data:{mime};base64,{base64.b64encode(img_bytes).decode()}"
|
|
1080
1082
|
cmd = CreateWatermark(**kwargs)
|
|
1083
|
+
self._ensure_open()
|
|
1081
1084
|
result = run_sync(self._session.send_command(cmd))
|
|
1082
1085
|
if isinstance(result, dict):
|
|
1083
1086
|
return str(result.get("id") or result.get("watermarkId") or "")
|
|
@@ -1105,6 +1108,7 @@ class Document:
|
|
|
1105
1108
|
from docx.commands import CreateContentControl
|
|
1106
1109
|
from docx.sdt import ContentControl
|
|
1107
1110
|
|
|
1111
|
+
self._ensure_open()
|
|
1108
1112
|
cmd = CreateContentControl(
|
|
1109
1113
|
control_kind=control_kind,
|
|
1110
1114
|
at=at or {"kind": "documentEnd"},
|
|
@@ -1457,87 +1461,52 @@ class Document:
|
|
|
1457
1461
|
|
|
1458
1462
|
@athena_extension(
|
|
1459
1463
|
since="0.14.0",
|
|
1460
|
-
description="Document.clear —
|
|
1464
|
+
description="Document.clear — reset the body to one empty paragraph in a single server write.",
|
|
1461
1465
|
)
|
|
1462
1466
|
def clear(self) -> None:
|
|
1463
1467
|
"""Clear the document body so subsequent writes start from a clean slate.
|
|
1464
1468
|
|
|
1465
|
-
Athena extension — python-docx has no equivalent.
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1469
|
+
Athena extension — python-docx has no equivalent. Replaces the
|
|
1470
|
+
whole body story (paragraphs, headings, tables, images) with a
|
|
1471
|
+
single empty paragraph in one server-side ``ClearBody`` command,
|
|
1472
|
+
so the cost is one round-trip regardless of document size.
|
|
1473
|
+
Headers, footers, footnotes and the final section's page setup
|
|
1474
|
+
are untouched; header/footer references the final section
|
|
1475
|
+
inherited from removed inline sections are kept.
|
|
1476
|
+
|
|
1477
|
+
Export-time rewrites queued against the old body (cell/table
|
|
1478
|
+
formatting, row heights, captured REF paragraphs) are dropped
|
|
1479
|
+
with it, and table numbering restarts from zero, so formatting
|
|
1480
|
+
recorded for a deleted table can never land on one created
|
|
1481
|
+
afterwards at the same position. :class:`~docx.table.Table`
|
|
1482
|
+
proxies obtained before the clear are dead: using one raises
|
|
1483
|
+
:class:`~docx.errors.ValidationError` instead of silently
|
|
1484
|
+
targeting a new table at the same index.
|
|
1485
|
+
|
|
1486
|
+
Raises :class:`~docx.errors.DocxError` if the server rejects the
|
|
1487
|
+
command; the body is never left half-cleared because the whole
|
|
1488
|
+
replacement is applied atomically or not at all, and no local
|
|
1489
|
+
state is touched unless the server accepted it.
|
|
1483
1490
|
"""
|
|
1484
|
-
from docx.
|
|
1491
|
+
from docx.commands import ClearBody
|
|
1485
1492
|
|
|
1486
1493
|
self._ensure_open()
|
|
1487
1494
|
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1495
|
+
run_sync(
|
|
1496
|
+
self._session.send_command(ClearBody()),
|
|
1497
|
+
timeout=LONG_RUNNING_COMMAND_TIMEOUT_SECONDS,
|
|
1498
|
+
)
|
|
1499
|
+
|
|
1500
|
+
self._session._post_export_ops.discard_body_ops()
|
|
1501
|
+
self._table_doc_index_counter = None
|
|
1502
|
+
self._session._table_session_count = 0 # type: ignore[attr-defined]
|
|
1503
|
+
self._session._body_generation += 1
|
|
1504
|
+
|
|
1505
|
+
# The next add_paragraph after clear() should start a new list,
|
|
1506
|
+
# not attach to whatever the previous run was doing.
|
|
1491
1507
|
self._last_list_item_id = None
|
|
1492
1508
|
self._last_list_kind = None
|
|
1493
1509
|
|
|
1494
|
-
for paragraph in self.paragraphs:
|
|
1495
|
-
try:
|
|
1496
|
-
if not isinstance(paragraph, _Paragraph):
|
|
1497
|
-
continue
|
|
1498
|
-
if paragraph.text:
|
|
1499
|
-
paragraph.clear()
|
|
1500
|
-
except Exception as err:
|
|
1501
|
-
_log_warn(f"Document.clear: skipped a paragraph: {err!r}")
|
|
1502
|
-
|
|
1503
|
-
# Cell clearing must NOT go through ``cell.text = ""``: ``self.tables``
|
|
1504
|
-
# materializes fresh ephemeral ``Table`` proxies whose
|
|
1505
|
-
# ``_written_cells`` set is empty, so the setter would take the
|
|
1506
|
-
# fast (append) path — ``tables.set_cell insideEnd`` with an empty
|
|
1507
|
-
# paragraph fragment — which leaves prior cell content untouched.
|
|
1508
|
-
# Instead, drive ``doc.replace`` directly against each cell's
|
|
1509
|
-
# inner paragraphs (mirroring the clear-then-insert branch in
|
|
1510
|
-
# ``_Cell.text``) so the clear semantics don't depend on the
|
|
1511
|
-
# in-session write tracking that this code path bypasses.
|
|
1512
|
-
from docx.text.paragraph import _node_text
|
|
1513
|
-
|
|
1514
|
-
for table in self.tables:
|
|
1515
|
-
for row in table.rows:
|
|
1516
|
-
for cell in row.cells:
|
|
1517
|
-
try:
|
|
1518
|
-
existing_pids = cell._inner_paragraph_ids()
|
|
1519
|
-
for pid in existing_pids:
|
|
1520
|
-
existing_text = _node_text(self._session, pid)
|
|
1521
|
-
if not existing_text:
|
|
1522
|
-
continue
|
|
1523
|
-
run_sync(
|
|
1524
|
-
self._session.doc.replace(
|
|
1525
|
-
{
|
|
1526
|
-
"target": {
|
|
1527
|
-
"kind": "text",
|
|
1528
|
-
"blockId": pid,
|
|
1529
|
-
"range": {
|
|
1530
|
-
"start": 0,
|
|
1531
|
-
"end": len(existing_text),
|
|
1532
|
-
},
|
|
1533
|
-
},
|
|
1534
|
-
"text": "",
|
|
1535
|
-
},
|
|
1536
|
-
),
|
|
1537
|
-
)
|
|
1538
|
-
except Exception as err:
|
|
1539
|
-
_log_warn(f"Document.clear: skipped a cell: {err!r}")
|
|
1540
|
-
|
|
1541
1510
|
def add_paragraph(
|
|
1542
1511
|
self,
|
|
1543
1512
|
text: str = "",
|
|
@@ -1012,10 +1012,9 @@ class _HeaderFooter:
|
|
|
1012
1012
|
# paragraph that doesn't have the style they asked for; the
|
|
1013
1013
|
# right behavior is to bubble so they can see the failure.
|
|
1014
1014
|
para.style = style_str
|
|
1015
|
-
#
|
|
1016
|
-
#
|
|
1017
|
-
#
|
|
1018
|
-
self._record_relocation(node_id=node_id, ref_id=ref_id, node_type="paragraph")
|
|
1015
|
+
# docx-studio writes headerFooterPart story content into the real
|
|
1016
|
+
# ``word/<header|footer>N.xml`` part, so a post-export relocation
|
|
1017
|
+
# would rip a real body block into the part.
|
|
1019
1018
|
return para
|
|
1020
1019
|
|
|
1021
1020
|
def add_table(
|
|
@@ -1100,10 +1099,8 @@ class _HeaderFooter:
|
|
|
1100
1099
|
# width so the lossy round-trip is visible.
|
|
1101
1100
|
if int(width) > 0:
|
|
1102
1101
|
_warn_hf_width(rows, cols)
|
|
1103
|
-
#
|
|
1104
|
-
#
|
|
1105
|
-
# ``word/<header|footer>N.xml``.
|
|
1106
|
-
self._record_relocation(node_id=node_id, ref_id=ref_id, node_type="table")
|
|
1102
|
+
# See add_paragraph: the server writes the table into the real
|
|
1103
|
+
# part, so a post-export relocation would corrupt the body.
|
|
1107
1104
|
return tbl
|
|
1108
1105
|
|
|
1109
1106
|
def _record_relocation(
|