athena-python-docx 0.15.1__tar.gz → 0.15.3__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.15.1 → athena_python_docx-0.15.3}/CLAUDE.md +13 -0
- athena_python_docx-0.15.3/DOCX_EXEC_LAB.md +189 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/PKG-INFO +10 -1
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/README.md +9 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/__init__.py +2 -3
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/_batching.py +11 -9
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/_buffer.py +40 -6
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/_http_doc.py +11 -16
- athena_python_docx-0.15.3/docx/_timeouts.py +32 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/client.py +16 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/commands.py +25 -3
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/document.py +133 -11
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/errors.py +31 -1
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/session.py +1 -1
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/pyproject.toml +1 -1
- athena_python_docx-0.15.3/scripts/docx_exec_lab.py +509 -0
- athena_python_docx-0.15.3/scripts/docx_exec_lab_examples/fast_table_fill.py +28 -0
- athena_python_docx-0.15.3/scripts/docx_exec_lab_examples/find_replace_literal.py +16 -0
- athena_python_docx-0.15.3/scripts/docx_exec_lab_server.py +1030 -0
- athena_python_docx-0.15.3/scripts/validate_find_replace_asset.py +237 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/52_iterate_everything.json +1 -1
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +1 -1
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/91_many_small_tables.json +1 -1
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_athena_extensions_contract.py +27 -2
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_athena_extensions_registry.py +5 -9
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_buffer.py +146 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_commands.py +18 -0
- athena_python_docx-0.15.3/tests/test_docx_exec_lab.py +178 -0
- athena_python_docx-0.15.3/tests/test_docx_exec_lab_server.py +106 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_execution_scope.py +2 -1
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_find_replace_session_open.py +128 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_http_transport.py +60 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_iter_inner_content.py +36 -0
- athena_python_docx-0.15.3/tests/test_validate_find_replace_asset_script.py +109 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_wire_contract.py +12 -2
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/uv.lock +1 -1
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/.gitignore +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/_athena_extension.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/_execution.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/_http.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/_image_utils.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/_postproc.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/_ptc.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/_table_styles.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/api.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/bookmarks.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/charts.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/comments.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/enum/__init__.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/enum/section.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/enum/style.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/enum/table.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/enum/text.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/exceptions.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/fields.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/footnotes.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/math.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/opc/__init__.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/opc/coreprops.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/oxml/__init__.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/revisions.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/sdt.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/section.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/settings.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/shape.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/shared.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/styles/__init__.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/styles/style.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/styles/styles.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/table.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/text/__init__.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/text/font.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/text/hyperlink.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/text/pagebreak.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/text/paragraph.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/text/parfmt.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/text/run.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/text/tabstops.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/toc.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/docx/typing.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/scripts/publish.sh +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/scripts/release.sh +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/scripts/round_trip_smoke.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/scripts/smoke_test_block_not_found.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/__init__.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/conftest.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/METHODOLOGY.md +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/README.md +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/__init__.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/ab_probe_cases.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/ab_probe_runner.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/auto_gen_cases.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/binary_round_trip.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/cases.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/complex_cases.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/coverage_report.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/extract.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/extreme_cases.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/fake_session.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/firm_templates/README.md +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/firm_templates/__init__.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/firm_templates/_runner.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/firm_templates/extractor.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/local_runner.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/mega_cases.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshot.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/ours_spec.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/parity_crawl.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/parity_diff.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/real_world_cases.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/round_trip_tests.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/runner.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/stock_spec.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/fidelity/test_e2e_against_staging.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/parity/README.md +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/parity/__init__.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/parity/baseline_gaps.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/parity/compare.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/parity/intentional_deviations.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/parity/introspect.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/parity/reports/gap_report.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/parity/run_parity.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/parity/snapshots/athena_latest.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/parity/test_parity_gap.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_add_section_extract_items.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_batching_perf.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_block_not_found_error.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_cell_add_table_not_supported.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_cell_inner_format_stash.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_cell_inner_run_format_stash.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_cell_inner_run_guard.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_cell_text_plain_fastpath.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_cell_text_replace_semantics.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_collapsed_range_format.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_command_dataclasses.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_comments.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_document_asset_id_property.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_document_clear.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_document_create.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_document_create_from_template.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_document_factory_validation.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_e2e_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_hyperlink_coalescing.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_insert_deferred.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_list_styles.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_merged_cell_secondary_slot.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_merged_cells.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_oxml_shim.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_paragraph_text_len_cache.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_parity_misc.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_parity_round2.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_phase_a_behavior.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_phase_b_headers_footers.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_phase_c_tables.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_postproc_cell_format_rewrite.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_postproc_ref_restore.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_pr19766_review_fixes.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_ptc.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_python_docx_api_parity.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_revisions.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_add_paragraph_style.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_add_picture.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_add_run.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_comments_add_comment.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_comments_get.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_document_audit.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_document_element.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_enum_section.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_font_audit.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_header_footer.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_hyperlink.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_inline_shape.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_misc.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_paragraph_strict.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_paragraph_style.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_parfmt.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_row_col_cell.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_run_add_break.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_run_bool_setters.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_run_style.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_run_style_strict.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_run_text.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_run_underline.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_section_audit.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_section_dimensions.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_section_onoff.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_settings.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_shared_audit.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_style.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_styles.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_table_audit.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_table_cell.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_table_dimensions.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_silent_stub_table_layout.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_smoke_integration.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_style_acceptance.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_style_font.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_style_setters_contract.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_table_set_cell_perf.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_table_style_id_resolution.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_temporarily_unavailable.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_xml_attr_guard.py +0 -0
- {athena_python_docx-0.15.1 → athena_python_docx-0.15.3}/tests/test_zod_wire_contract.py +0 -0
|
@@ -338,6 +338,19 @@ Issue numbers reference `python-openxml/python-docx`.
|
|
|
338
338
|
`"endnotes"` / `"comments"` / `"all"`. Routes through
|
|
339
339
|
`FindReplace`.
|
|
340
340
|
|
|
341
|
+
- **`Document.find_text(pattern, *, regex=False, match_case=False,
|
|
342
|
+
whole_word=False, in_=None, max_results=20, context_chars=80)
|
|
343
|
+
-> dict`** — fast bounded text search for large-doc inspection.
|
|
344
|
+
Returns `{count, matches, truncated}` with context snippets instead
|
|
345
|
+
of exporting the whole `.docx` or walking every paragraph/run through
|
|
346
|
+
live SDK reads. Routes through `FindText`.
|
|
347
|
+
|
|
348
|
+
- **`Document.iter_runs(*, in_=None, limit=None, offset=None)
|
|
349
|
+
-> list[dict]`** — server-side run enumeration for large-doc
|
|
350
|
+
inspection. Use this when an agent needs to sample or inspect runs
|
|
351
|
+
without issuing one ``getNodeById`` query per paragraph through
|
|
352
|
+
``doc.paragraphs`` + ``paragraph.runs``. Routes through `IterRuns`.
|
|
353
|
+
|
|
341
354
|
- **`Paragraph.list_id` / `Paragraph.list_level` /
|
|
342
355
|
`Paragraph.list_format` / `Paragraph.list_number`** — list-item
|
|
343
356
|
numbering reads. python-docx issue #471 (13 👍) demands access to
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# DocX Execution Lab
|
|
2
|
+
|
|
3
|
+
`scripts/docx_exec_lab.py` is a headless runner for quickly testing
|
|
4
|
+
`athena-python-docx` snippets against Athena SuperDocument assets. It
|
|
5
|
+
shortens the debug loop by running local SDK code directly against a
|
|
6
|
+
chosen DOCX Studio API, capturing stdout/stderr/errors/timings, and
|
|
7
|
+
optionally exporting the result to `.docx` for assertions.
|
|
8
|
+
|
|
9
|
+
Use this before waiting on a full Agent -> Daytona -> preview cycle when
|
|
10
|
+
debugging SDK behavior such as large-document replacement, table fills,
|
|
11
|
+
unsupported OOXML internals, or export fidelity.
|
|
12
|
+
|
|
13
|
+
## Inputs
|
|
14
|
+
|
|
15
|
+
The runner accepts code from `--code-file` or stdin. It injects:
|
|
16
|
+
|
|
17
|
+
- `ATHENA_DOCX_ASSET_ID` - the asset id passed with `--asset-id`
|
|
18
|
+
- `ATHENA_DOCX_BASE_URL` and `DOCX_STUDIO_BASE_URL` - when `--base-url` is set
|
|
19
|
+
- `DOCX_EXEC_LAB_OUTPUT_DIR` - output folder for the run
|
|
20
|
+
- `DOCX_EXEC_LAB_EXPORT_PATH` - export target when `--export-docx` is set
|
|
21
|
+
|
|
22
|
+
Authentication is read from the normal SDK environment, usually:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
export ATHENA_DOCX_API_KEY="<your-api-key>"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Do not commit API keys. Use shell env vars or your local secret manager.
|
|
29
|
+
|
|
30
|
+
## Run Against A PR Preview
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
cd docx-studio/python-sdk
|
|
34
|
+
|
|
35
|
+
export ATHENA_DOCX_API_KEY="<your-api-key>"
|
|
36
|
+
|
|
37
|
+
uv run python scripts/docx_exec_lab.py \
|
|
38
|
+
--base-url "https://docx-studio-pr21665-preview.previews.athenaintel.com" \
|
|
39
|
+
--asset-id "asset_..." \
|
|
40
|
+
--code-file scripts/docx_exec_lab_examples/find_replace_literal.py \
|
|
41
|
+
--timeout 30 \
|
|
42
|
+
--output-dir /tmp/docx-lab-find-replace \
|
|
43
|
+
--export-docx \
|
|
44
|
+
--assert-stdout-contains "Replacements made:" \
|
|
45
|
+
--assert-docx-text-contains "[Purchaser]"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The script writes structured JSON to `<output-dir>/result.json` and
|
|
49
|
+
prints the same JSON to stdout. The JSON includes success flags,
|
|
50
|
+
duration, stdout, stderr, exit code, error text, code path, asset id, and
|
|
51
|
+
artifact paths.
|
|
52
|
+
|
|
53
|
+
## Run Against Staging
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
cd docx-studio/python-sdk
|
|
57
|
+
|
|
58
|
+
export ATHENA_DOCX_API_KEY="<your-staging-api-key>"
|
|
59
|
+
export ATHENA_DOCX_BASE_URL="https://docx-studio.stg.athenaintel.com"
|
|
60
|
+
|
|
61
|
+
uv run python scripts/docx_exec_lab.py \
|
|
62
|
+
--asset-id "asset_..." \
|
|
63
|
+
--code-file scripts/docx_exec_lab_examples/fast_table_fill.py \
|
|
64
|
+
--timeout 30 \
|
|
65
|
+
--output-dir /tmp/docx-lab-table-fill \
|
|
66
|
+
--export-docx \
|
|
67
|
+
--assert-stdout-contains "Filled 4 table rows" \
|
|
68
|
+
--assert-docx-text-contains "LOW (NYSE)"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Run Against Local DOCX Studio
|
|
72
|
+
|
|
73
|
+
Start DOCX Studio locally, then point the runner at that base URL:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
cd docx-studio/python-sdk
|
|
77
|
+
|
|
78
|
+
export ATHENA_DOCX_API_KEY="<local-or-dev-api-key>"
|
|
79
|
+
|
|
80
|
+
uv run python scripts/docx_exec_lab.py \
|
|
81
|
+
--base-url "http://localhost:3001" \
|
|
82
|
+
--asset-id "asset_..." \
|
|
83
|
+
--code-file scripts/docx_exec_lab_examples/find_replace_literal.py \
|
|
84
|
+
--output-dir /tmp/docx-lab-local \
|
|
85
|
+
--export-docx
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Local UI With SuperDoc Embed
|
|
89
|
+
|
|
90
|
+
The lab also includes a local-only HTML UI that wraps the same headless
|
|
91
|
+
runner and iframes Olympus's existing SuperDoc embed route.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
cd docx-studio/python-sdk
|
|
95
|
+
|
|
96
|
+
export ATHENA_DOCX_API_KEY="<your-staging-api-key>"
|
|
97
|
+
|
|
98
|
+
uv run python scripts/docx_exec_lab_server.py --open
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Defaults:
|
|
102
|
+
|
|
103
|
+
- DOCX Studio API: `https://docx-studio.stg.athenaintel.com`
|
|
104
|
+
- Agora embed-token API: `https://agora-staging.athenaintel.com`
|
|
105
|
+
- Olympus iframe host: `https://staging-app.athenaintel.com`
|
|
106
|
+
|
|
107
|
+
The UI accepts an `asset_id`, Python code, and optional assertions. The
|
|
108
|
+
right pane can mint an editable embed token through staging Agora and
|
|
109
|
+
iframe the returned `https://staging-app.athenaintel.com/embed/{token}`
|
|
110
|
+
URL, so changes render in the real Olympus/SuperDoc viewer.
|
|
111
|
+
|
|
112
|
+
If the staging Agora URL differs, pass:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
uv run python scripts/docx_exec_lab_server.py \
|
|
116
|
+
--agora-base-url "https://..." \
|
|
117
|
+
--olympus-base-url "https://staging-app.athenaintel.com" \
|
|
118
|
+
--docx-base-url "https://docx-studio.stg.athenaintel.com"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The page also has an API key field. Values entered there are sent only
|
|
122
|
+
to the local server for that request and are not written to `result.json`.
|
|
123
|
+
|
|
124
|
+
## Use Stdin For One-Off Repros
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
cd docx-studio/python-sdk
|
|
128
|
+
|
|
129
|
+
printf '%s\n' \
|
|
130
|
+
'import os' \
|
|
131
|
+
'from docx import Document' \
|
|
132
|
+
'doc = Document(os.environ["ATHENA_DOCX_ASSET_ID"])' \
|
|
133
|
+
'print(doc.asset_id)' \
|
|
134
|
+
| uv run python scripts/docx_exec_lab.py \
|
|
135
|
+
--asset-id "asset_..." \
|
|
136
|
+
--base-url "https://docx-studio-pr21665-preview.previews.athenaintel.com" \
|
|
137
|
+
--assert-stdout-contains "asset_"
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Export Assertions
|
|
141
|
+
|
|
142
|
+
When `--export-docx` is set, the snippet must leave an open global
|
|
143
|
+
variable named `doc`. The lab calls:
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
doc.export_docx("<output-dir>/exported.docx")
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Then `--assert-docx-text-contains` extracts text from the exported Word
|
|
150
|
+
XML and checks for expected content. This catches "the command returned
|
|
151
|
+
success but the document did not change" failures.
|
|
152
|
+
|
|
153
|
+
If a snippet needs custom export behavior, pass `--export-path` without
|
|
154
|
+
`--export-docx` and write `os.environ["DOCX_EXEC_LAB_EXPORT_PATH"]`
|
|
155
|
+
inside the snippet. The lab records that file if it exists.
|
|
156
|
+
|
|
157
|
+
## Table Fill Guidance
|
|
158
|
+
|
|
159
|
+
Use the fast cell text path for bulk table values:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
table = doc.add_table(rows=len(rows), cols=2, style="Table Grid")
|
|
163
|
+
for row_index, (label, value) in enumerate(rows):
|
|
164
|
+
table.cell(row_index, 0).text = label
|
|
165
|
+
table.cell(row_index, 1).text = value
|
|
166
|
+
doc.save()
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Avoid reading `cell.paragraphs[0].runs[0]` inside the fill loop. That
|
|
170
|
+
interleaves writes with remote reads, flushes pending operations, and is
|
|
171
|
+
much slower on real assets.
|
|
172
|
+
|
|
173
|
+
## Opt-In Real Asset Smoke
|
|
174
|
+
|
|
175
|
+
The unit tests do not require network access. A real export smoke is
|
|
176
|
+
available but skipped unless explicitly enabled:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
cd docx-studio/python-sdk
|
|
180
|
+
|
|
181
|
+
export ATHENA_DOCX_LAB_RUN_INTEGRATION=1
|
|
182
|
+
export ATHENA_DOCX_LAB_ASSET_ID="asset_..."
|
|
183
|
+
export ATHENA_DOCX_API_KEY="<your-api-key>"
|
|
184
|
+
export ATHENA_DOCX_BASE_URL="https://docx-studio-pr21665-preview.previews.athenaintel.com"
|
|
185
|
+
|
|
186
|
+
uv run pytest tests/test_docx_exec_lab.py -m integration -q
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Use disposable or test assets for mutation snippets.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: athena-python-docx
|
|
3
|
-
Version: 0.15.
|
|
3
|
+
Version: 0.15.3
|
|
4
4
|
Summary: Drop-in replacement for python-docx that connects to Athena's Superdoc/Keryx collaborative document stack
|
|
5
5
|
Project-URL: Homepage, https://athenaintelligence.ai
|
|
6
6
|
Author-email: Athena Intelligence <engineering@athenaintelligence.ai>
|
|
@@ -67,6 +67,15 @@ uv pip install -e ".[dev]"
|
|
|
67
67
|
uv run pytest tests/ -x
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
## DocX Execution Lab
|
|
71
|
+
|
|
72
|
+
For fast local repros against real Athena document assets, use the
|
|
73
|
+
headless runner in `scripts/docx_exec_lab.py`. It executes an arbitrary
|
|
74
|
+
Python snippet with this checkout of `athena-python-docx`, captures
|
|
75
|
+
stdout/stderr/errors/timings, and can export the resulting `.docx` for
|
|
76
|
+
text assertions. `scripts/docx_exec_lab_server.py` adds a local UI with
|
|
77
|
+
an Olympus/SuperDoc embed preview pane. See `DOCX_EXEC_LAB.md`.
|
|
78
|
+
|
|
70
79
|
## Environment variables
|
|
71
80
|
|
|
72
81
|
Required when connecting to Keryx (set by Athena backend when executing in Daytona):
|
|
@@ -40,6 +40,15 @@ uv pip install -e ".[dev]"
|
|
|
40
40
|
uv run pytest tests/ -x
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
## DocX Execution Lab
|
|
44
|
+
|
|
45
|
+
For fast local repros against real Athena document assets, use the
|
|
46
|
+
headless runner in `scripts/docx_exec_lab.py`. It executes an arbitrary
|
|
47
|
+
Python snippet with this checkout of `athena-python-docx`, captures
|
|
48
|
+
stdout/stderr/errors/timings, and can export the resulting `.docx` for
|
|
49
|
+
text assertions. `scripts/docx_exec_lab_server.py` adds a local UI with
|
|
50
|
+
an Olympus/SuperDoc embed preview pane. See `DOCX_EXEC_LAB.md`.
|
|
51
|
+
|
|
43
52
|
## Environment variables
|
|
44
53
|
|
|
45
54
|
Required when connecting to Keryx (set by Athena backend when executing in Daytona):
|
|
@@ -6,11 +6,11 @@ See CLAUDE.md for the API parity contract.
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
-
__version__ = "0.15.
|
|
9
|
+
__version__ = "0.15.3"
|
|
10
10
|
|
|
11
|
-
from docx._execution import begin_execution
|
|
12
11
|
from docx.api import Document
|
|
13
12
|
from docx._buffer import flush_all
|
|
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
|
|
16
16
|
|
|
@@ -24,7 +24,6 @@ __all__ = [
|
|
|
24
24
|
"Twips",
|
|
25
25
|
"Length",
|
|
26
26
|
"RGBColor",
|
|
27
|
-
"begin_execution",
|
|
28
27
|
"flush_all",
|
|
29
28
|
"__version__",
|
|
30
29
|
]
|
|
@@ -7,16 +7,14 @@ This is the only place in the SDK that uses threading.
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
import asyncio
|
|
10
|
+
import concurrent.futures
|
|
10
11
|
import threading
|
|
11
12
|
from typing import Any, Coroutine, TypeVar
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
from docx._timeouts import DEFAULT_OP_TIMEOUT_SECONDS
|
|
15
|
+
from docx.errors import OperationTimeoutError
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
# under this — the value is a safety net so a WebSocket stall or Keryx
|
|
17
|
-
# downtime surfaces a TimeoutError instead of wedging the calling thread
|
|
18
|
-
# until the outer Daytona INSTRUCTION_TIMEOUT_SECONDS kill.
|
|
19
|
-
DEFAULT_OP_TIMEOUT_SECONDS: float = 60.0
|
|
17
|
+
T = TypeVar("T")
|
|
20
18
|
|
|
21
19
|
_loop: asyncio.AbstractEventLoop | None = None
|
|
22
20
|
_thread: threading.Thread | None = None
|
|
@@ -64,13 +62,17 @@ def run_sync(
|
|
|
64
62
|
(use sparingly — prefer the default so stalls surface early).
|
|
65
63
|
|
|
66
64
|
Raises:
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
OperationTimeoutError: if the coroutine does not complete within
|
|
66
|
+
`timeout` seconds.
|
|
69
67
|
The coroutine's exception, re-raised in the calling thread.
|
|
70
68
|
"""
|
|
71
69
|
loop = _ensure_loop()
|
|
72
70
|
future = asyncio.run_coroutine_threadsafe(coro, loop)
|
|
73
|
-
|
|
71
|
+
try:
|
|
72
|
+
return future.result(timeout=timeout)
|
|
73
|
+
except concurrent.futures.TimeoutError as exc:
|
|
74
|
+
future.cancel()
|
|
75
|
+
raise OperationTimeoutError(timeout_seconds=timeout) from exc
|
|
74
76
|
|
|
75
77
|
|
|
76
78
|
def shutdown() -> None:
|
|
@@ -39,6 +39,7 @@ from docx._execution import (
|
|
|
39
39
|
current_execution_id,
|
|
40
40
|
is_stale_execution,
|
|
41
41
|
)
|
|
42
|
+
from docx._timeouts import timeout_for_commands
|
|
42
43
|
from docx.commands import Command, is_response_bearing, must_flush_immediately
|
|
43
44
|
from docx.errors import DocxError, FlushAllError
|
|
44
45
|
|
|
@@ -113,6 +114,7 @@ def _extract_partial_results(exc: BaseException) -> list[Any]:
|
|
|
113
114
|
out.append(result)
|
|
114
115
|
return out
|
|
115
116
|
|
|
117
|
+
|
|
116
118
|
if TYPE_CHECKING:
|
|
117
119
|
from docx._http_doc import HttpClient
|
|
118
120
|
|
|
@@ -223,18 +225,39 @@ def flush_all(*, strict: bool = False) -> None:
|
|
|
223
225
|
sys.stderr.write(f"[docx-sdk] flush_all: {msg}\n")
|
|
224
226
|
|
|
225
227
|
if failures:
|
|
226
|
-
raise FlushAllError(failures)
|
|
228
|
+
raise FlushAllError(failures=failures)
|
|
227
229
|
|
|
228
230
|
|
|
229
231
|
# ---------------------------------------------------------------------------
|
|
230
232
|
# CommandBuffer
|
|
231
233
|
# ---------------------------------------------------------------------------
|
|
232
234
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
235
|
+
|
|
236
|
+
def _default_auto_flush_seconds() -> float:
|
|
237
|
+
"""Return the transparent-batching idle window.
|
|
238
|
+
|
|
239
|
+
A very small window streams single edits quickly, but it can also flush
|
|
240
|
+
generated table-population loops halfway through the loop. That is painful
|
|
241
|
+
on large legal documents because each partial table batch can take longer
|
|
242
|
+
than the normal client timeout. Keep the default human-fast while giving
|
|
243
|
+
agent code enough room to coalesce common loops; allow local override for
|
|
244
|
+
debugging.
|
|
245
|
+
"""
|
|
246
|
+
import os
|
|
247
|
+
|
|
248
|
+
raw = os.environ.get("ATHENA_DOCX_AUTO_FLUSH_SECONDS")
|
|
249
|
+
if raw is None:
|
|
250
|
+
return 1.0
|
|
251
|
+
try:
|
|
252
|
+
return max(float(raw), 0.0)
|
|
253
|
+
except ValueError:
|
|
254
|
+
return 1.0
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
# Idle window before a pure-mutation buffer auto-flushes. Agent-generated
|
|
258
|
+
# loops commonly perform dozens of property setters; a 1s window still feels
|
|
259
|
+
# live while avoiding mid-loop table flushes on large documents.
|
|
260
|
+
DEFAULT_AUTO_FLUSH_SECONDS: float = _default_auto_flush_seconds()
|
|
238
261
|
|
|
239
262
|
|
|
240
263
|
class CommandBuffer:
|
|
@@ -305,6 +328,12 @@ class CommandBuffer:
|
|
|
305
328
|
with self._lock:
|
|
306
329
|
return len(self._pending)
|
|
307
330
|
|
|
331
|
+
@property
|
|
332
|
+
def pending_timeout_seconds(self) -> float:
|
|
333
|
+
with self._lock:
|
|
334
|
+
pending = list(self._pending)
|
|
335
|
+
return timeout_for_commands(commands=pending)
|
|
336
|
+
|
|
308
337
|
@property
|
|
309
338
|
def is_stale_for_current_execution(self) -> bool:
|
|
310
339
|
return is_stale_execution(self._execution_id)
|
|
@@ -476,10 +505,13 @@ class CommandBuffer:
|
|
|
476
505
|
return
|
|
477
506
|
try:
|
|
478
507
|
if self.is_stale_for_current_execution:
|
|
508
|
+
pending: list[Command]
|
|
479
509
|
with self._lock:
|
|
480
510
|
self._cancel_timer_locked()
|
|
511
|
+
pending = self._pending
|
|
481
512
|
self._pending = []
|
|
482
513
|
self._proxy_id_refs = {}
|
|
514
|
+
_ptc_emit_end_batch(pending, is_error=True)
|
|
483
515
|
else:
|
|
484
516
|
self.flush()
|
|
485
517
|
finally:
|
|
@@ -553,6 +585,8 @@ class CommandBuffer:
|
|
|
553
585
|
self._timer = None
|
|
554
586
|
|
|
555
587
|
def _auto_flush(self) -> None:
|
|
588
|
+
if self.is_stale_for_current_execution:
|
|
589
|
+
return
|
|
556
590
|
try:
|
|
557
591
|
self.flush()
|
|
558
592
|
except Exception as e: # noqa: BLE001
|
|
@@ -38,6 +38,7 @@ from requests.adapters import HTTPAdapter
|
|
|
38
38
|
from urllib3.util.retry import Retry
|
|
39
39
|
|
|
40
40
|
from docx._buffer import CommandBuffer
|
|
41
|
+
from docx._timeouts import timeout_for_commands
|
|
41
42
|
from docx.commands import (
|
|
42
43
|
BlocksList,
|
|
43
44
|
BookmarksGet,
|
|
@@ -86,6 +87,7 @@ from docx.commands import (
|
|
|
86
87
|
FieldsRefresh,
|
|
87
88
|
Find,
|
|
88
89
|
FindReplace,
|
|
90
|
+
FindText,
|
|
89
91
|
FootnotesDelete,
|
|
90
92
|
FootnotesGet,
|
|
91
93
|
FootnotesList,
|
|
@@ -210,9 +212,7 @@ def _looks_like_block_not_found(err_obj: dict) -> bool:
|
|
|
210
212
|
return False
|
|
211
213
|
lower = msg.lower()
|
|
212
214
|
if "not found" in lower:
|
|
213
|
-
return 'block "' in lower or (
|
|
214
|
-
'block ' in lower and ' was not found' in lower
|
|
215
|
-
)
|
|
215
|
+
return 'block "' in lower or ("block " in lower and " was not found" in lower)
|
|
216
216
|
if "invalid_target" in lower or "invalid target" in lower:
|
|
217
217
|
# "Expected paragraph:<id> but found tableCell:<id>" — the new
|
|
218
218
|
# variant of the § 13 cell-inner-paragraph addressing gap.
|
|
@@ -323,7 +323,7 @@ _TABLE_QUERY_COMMANDS: frozenset[str] = frozenset(
|
|
|
323
323
|
|
|
324
324
|
_TABLE_CLIENT_ID_HINT: str = (
|
|
325
325
|
"\n\nHint: this looks like a stale client-side table UUID "
|
|
326
|
-
|
|
326
|
+
"(``t_xxxxxxxxxxxx``). Either an earlier batch in this execution "
|
|
327
327
|
"raised before the SDK could rewrite the Table proxy's id from the "
|
|
328
328
|
"client UUID to the real SuperDoc id, OR the table belongs to a "
|
|
329
329
|
"prior execution whose state has been discarded. Recover by calling "
|
|
@@ -521,8 +521,7 @@ def _http_post_json(
|
|
|
521
521
|
if is_remote_disconnect:
|
|
522
522
|
n_commands = (
|
|
523
523
|
len(body.get("commands", []))
|
|
524
|
-
if isinstance(body, dict)
|
|
525
|
-
and isinstance(body.get("commands"), list)
|
|
524
|
+
if isinstance(body, dict) and isinstance(body.get("commands"), list)
|
|
526
525
|
else 0
|
|
527
526
|
)
|
|
528
527
|
hint = (
|
|
@@ -608,13 +607,9 @@ def _http_post_json(
|
|
|
608
607
|
if _looks_like_block_not_found(err_obj):
|
|
609
608
|
cmd_type = err_obj.get("type")
|
|
610
609
|
paragraph_targeting = (
|
|
611
|
-
isinstance(cmd_type, str)
|
|
612
|
-
and cmd_type in _PARAGRAPH_TARGETING_COMMANDS
|
|
613
|
-
)
|
|
614
|
-
table_query = (
|
|
615
|
-
isinstance(cmd_type, str)
|
|
616
|
-
and cmd_type in _TABLE_QUERY_COMMANDS
|
|
610
|
+
isinstance(cmd_type, str) and cmd_type in _PARAGRAPH_TARGETING_COMMANDS
|
|
617
611
|
)
|
|
612
|
+
table_query = isinstance(cmd_type, str) and cmd_type in _TABLE_QUERY_COMMANDS
|
|
618
613
|
if paragraph_targeting:
|
|
619
614
|
hint = _CELL_PARAGRAPH_HINT
|
|
620
615
|
elif table_query:
|
|
@@ -749,6 +744,7 @@ class HttpClient:
|
|
|
749
744
|
url=url,
|
|
750
745
|
api_key=self._api_key,
|
|
751
746
|
body=body,
|
|
747
|
+
timeout=timeout_for_commands(commands=commands),
|
|
752
748
|
)
|
|
753
749
|
applied = resp.get("applied")
|
|
754
750
|
# An empty applied list paired with an `error` field is the
|
|
@@ -881,7 +877,6 @@ _OP_TO_COMMAND: dict[str, type[Command]] = {
|
|
|
881
877
|
"track_changes.list": TrackChangesList,
|
|
882
878
|
"track_changes.get": TrackChangesGet,
|
|
883
879
|
"track_changes.decide": TrackChangesDecide,
|
|
884
|
-
|
|
885
880
|
# --- Athena-extension ops (v0.11.0) -----------------------------------
|
|
886
881
|
# Mirror the dotted-path style of the upstream-parity routes above:
|
|
887
882
|
# ``hyperlinks.create`` matches SuperDoc's ``doc.hyperlinks.create``.
|
|
@@ -928,6 +923,7 @@ _OP_TO_COMMAND: dict[str, type[Command]] = {
|
|
|
928
923
|
"sdt.delete": ContentControlsDelete,
|
|
929
924
|
"sections.set_columns": SetSectionColumns,
|
|
930
925
|
"find_replace": FindReplace,
|
|
926
|
+
"find_text": FindText,
|
|
931
927
|
"iter_runs": IterRuns,
|
|
932
928
|
"export_pdf": ExportPDF,
|
|
933
929
|
"numbering.get": NumberingGet,
|
|
@@ -968,6 +964,7 @@ _FIELD_RENAMES: dict[str, dict[str, str]] = {
|
|
|
968
964
|
# path-proxy snake-case transform leaves `in` as a Python keyword
|
|
969
965
|
# collision; rename to `in_` here so the dataclass accepts it.
|
|
970
966
|
"find_replace": {"in": "in_"},
|
|
967
|
+
"find_text": {"in": "in_"},
|
|
971
968
|
"iter_runs": {"in": "in_"},
|
|
972
969
|
"numbering.list": {"in": "in_"},
|
|
973
970
|
}
|
|
@@ -983,9 +980,7 @@ def _build_command(op: str, params: dict[str, Any]) -> Command:
|
|
|
983
980
|
)
|
|
984
981
|
|
|
985
982
|
# Convert param keys camelCase → snake_case to match dataclass fields.
|
|
986
|
-
snake_params: dict[str, Any] = {
|
|
987
|
-
_camel_to_snake(k): v for k, v in params.items()
|
|
988
|
-
}
|
|
983
|
+
snake_params: dict[str, Any] = {_camel_to_snake(k): v for k, v in params.items()}
|
|
989
984
|
|
|
990
985
|
# Apply per-op renames (e.g. find: type → node_type).
|
|
991
986
|
renames = _FIELD_RENAMES.get(op)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Timeout budgets for SDK operations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
DEFAULT_OP_TIMEOUT_SECONDS: float = 60.0
|
|
8
|
+
|
|
9
|
+
# Large document-wide primitives can be intentionally expensive while still
|
|
10
|
+
# being the safest path for agents. Keep the normal SDK budget tight, but give
|
|
11
|
+
# these one-shot commands enough room to finish on dense legal documents.
|
|
12
|
+
LONG_RUNNING_COMMAND_TIMEOUT_SECONDS: float = 300.0
|
|
13
|
+
|
|
14
|
+
_LONG_RUNNING_COMMAND_TYPES: frozenset[str] = frozenset(
|
|
15
|
+
{
|
|
16
|
+
"FindReplace",
|
|
17
|
+
# Plain ``cell.text = value`` table fills are buffered as TableSetCell
|
|
18
|
+
# commands. On dense legal documents, SuperDoc can spend well over the
|
|
19
|
+
# ordinary 60s budget committing a table batch even when the Python code
|
|
20
|
+
# uses the fast no-read cell path.
|
|
21
|
+
"TableSetCell",
|
|
22
|
+
}
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def timeout_for_commands(*, commands: list[Any]) -> float:
|
|
27
|
+
"""Return the HTTP timeout budget for a batch of command objects."""
|
|
28
|
+
for command in commands:
|
|
29
|
+
command_type = getattr(command, "type", type(command).__name__)
|
|
30
|
+
if command_type in _LONG_RUNNING_COMMAND_TYPES:
|
|
31
|
+
return LONG_RUNNING_COMMAND_TIMEOUT_SECONDS
|
|
32
|
+
return DEFAULT_OP_TIMEOUT_SECONDS
|
|
@@ -23,6 +23,7 @@ from contextlib import asynccontextmanager
|
|
|
23
23
|
from typing import TYPE_CHECKING, Any
|
|
24
24
|
|
|
25
25
|
from docx._execution import assert_current_execution, current_execution_id
|
|
26
|
+
from docx._timeouts import DEFAULT_OP_TIMEOUT_SECONDS
|
|
26
27
|
from docx.errors import (
|
|
27
28
|
AuthenticationError,
|
|
28
29
|
DocumentClosedError,
|
|
@@ -99,6 +100,21 @@ class Session:
|
|
|
99
100
|
def is_open(self) -> bool:
|
|
100
101
|
return self._opened and not self._closed
|
|
101
102
|
|
|
103
|
+
@property
|
|
104
|
+
def pending_timeout_seconds(self) -> float:
|
|
105
|
+
handle = self._doc_handle
|
|
106
|
+
buffer = getattr(handle, "buffer", None) if handle is not None else None
|
|
107
|
+
timeout = (
|
|
108
|
+
getattr(buffer, "pending_timeout_seconds", DEFAULT_OP_TIMEOUT_SECONDS)
|
|
109
|
+
if buffer is not None
|
|
110
|
+
else DEFAULT_OP_TIMEOUT_SECONDS
|
|
111
|
+
)
|
|
112
|
+
if isinstance(timeout, bool):
|
|
113
|
+
return DEFAULT_OP_TIMEOUT_SECONDS
|
|
114
|
+
if isinstance(timeout, (int, float)):
|
|
115
|
+
return float(timeout)
|
|
116
|
+
return DEFAULT_OP_TIMEOUT_SECONDS
|
|
117
|
+
|
|
102
118
|
async def open(self) -> None:
|
|
103
119
|
"""Construct the HTTP client + buffered doc handle.
|
|
104
120
|
|
|
@@ -1510,6 +1510,27 @@ class FindReplace(Command):
|
|
|
1510
1510
|
max_replacements: int | None = None
|
|
1511
1511
|
|
|
1512
1512
|
|
|
1513
|
+
@dataclass
|
|
1514
|
+
class FindText(Command):
|
|
1515
|
+
"""Search document text and return bounded match context.
|
|
1516
|
+
|
|
1517
|
+
Athena extension beyond python-docx 1.x. This is the read-side
|
|
1518
|
+
companion to :class:`FindReplace`: it avoids exporting a full
|
|
1519
|
+
``.docx`` or walking every paragraph/run when callers only need a
|
|
1520
|
+
match count plus a few snippets. ``max_results`` limits how many
|
|
1521
|
+
contexts come back; ``count`` in the result remains the total
|
|
1522
|
+
number of matches found.
|
|
1523
|
+
"""
|
|
1524
|
+
|
|
1525
|
+
pattern: str = ""
|
|
1526
|
+
regex: bool | None = None
|
|
1527
|
+
match_case: bool | None = None
|
|
1528
|
+
whole_word: bool | None = None
|
|
1529
|
+
in_: str | dict[str, Any] | None = None
|
|
1530
|
+
max_results: int | None = None
|
|
1531
|
+
context_chars: int | None = None
|
|
1532
|
+
|
|
1533
|
+
|
|
1513
1534
|
@dataclass
|
|
1514
1535
|
class IterRuns(Command):
|
|
1515
1536
|
"""Stream runs across the document (with story locator + offset)
|
|
@@ -1637,6 +1658,7 @@ _QUERY_TYPES: frozenset[str] = frozenset(
|
|
|
1637
1658
|
"ContentControlsGet",
|
|
1638
1659
|
"NumberingGet",
|
|
1639
1660
|
"NumberingList",
|
|
1661
|
+
"FindText",
|
|
1640
1662
|
"IterRuns",
|
|
1641
1663
|
}
|
|
1642
1664
|
)
|
|
@@ -1741,9 +1763,7 @@ from docx._athena_extension import ( # noqa: E402 — local circular import saf
|
|
|
1741
1763
|
)
|
|
1742
1764
|
|
|
1743
1765
|
|
|
1744
|
-
def _mark_athena_extension_command(
|
|
1745
|
-
cls: type, issue: "str | None", description: str
|
|
1746
|
-
) -> None:
|
|
1766
|
+
def _mark_athena_extension_command(cls: type, issue: "str | None", description: str) -> None:
|
|
1747
1767
|
setattr(cls, ATHENA_EXTENSION_ATTR, True)
|
|
1748
1768
|
# ``issue`` is ``None`` for additions that don't have a 1:1 upstream
|
|
1749
1769
|
# python-docx issue — typically things that are *not* missing from
|
|
@@ -1799,6 +1819,7 @@ for _cls, _issue, _desc in [
|
|
|
1799
1819
|
(ContentControlsDelete, "python-docx#155", "SDT delete"),
|
|
1800
1820
|
(SetSectionColumns, "python-docx#167", "Section multi-column layout"),
|
|
1801
1821
|
(FindReplace, "python-docx#30", "Formatting-preserving find/replace"),
|
|
1822
|
+
(FindText, "python-docx#980", "Fast document text search"),
|
|
1802
1823
|
(IterRuns, "python-docx#980", "Run stream for run-isolation"),
|
|
1803
1824
|
(ExportPDF, "python-docx#113", "PDF export"),
|
|
1804
1825
|
(ExportDocx, None, "DOCX export — local file roundtrip"),
|
|
@@ -1931,6 +1952,7 @@ __all__ = [
|
|
|
1931
1952
|
"ContentControlsDelete",
|
|
1932
1953
|
"SetSectionColumns",
|
|
1933
1954
|
"FindReplace",
|
|
1955
|
+
"FindText",
|
|
1934
1956
|
"IterRuns",
|
|
1935
1957
|
"ExportPDF",
|
|
1936
1958
|
"ExportDocx",
|