athena-python-docx 0.21.2__tar.gz → 0.21.5__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.2 → athena_python_docx-0.21.5}/PKG-INFO +1 -1
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/__init__.py +3 -2
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_buffer.py +93 -5
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/client.py +8 -3
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/table.py +74 -6
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/text/paragraph.py +34 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/text/run.py +10 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/pyproject.toml +1 -1
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/fake_session.py +60 -46
- athena_python_docx-0.21.5/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +6 -0
- athena_python_docx-0.21.5/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +9 -0
- athena_python_docx-0.21.5/tests/fidelity/op_snapshots/35_full_report.json +39 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -2
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -6
- athena_python_docx-0.21.5/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +8 -0
- athena_python_docx-0.21.5/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +19 -0
- athena_python_docx-0.21.5/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +604 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/68_invoice.json +0 -8
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/69_newsletter.json +0 -24
- athena_python_docx-0.21.5/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +35 -0
- athena_python_docx-0.21.5/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +8 -0
- athena_python_docx-0.21.5/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +124 -0
- athena_python_docx-0.21.5/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +34 -0
- athena_python_docx-0.21.5/tests/fidelity/op_snapshots/ex14_styled_report_table.json +37 -0
- athena_python_docx-0.21.5/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +272 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/mega01_book_chapter.json +3 -87
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/mega02_research_proposal.json +5 -53
- athena_python_docx-0.21.5/tests/fidelity/op_snapshots/mega03_financial_statement.json +45 -0
- athena_python_docx-0.21.5/tests/fidelity/op_snapshots/mega05_user_manual.json +49 -0
- athena_python_docx-0.21.5/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +50 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/mega09_signed_contract.json +2 -24
- athena_python_docx-0.21.5/tests/fidelity/op_snapshots/mega10_api_documentation.json +95 -0
- athena_python_docx-0.21.5/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +36 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_buffer.py +97 -0
- athena_python_docx-0.21.5/tests/test_cell_fastpath_runs.py +208 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_positional_cell_id.py +69 -3
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_ptc.py +35 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/uv.lock +1 -1
- athena_python_docx-0.21.2/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -12
- athena_python_docx-0.21.2/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -19
- athena_python_docx-0.21.2/tests/fidelity/op_snapshots/35_full_report.json +0 -78
- athena_python_docx-0.21.2/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -19
- athena_python_docx-0.21.2/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -53
- athena_python_docx-0.21.2/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -2804
- athena_python_docx-0.21.2/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -89
- athena_python_docx-0.21.2/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -14
- athena_python_docx-0.21.2/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -764
- athena_python_docx-0.21.2/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -94
- athena_python_docx-0.21.2/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -148
- athena_python_docx-0.21.2/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -632
- athena_python_docx-0.21.2/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -122
- athena_python_docx-0.21.2/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -137
- athena_python_docx-0.21.2/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -170
- athena_python_docx-0.21.2/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -271
- athena_python_docx-0.21.2/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -132
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/.gitignore +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/CLAUDE.md +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/DOCX_EXEC_LAB.md +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/README.md +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_athena_extension.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_batching.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_citations.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_execution.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_http.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_http_doc.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_image_utils.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_postproc.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_ptc.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_references.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_table_styles.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_timeouts.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/api.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/bookmarks.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/charts.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/commands.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/comments.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/document.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/enum/__init__.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/enum/section.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/enum/shape.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/enum/style.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/enum/table.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/enum/text.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/errors.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/exceptions.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/fields.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/footnotes.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/math.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/opc/__init__.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/opc/coreprops.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/oxml/__init__.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/revisions.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/sdt.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/section.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/session.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/settings.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/shape.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/shared.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/styles/__init__.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/styles/style.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/styles/styles.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/text/__init__.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/text/font.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/text/hyperlink.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/text/pagebreak.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/text/parfmt.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/text/tabstops.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/toc.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/typing.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/docx_exec_lab.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/dump_wire_fixtures.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/publish.sh +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/release.sh +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/round_trip_smoke.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/smoke_test_block_not_found.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/validate_find_replace_asset.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/__init__.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/conftest.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/METHODOLOGY.md +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/README.md +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/__init__.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/ab_probe_cases.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/ab_probe_runner.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/auto_gen_cases.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/binary_round_trip.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/cases.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/complex_cases.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/coverage_report.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/extract.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/extreme_cases.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/firm_templates/README.md +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/firm_templates/__init__.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/firm_templates/_runner.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/firm_templates/extractor.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/local_runner.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/mega_cases.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshot.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/ours_spec.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/parity_crawl.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/parity_diff.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/real_world_cases.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/round_trip_tests.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/stock_spec.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/test_e2e_against_staging.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/README.md +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/__init__.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/baseline_gaps.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/compare.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/intentional_deviations.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/introspect.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/reports/gap_report.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/run_parity.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/snapshots/athena_latest.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/test_parity_gap.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_add_section_extract_items.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_athena_extensions_contract.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_athena_extensions_registry.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_batching_perf.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_block_not_found_error.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_cell_add_table_not_supported.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_cell_inner_format_stash.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_cell_inner_run_format_stash.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_cell_inner_run_guard.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_cell_text_plain_fastpath.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_cell_text_replace_semantics.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_citations.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_collapsed_range_format.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_command_dataclasses.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_commands.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_comments.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_document_asset_id_property.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_document_clear.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_document_create.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_document_create_from_template.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_document_factory_validation.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_docx_exec_lab.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_e2e_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_execution_scope.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_find_replace_session_open.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_http_transport.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_hyperlink_coalescing.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_image_url_data_uri.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_insert_deferred.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_iter_inner_content.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_list_styles.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_merged_cell_secondary_slot.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_merged_cells.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_oxml_shim.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_paragraph_text_len_cache.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_parity_misc.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_parity_round2.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_phase_a_behavior.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_phase_b_headers_footers.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_phase_c_tables.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_postproc_cell_format_rewrite.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_postproc_ref_restore.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_pr19766_review_fixes.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_python_docx_api_parity.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_revisions.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_add_paragraph_style.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_add_picture.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_add_run.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_comments_add_comment.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_comments_get.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_document_audit.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_document_element.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_enum_section.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_font_audit.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_header_footer.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_hyperlink.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_inline_shape.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_misc.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_paragraph_strict.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_paragraph_style.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_parfmt.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_row_col_cell.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_run_add_break.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_run_bool_setters.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_run_style.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_run_style_strict.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_run_text.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_run_underline.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_section_audit.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_section_dimensions.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_section_onoff.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_settings.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_shared_audit.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_style.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_styles.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_table_audit.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_table_cell.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_table_dimensions.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_table_layout.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_smoke_integration.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_style_acceptance.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_style_font.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_style_setters_contract.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_table_set_cell_perf.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_table_style_id_resolution.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_temporarily_unavailable.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_validate_find_replace_asset_script.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_wire_contract.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_xml_attr_guard.py +0 -0
- {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_zod_wire_contract.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: athena-python-docx
|
|
3
|
-
Version: 0.21.
|
|
3
|
+
Version: 0.21.5
|
|
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>
|
|
@@ -6,10 +6,10 @@ See CLAUDE.md for the API parity contract.
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
-
__version__ = "0.21.
|
|
9
|
+
__version__ = "0.21.5"
|
|
10
10
|
|
|
11
11
|
from docx.api import Document
|
|
12
|
-
from docx._buffer import flush_all
|
|
12
|
+
from docx._buffer import discard_all, flush_all
|
|
13
13
|
|
|
14
14
|
# Re-exports python-docx ships at docx top-level for convenience.
|
|
15
15
|
from docx.shared import Emu, Inches, Pt, Cm, Mm, Twips, Length, RGBColor
|
|
@@ -25,6 +25,7 @@ __all__ = [
|
|
|
25
25
|
"Length",
|
|
26
26
|
"RGBColor",
|
|
27
27
|
"flush_all",
|
|
28
|
+
"discard_all",
|
|
28
29
|
"__version__",
|
|
29
30
|
]
|
|
30
31
|
|
|
@@ -124,10 +124,12 @@ if TYPE_CHECKING:
|
|
|
124
124
|
# HTTP POST to agora (see ``_ptc._send``), so emitting one card per low-level
|
|
125
125
|
# mutation produces hundreds of sub-cards per script and pays full network
|
|
126
126
|
# RTT on every one. The allow-list keeps the per-action signal — one card
|
|
127
|
-
# per logical block creation
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
_PTC_EMIT_TOOLS: frozenset[str] = frozenset(
|
|
127
|
+
# per logical block/citation creation — without the spam or the cumulative
|
|
128
|
+
# latency. Asset-level creation events (``CreateDocument``) are emitted from
|
|
129
|
+
# ``Document.create`` directly, not through this buffer path.
|
|
130
|
+
_PTC_EMIT_TOOLS: frozenset[str] = frozenset(
|
|
131
|
+
{"CreateParagraph", "CreateHeading", "CreateTable", "AddCitation"}
|
|
132
|
+
)
|
|
131
133
|
|
|
132
134
|
|
|
133
135
|
def _should_emit_ptc(cmd: Command) -> bool:
|
|
@@ -243,6 +245,42 @@ def flush_all(*, strict: bool = False) -> None:
|
|
|
243
245
|
raise FlushAllError(failures=failures)
|
|
244
246
|
|
|
245
247
|
|
|
248
|
+
def discard_all() -> int:
|
|
249
|
+
"""Drop the pending commands of every live CommandBuffer; return the count.
|
|
250
|
+
|
|
251
|
+
The failed-script counterpart of :func:`flush_all`: after user code
|
|
252
|
+
raises, the executor discards the half-buffered tail instead of posting
|
|
253
|
+
it (see :meth:`CommandBuffer.discard_pending`). Safe to call when no
|
|
254
|
+
buffers exist (returns 0).
|
|
255
|
+
|
|
256
|
+
Buffers from a previous ``execute_word_document_code`` call are skipped
|
|
257
|
+
(mirroring :func:`flush_all`'s stale handling) so cross-execution
|
|
258
|
+
leftovers stay visible to diagnostics instead of being silently
|
|
259
|
+
swallowed. Best-effort like :meth:`CommandBuffer.discard_pending`: a
|
|
260
|
+
batch already handed to an in-flight flush cannot be recalled.
|
|
261
|
+
"""
|
|
262
|
+
with _registry_lock:
|
|
263
|
+
snapshot = list(_active_buffers)
|
|
264
|
+
live = [ref for ref in snapshot if ref() is not None]
|
|
265
|
+
_active_buffers[:] = live
|
|
266
|
+
|
|
267
|
+
dropped = 0
|
|
268
|
+
for ref in live:
|
|
269
|
+
buf = ref()
|
|
270
|
+
if buf is None:
|
|
271
|
+
continue
|
|
272
|
+
if buf.is_stale_for_current_execution:
|
|
273
|
+
pending_count = buf.pending_count
|
|
274
|
+
if pending_count > 0:
|
|
275
|
+
sys.stderr.write(
|
|
276
|
+
f"[docx-sdk] discard_all: skipping stale buffer "
|
|
277
|
+
f"{buf.asset_id} with {pending_count} unflushed command(s)\n",
|
|
278
|
+
)
|
|
279
|
+
continue
|
|
280
|
+
dropped += buf.discard_pending()
|
|
281
|
+
return dropped
|
|
282
|
+
|
|
283
|
+
|
|
246
284
|
# ---------------------------------------------------------------------------
|
|
247
285
|
# CommandBuffer
|
|
248
286
|
# ---------------------------------------------------------------------------
|
|
@@ -314,6 +352,12 @@ class CommandBuffer:
|
|
|
314
352
|
# original semantics.
|
|
315
353
|
self._change_mode: str | None = None
|
|
316
354
|
self._user: dict[str, str] | None = None
|
|
355
|
+
# A batch the background idle timer failed to deliver. The timer
|
|
356
|
+
# thread can't raise into user code, so the failure is stashed here
|
|
357
|
+
# and re-raised on the NEXT buffer interaction (call/flush) — a
|
|
358
|
+
# dropped batch must never be reported as success by a later
|
|
359
|
+
# ``doc.save()`` that finds an empty buffer.
|
|
360
|
+
self._deferred_flush_error: Exception | None = None
|
|
317
361
|
# client_node_id → list of (proxy, attr) pairs to update with the
|
|
318
362
|
# real nodeId after flush. Populated by ``add_paragraph`` /
|
|
319
363
|
# ``add_heading`` / etc. when they queue a Create with a
|
|
@@ -420,6 +464,7 @@ class CommandBuffer:
|
|
|
420
464
|
proxy's ``_node_id`` in-place.
|
|
421
465
|
"""
|
|
422
466
|
self._assert_current_execution()
|
|
467
|
+
self._raise_deferred_flush_error()
|
|
423
468
|
if self._closed:
|
|
424
469
|
raise RuntimeError(
|
|
425
470
|
f"CommandBuffer for {self._asset_id} is closed",
|
|
@@ -488,6 +533,7 @@ class CommandBuffer:
|
|
|
488
533
|
UUID swap.
|
|
489
534
|
"""
|
|
490
535
|
self._assert_current_execution()
|
|
536
|
+
self._raise_deferred_flush_error()
|
|
491
537
|
with self._lock:
|
|
492
538
|
self._cancel_timer_locked()
|
|
493
539
|
pending = self._pending
|
|
@@ -633,12 +679,54 @@ class CommandBuffer:
|
|
|
633
679
|
self._timer.cancel()
|
|
634
680
|
self._timer = None
|
|
635
681
|
|
|
682
|
+
def _raise_deferred_flush_error(self) -> None:
|
|
683
|
+
"""Re-raise a failure captured by the background idle timer.
|
|
684
|
+
|
|
685
|
+
Cleared on raise so the surfaced traceback is delivered exactly once;
|
|
686
|
+
the batch it describes is already gone either way.
|
|
687
|
+
"""
|
|
688
|
+
with self._lock:
|
|
689
|
+
err = self._deferred_flush_error
|
|
690
|
+
self._deferred_flush_error = None
|
|
691
|
+
if err is not None:
|
|
692
|
+
raise err
|
|
693
|
+
|
|
694
|
+
def discard_pending(self) -> int:
|
|
695
|
+
"""Drop buffered commands without sending them; return the count.
|
|
696
|
+
|
|
697
|
+
Used by the sandbox executor when user code raised: posting the dead
|
|
698
|
+
script's half-buffered tail writes content whose prerequisites failed
|
|
699
|
+
(and duplicates it when the agent retries the whole script).
|
|
700
|
+
|
|
701
|
+
Best-effort: only commands still sitting in the pending queue are
|
|
702
|
+
dropped. A batch the idle timer has already drained and handed to the
|
|
703
|
+
HTTP client is in flight and cannot be recalled (the module
|
|
704
|
+
docstring's timer-race note applies here too).
|
|
705
|
+
"""
|
|
706
|
+
with self._lock:
|
|
707
|
+
self._cancel_timer_locked()
|
|
708
|
+
pending = self._pending
|
|
709
|
+
self._pending = []
|
|
710
|
+
self._proxy_id_refs = {}
|
|
711
|
+
self._deferred_flush_error = None
|
|
712
|
+
_ptc_emit_end_batch(pending, is_error=True)
|
|
713
|
+
return len(pending)
|
|
714
|
+
|
|
636
715
|
def _auto_flush(self) -> None:
|
|
637
716
|
if self.is_stale_for_current_execution:
|
|
638
717
|
return
|
|
639
718
|
try:
|
|
640
719
|
self.flush()
|
|
641
720
|
except Exception as e: # noqa: BLE001
|
|
721
|
+
# The timer thread has no caller to raise into. Record the
|
|
722
|
+
# failure so the next call()/flush() (e.g. ``doc.save()``)
|
|
723
|
+
# surfaces it instead of reporting a drained buffer as success.
|
|
724
|
+
e.add_note(
|
|
725
|
+
"raised by a background docx-sdk auto-flush that failed earlier; "
|
|
726
|
+
"the commands in that batch were NOT applied",
|
|
727
|
+
)
|
|
728
|
+
with self._lock:
|
|
729
|
+
self._deferred_flush_error = e
|
|
642
730
|
sys.stderr.write(
|
|
643
731
|
f"[docx-sdk] auto-flush failed for {self._asset_id}: {e}\n",
|
|
644
732
|
)
|
|
@@ -650,4 +738,4 @@ class CommandBuffer:
|
|
|
650
738
|
)
|
|
651
739
|
|
|
652
740
|
|
|
653
|
-
__all__ = ["CommandBuffer", "flush_all", "DEFAULT_AUTO_FLUSH_SECONDS"]
|
|
741
|
+
__all__ = ["CommandBuffer", "flush_all", "discard_all", "DEFAULT_AUTO_FLUSH_SECONDS"]
|
|
@@ -223,9 +223,14 @@ class Session:
|
|
|
223
223
|
|
|
224
224
|
handle = self._doc_handle
|
|
225
225
|
buffer = getattr(handle, "buffer", None) if handle is not None else None
|
|
226
|
-
if buffer is
|
|
227
|
-
|
|
228
|
-
|
|
226
|
+
if buffer is None:
|
|
227
|
+
_log_info(f"Saved {self._asset_id} (no command buffer)")
|
|
228
|
+
return
|
|
229
|
+
flushed = buffer.flush()
|
|
230
|
+
# flush() raises on delivery failure — including one captured earlier
|
|
231
|
+
# by the background auto-flush — so this success line is only reached
|
|
232
|
+
# when nothing the buffer ever held was silently dropped.
|
|
233
|
+
_log_info(f"Saved {self._asset_id} ({len(flushed)} buffered command(s) flushed)")
|
|
229
234
|
|
|
230
235
|
async def close(self) -> None:
|
|
231
236
|
"""Close the session. Idempotent."""
|
|
@@ -458,6 +458,13 @@ class Table:
|
|
|
458
458
|
# Cells written before 0.15 (the pre-tracking world) silently fall
|
|
459
459
|
# back to the legacy append behavior on their first 0.15+ rewrite.
|
|
460
460
|
self._written_cells: set[tuple[int, int]] = set()
|
|
461
|
+
# Cell → plain text the SDK itself wrote this session via ``.text``.
|
|
462
|
+
# Powers the read-free ``_Cell.paragraphs``/``runs`` fast path: while
|
|
463
|
+
# a cell's full content is SDK-known (single plain-text run), header/
|
|
464
|
+
# zebra formatting loops need zero query round-trips. Any write the
|
|
465
|
+
# SDK can't model (markdown text, ``add_paragraph``, cell-inner
|
|
466
|
+
# ``add_run``) evicts the entry and restores the query path.
|
|
467
|
+
self._fastpath_cell_text: dict[tuple[int, int], str] = {}
|
|
461
468
|
|
|
462
469
|
def _fresh_node_info(self) -> tuple[str, dict]:
|
|
463
470
|
"""Resolve the live nodeId and return ``(node_id, info_dict)``.
|
|
@@ -1541,11 +1548,13 @@ class _Cell:
|
|
|
1541
1548
|
|
|
1542
1549
|
Reads the proxy's stored ``_node_id`` with NO round-trip. DOCX Studio
|
|
1543
1550
|
tables carry real (non-``t:N``) node ids → returns ``None`` so the
|
|
1544
|
-
caller queries (DOCX Studio is pooled, so that read is cheap anyway).
|
|
1545
|
-
freshly-created docx-editor table still holds a client UUID until its
|
|
1546
|
-
create flushes
|
|
1547
|
-
|
|
1548
|
-
|
|
1551
|
+
caller queries (DOCX Studio is pooled, so that read is cheap anyway).
|
|
1552
|
+
A freshly-created docx-editor table still holds a client UUID until its
|
|
1553
|
+
create flushes. For docx-editor, :meth:`Document.add_table` already
|
|
1554
|
+
knows the table's eventual document-order index, so SDK-created body
|
|
1555
|
+
tables can fast-path before a fragile first ``tables.get_cells`` query.
|
|
1556
|
+
We first try to flush the pending create so DOCX Studio's non-positional
|
|
1557
|
+
table ids can still fall back to the query path after the id rewrite.
|
|
1549
1558
|
|
|
1550
1559
|
Gated to a **known uniform grid**: the table must have been created
|
|
1551
1560
|
via :meth:`Document.add_table` (so ``_doc_index`` is set and the full
|
|
@@ -1557,11 +1566,27 @@ class _Cell:
|
|
|
1557
1566
|
fall back to the query path to stay address-correct.
|
|
1558
1567
|
"""
|
|
1559
1568
|
table = self._table
|
|
1560
|
-
|
|
1569
|
+
doc_index = getattr(table, "_doc_index", None)
|
|
1570
|
+
if doc_index is None or getattr(table, "_has_merges", False):
|
|
1561
1571
|
return None
|
|
1562
1572
|
nid = getattr(table, "_node_id", None)
|
|
1563
1573
|
if isinstance(nid, str) and nid.startswith("t:") and nid[2:].isdigit():
|
|
1564
1574
|
return f"tc:{nid[2:]}:{self._row}:{self._col}"
|
|
1575
|
+
if (
|
|
1576
|
+
isinstance(nid, str)
|
|
1577
|
+
and nid.startswith("t_")
|
|
1578
|
+
and getattr(table, "_header_footer_ref_id", None) is None
|
|
1579
|
+
):
|
|
1580
|
+
handle = getattr(table._session, "_doc_handle", None)
|
|
1581
|
+
buf = getattr(handle, "buffer", None) if handle is not None else None
|
|
1582
|
+
if buf is not None:
|
|
1583
|
+
buf.flush()
|
|
1584
|
+
nid = getattr(table, "_node_id", None)
|
|
1585
|
+
if isinstance(nid, str) and nid.startswith("t:") and nid[2:].isdigit():
|
|
1586
|
+
return f"tc:{nid[2:]}:{self._row}:{self._col}"
|
|
1587
|
+
if not (isinstance(nid, str) and nid.startswith("t_")):
|
|
1588
|
+
return None
|
|
1589
|
+
return f"tc:{doc_index}:{self._row}:{self._col}"
|
|
1565
1590
|
return None
|
|
1566
1591
|
|
|
1567
1592
|
def _cell_id(self) -> str:
|
|
@@ -1882,6 +1907,10 @@ class _Cell:
|
|
|
1882
1907
|
# to the same position takes the clear-then-insert branch above
|
|
1883
1908
|
# (see ``Table._written_cells`` docstring).
|
|
1884
1909
|
self._table._written_cells.add(cell_key)
|
|
1910
|
+
if _is_plain_text(value) and value:
|
|
1911
|
+
self._table._fastpath_cell_text[cell_key] = value
|
|
1912
|
+
else:
|
|
1913
|
+
self._table._fastpath_cell_text.pop(cell_key, None)
|
|
1885
1914
|
return
|
|
1886
1915
|
except Exception as e:
|
|
1887
1916
|
raise RuntimeError(
|
|
@@ -1902,6 +1931,38 @@ class _Cell:
|
|
|
1902
1931
|
# debugger and gives ``_apply_inline`` and friends something
|
|
1903
1932
|
# stable to consult when we eventually add a materialization
|
|
1904
1933
|
# fallback at the SDK layer.
|
|
1934
|
+
fast_text = self._table._fastpath_cell_text.get((self._row, self._col))
|
|
1935
|
+
if fast_text is not None:
|
|
1936
|
+
positional = self._positional_cell_id()
|
|
1937
|
+
if positional is not None:
|
|
1938
|
+
# Read-free fast path: the SDK wrote this cell's entire
|
|
1939
|
+
# content itself (one plain-text run), so it can hand back a
|
|
1940
|
+
# positional paragraph proxy without the per-cell
|
|
1941
|
+
# ``getNodeById`` walk. Formatting on its runs buffers a
|
|
1942
|
+
# whole-cell ``FormatApply`` against the ``tc:`` id — the
|
|
1943
|
+
# applier's documented cell-formatting semantics — so header/
|
|
1944
|
+
# zebra loops batch instead of paying one Y.Doc reload per
|
|
1945
|
+
# cell.
|
|
1946
|
+
ooxml_base = self._ooxml_address_base()
|
|
1947
|
+
fast_p = Paragraph(
|
|
1948
|
+
session=self._table._session,
|
|
1949
|
+
node_id=positional,
|
|
1950
|
+
node_type="paragraph",
|
|
1951
|
+
in_cell=True,
|
|
1952
|
+
cell_ooxml_address=(
|
|
1953
|
+
(*ooxml_base, 0) if ooxml_base is not None else None
|
|
1954
|
+
),
|
|
1955
|
+
cell=self,
|
|
1956
|
+
)
|
|
1957
|
+
fast_p._cell_inner = True # type: ignore[attr-defined]
|
|
1958
|
+
fast_p._cell_address = { # type: ignore[attr-defined]
|
|
1959
|
+
"kind": "block",
|
|
1960
|
+
"nodeType": "tableCell",
|
|
1961
|
+
"nodeId": positional,
|
|
1962
|
+
}
|
|
1963
|
+
fast_p._fastpath_runs_text = fast_text # type: ignore[attr-defined]
|
|
1964
|
+
return [fast_p]
|
|
1965
|
+
|
|
1905
1966
|
pids = self._inner_paragraph_ids()
|
|
1906
1967
|
# Resolve the cell address ONCE and re-use it for every
|
|
1907
1968
|
# paragraph; the original draft called ``self._cell_address()``
|
|
@@ -1970,6 +2031,9 @@ class _Cell:
|
|
|
1970
2031
|
) -> "Paragraph":
|
|
1971
2032
|
"""Append a paragraph to the cell.
|
|
1972
2033
|
|
|
2034
|
+
Evicts the cell from the read-free ``paragraphs`` fast path — the
|
|
2035
|
+
cell no longer holds the single SDK-written run that path models.
|
|
2036
|
+
|
|
1973
2037
|
Mirrors python-docx ``_Cell.add_paragraph(text, style)``. The
|
|
1974
2038
|
``style`` argument accepts either a style id string or a
|
|
1975
2039
|
:class:`docx.styles.style.ParagraphStyle` instance; previously
|
|
@@ -2007,6 +2071,10 @@ class _Cell:
|
|
|
2007
2071
|
f"{type(style).__name__} {style!r}",
|
|
2008
2072
|
)
|
|
2009
2073
|
|
|
2074
|
+
# Evict AFTER validation so a rejected call leaves the read-free
|
|
2075
|
+
# fast path intact (nothing mutated).
|
|
2076
|
+
self._table._fastpath_cell_text.pop((self._row, self._col), None)
|
|
2077
|
+
|
|
2010
2078
|
fragment: dict = {
|
|
2011
2079
|
"kind": "paragraph",
|
|
2012
2080
|
"paragraph": {
|
|
@@ -448,6 +448,27 @@ class Paragraph:
|
|
|
448
448
|
"""
|
|
449
449
|
from docx.text.run import Run
|
|
450
450
|
|
|
451
|
+
fast_text: object = getattr(self, "_fastpath_runs_text", None)
|
|
452
|
+
if isinstance(fast_text, str) and fast_text:
|
|
453
|
+
# Read-free fast path for a cell paragraph whose entire content
|
|
454
|
+
# the SDK wrote itself (see ``Table._fastpath_cell_text``): one
|
|
455
|
+
# synthetic run over the known text, no ``getNodeById`` walk.
|
|
456
|
+
# Format setters on it buffer a whole-cell ``FormatApply``
|
|
457
|
+
# against the positional ``tc:`` id; property READS may still
|
|
458
|
+
# trigger a live walk (the applier resolves ``tc:`` ids), so
|
|
459
|
+
# only the hot format-loop path is short-circuited.
|
|
460
|
+
self._text_len = len(fast_text)
|
|
461
|
+
fast_run = Run(
|
|
462
|
+
session=self._session,
|
|
463
|
+
block_id=self._node_id,
|
|
464
|
+
range_=(0, len(fast_text)),
|
|
465
|
+
inline={"type": "text", "text": fast_text},
|
|
466
|
+
in_cell=self._in_cell,
|
|
467
|
+
cell_ooxml_address=self._cell_ooxml_address,
|
|
468
|
+
)
|
|
469
|
+
fast_run._fastpath_text = fast_text # type: ignore[attr-defined]
|
|
470
|
+
return [fast_run]
|
|
471
|
+
|
|
451
472
|
info: object = self._node_info()
|
|
452
473
|
inlines: list[dict] = _walk_inlines(info)
|
|
453
474
|
runs: list["Run"] = []
|
|
@@ -1131,6 +1152,19 @@ class Paragraph:
|
|
|
1131
1152
|
f"or None; got {type(style).__name__} {style!r}",
|
|
1132
1153
|
)
|
|
1133
1154
|
|
|
1155
|
+
# Content beyond the single SDK-written run: evict this paragraph's
|
|
1156
|
+
# cell from the read-free fast path so the next ``paragraphs``/
|
|
1157
|
+
# ``runs`` read reflects real server state. After validation so a
|
|
1158
|
+
# rejected call leaves the fast path intact (nothing mutated).
|
|
1159
|
+
self._fastpath_runs_text = None # type: ignore[attr-defined]
|
|
1160
|
+
cell = self._cell
|
|
1161
|
+
table = getattr(cell, "_table", None) if cell is not None else None
|
|
1162
|
+
if table is not None:
|
|
1163
|
+
row = getattr(cell, "_row", None)
|
|
1164
|
+
col = getattr(cell, "_col", None)
|
|
1165
|
+
if row is not None and col is not None:
|
|
1166
|
+
getattr(table, "_fastpath_cell_text", {}).pop((row, col), None)
|
|
1167
|
+
|
|
1134
1168
|
# Use the cached paragraph text length when known — every
|
|
1135
1169
|
# avoided ``_node_text`` here drops one ``get_node_by_id`` HTTP
|
|
1136
1170
|
# round-trip. Linear ``doc.add_paragraph(); p.add_run(...);
|
|
@@ -336,6 +336,12 @@ class Run:
|
|
|
336
336
|
# ---- text ----
|
|
337
337
|
@property
|
|
338
338
|
def text(self) -> str:
|
|
339
|
+
# Fast-path runs (see ``Table._fastpath_cell_text``) carry their
|
|
340
|
+
# known text — the whole point is zero read round-trips. Mutations
|
|
341
|
+
# through this proxy keep the attr in sync or clear it.
|
|
342
|
+
fast: object = getattr(self, "_fastpath_text", None)
|
|
343
|
+
if isinstance(fast, str):
|
|
344
|
+
return fast
|
|
339
345
|
full: str = _block_text(self._session, self._block_id)
|
|
340
346
|
return full[self._range[0] : self._range[1]]
|
|
341
347
|
|
|
@@ -399,7 +405,10 @@ class Run:
|
|
|
399
405
|
f"{value!r}",
|
|
400
406
|
)
|
|
401
407
|
if self._check_cell_inner("text"):
|
|
408
|
+
# Warn-and-skip: nothing mutated, so the fast-path text cache
|
|
409
|
+
# (when present) still reflects reality — keep it.
|
|
402
410
|
return
|
|
411
|
+
self._fastpath_text = None # type: ignore[attr-defined]
|
|
403
412
|
old_len: int = self._range[1] - self._range[0]
|
|
404
413
|
run_sync(
|
|
405
414
|
self._session.doc.replace(
|
|
@@ -435,6 +444,7 @@ class Run:
|
|
|
435
444
|
in_cell=True,
|
|
436
445
|
cell_ooxml_address=getattr(self, "_cell_ooxml_address", None),
|
|
437
446
|
)
|
|
447
|
+
self._fastpath_text = None # type: ignore[attr-defined]
|
|
438
448
|
end: int = self._range[1]
|
|
439
449
|
cursor: dict = {"kind": "text", "blockId": self._block_id, "offset": end}
|
|
440
450
|
run_sync(
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "athena-python-docx"
|
|
7
|
-
version = "0.21.
|
|
7
|
+
version = "0.21.5"
|
|
8
8
|
description = "Drop-in replacement for python-docx that connects to Athena's DOCX Studio collaborative document stack"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -162,6 +162,31 @@ class FakeDocState:
|
|
|
162
162
|
return t
|
|
163
163
|
return None
|
|
164
164
|
|
|
165
|
+
def _resolve_cell_ref(self, node_id: object) -> tuple[_Table, int, int, _Block] | None:
|
|
166
|
+
if not isinstance(node_id, str) or not node_id:
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
parts = node_id.split(":")
|
|
170
|
+
if len(parts) == 4 and parts[0] == "tc":
|
|
171
|
+
try:
|
|
172
|
+
table_idx = int(parts[1])
|
|
173
|
+
row_idx = int(parts[2])
|
|
174
|
+
col_idx = int(parts[3])
|
|
175
|
+
except ValueError:
|
|
176
|
+
return None
|
|
177
|
+
if 0 <= table_idx < len(self.tables):
|
|
178
|
+
table = self.tables[table_idx]
|
|
179
|
+
if 0 <= row_idx < len(table.cells) and 0 <= col_idx < len(table.cells[row_idx]):
|
|
180
|
+
return (table, row_idx, col_idx, table.cells[row_idx][col_idx])
|
|
181
|
+
return None
|
|
182
|
+
|
|
183
|
+
for table in self.tables:
|
|
184
|
+
for row_idx, row in enumerate(table.cells):
|
|
185
|
+
for col_idx, cell in enumerate(row):
|
|
186
|
+
if node_id in {cell.node_id, f"cell_{table.node_id}_{row_idx}_{col_idx}"}:
|
|
187
|
+
return (table, row_idx, col_idx, cell)
|
|
188
|
+
return None
|
|
189
|
+
|
|
165
190
|
def respond(self, op: str, params: dict) -> Any:
|
|
166
191
|
# ---- header/footer part orchestration ----
|
|
167
192
|
# _HeaderFooter.add_paragraph / add_table mint a new part on
|
|
@@ -273,33 +298,29 @@ class FakeDocState:
|
|
|
273
298
|
"node": node,
|
|
274
299
|
"address": {"kind": "block", "nodeType": b.node_type, "nodeId": b.node_id},
|
|
275
300
|
}
|
|
276
|
-
# Table cell
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
"nodeType": "tableCell",
|
|
300
|
-
"nodeId": cell.node_id,
|
|
301
|
-
},
|
|
302
|
-
}
|
|
301
|
+
# Table cell. The real docx-editor surface addresses cells by
|
|
302
|
+
# positional ids (tc:<tableIndex>:<row>:<col>), while DOCX Studio
|
|
303
|
+
# also exposes table-specific ids from tables.getCells. Support
|
|
304
|
+
# both so the fake exercises the same no-query fast path as agents.
|
|
305
|
+
cell_ref = self._resolve_cell_ref(nid)
|
|
306
|
+
if cell_ref is not None:
|
|
307
|
+
_table, _row_idx, _col_idx, cell = cell_ref
|
|
308
|
+
inner = {
|
|
309
|
+
"type": "paragraph",
|
|
310
|
+
"attrs": {"nodeId": cell.node_id},
|
|
311
|
+
"content": [{"type": "text", "text": cell.text}] if cell.text else [],
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
"node": {
|
|
315
|
+
"nodeType": "tableCell",
|
|
316
|
+
"tableCell": {"content": [inner]},
|
|
317
|
+
},
|
|
318
|
+
"address": {
|
|
319
|
+
"kind": "block",
|
|
320
|
+
"nodeType": "tableCell",
|
|
321
|
+
"nodeId": cell.node_id,
|
|
322
|
+
},
|
|
323
|
+
}
|
|
303
324
|
return {"node": {}, "address": {}}
|
|
304
325
|
|
|
305
326
|
if op == "blocks.list":
|
|
@@ -515,16 +536,11 @@ class FakeDocState:
|
|
|
515
536
|
if isinstance(t, str):
|
|
516
537
|
appended += t
|
|
517
538
|
# Find cell and append to its first paragraph.
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
cell.node_id,
|
|
524
|
-
}
|
|
525
|
-
if cell_nid in expected_ids:
|
|
526
|
-
cell.text = cell.text + appended
|
|
527
|
-
cell.inlines = [{"run": {"text": cell.text}}] if cell.text else []
|
|
539
|
+
cell_ref = self._resolve_cell_ref(cell_nid)
|
|
540
|
+
if cell_ref is not None:
|
|
541
|
+
_table, _row_idx, _col_idx, cell = cell_ref
|
|
542
|
+
cell.text = cell.text + appended
|
|
543
|
+
cell.inlines = [{"run": {"text": cell.text}}] if cell.text else []
|
|
528
544
|
return {"ok": True}
|
|
529
545
|
|
|
530
546
|
# TableSetCell: address a cell by (tableNodeId, row, col) and
|
|
@@ -979,15 +995,13 @@ class FakeDocState:
|
|
|
979
995
|
if op == "tables.setCellProperties":
|
|
980
996
|
target = params.get("target") or {}
|
|
981
997
|
nid = target.get("nodeId") or params.get("nodeId")
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
if "preferredWidthPt" in params:
|
|
990
|
-
t.cell_widths[r_idx][c_idx] = params["preferredWidthPt"]
|
|
998
|
+
cell_ref = self._resolve_cell_ref(nid)
|
|
999
|
+
if cell_ref is not None:
|
|
1000
|
+
t, r_idx, c_idx, _cell = cell_ref
|
|
1001
|
+
if "verticalAlign" in params:
|
|
1002
|
+
t.cell_verticals[r_idx][c_idx] = params["verticalAlign"]
|
|
1003
|
+
if "preferredWidthPt" in params:
|
|
1004
|
+
t.cell_widths[r_idx][c_idx] = params["preferredWidthPt"]
|
|
991
1005
|
return {"ok": True}
|
|
992
1006
|
if op == "tables.setTableOptions":
|
|
993
1007
|
# Per DOCX Studio 1.8.1 setTableOptions only handles
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[
|
|
2
|
+
"sections.list",
|
|
3
|
+
"sections.setPageMargins",
|
|
4
|
+
"sections.setPageMargins",
|
|
5
|
+
"create.paragraph",
|
|
6
|
+
"styles.paragraph.setStyle",
|
|
7
|
+
"format.paragraph.setAlignment",
|
|
8
|
+
"create.paragraph",
|
|
9
|
+
"insert",
|
|
10
|
+
"format.apply",
|
|
11
|
+
"insert",
|
|
12
|
+
"insert",
|
|
13
|
+
"format.apply",
|
|
14
|
+
"format.apply",
|
|
15
|
+
"insert",
|
|
16
|
+
"format.paragraph.setSpacing",
|
|
17
|
+
"create.heading",
|
|
18
|
+
"create.table",
|
|
19
|
+
"tables.setStyle",
|
|
20
|
+
"tables.get",
|
|
21
|
+
"tables.setLayout",
|
|
22
|
+
"tables.get",
|
|
23
|
+
"tables.get",
|
|
24
|
+
"tables.setCell",
|
|
25
|
+
"tables.setCell",
|
|
26
|
+
"tables.setCell",
|
|
27
|
+
"tables.setCellProperties",
|
|
28
|
+
"format.apply",
|
|
29
|
+
"tables.setCellProperties",
|
|
30
|
+
"format.apply",
|
|
31
|
+
"tables.setCellProperties",
|
|
32
|
+
"format.apply",
|
|
33
|
+
"tables.setCell",
|
|
34
|
+
"tables.setCell",
|
|
35
|
+
"tables.setCell",
|
|
36
|
+
"tables.setCell",
|
|
37
|
+
"tables.setCell",
|
|
38
|
+
"tables.setCell"
|
|
39
|
+
]
|
|
@@ -29,16 +29,10 @@
|
|
|
29
29
|
"tables.get",
|
|
30
30
|
"tables.setLayout",
|
|
31
31
|
"tables.setCell",
|
|
32
|
-
"tables.get",
|
|
33
|
-
"tables.getCells",
|
|
34
32
|
"tables.setCellProperties",
|
|
35
33
|
"tables.setCell",
|
|
36
|
-
"tables.get",
|
|
37
|
-
"tables.getCells",
|
|
38
34
|
"tables.setCellProperties",
|
|
39
35
|
"tables.setCell",
|
|
40
|
-
"tables.get",
|
|
41
|
-
"tables.getCells",
|
|
42
36
|
"tables.setCellProperties",
|
|
43
37
|
"tables.setCell",
|
|
44
38
|
"tables.setCell",
|