athena-python-docx 0.15.2__tar.gz → 0.15.4__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.2 → athena_python_docx-0.15.4}/CLAUDE.md +7 -0
- athena_python_docx-0.15.4/DOCX_EXEC_LAB.md +189 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/PKG-INFO +10 -1
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/README.md +9 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/__init__.py +2 -3
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/_batching.py +1 -7
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/_buffer.py +64 -15
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/_http_doc.py +29 -16
- athena_python_docx-0.15.4/docx/_timeouts.py +32 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/client.py +18 -2
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/commands.py +25 -3
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/document.py +93 -11
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/errors.py +15 -6
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/pyproject.toml +1 -1
- athena_python_docx-0.15.4/scripts/docx_exec_lab.py +509 -0
- athena_python_docx-0.15.4/scripts/docx_exec_lab_examples/fast_table_fill.py +28 -0
- athena_python_docx-0.15.4/scripts/docx_exec_lab_examples/find_replace_literal.py +16 -0
- athena_python_docx-0.15.4/scripts/docx_exec_lab_server.py +1030 -0
- athena_python_docx-0.15.4/scripts/validate_find_replace_asset.py +237 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/52_iterate_everything.json +1 -1
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +1 -1
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/91_many_small_tables.json +1 -1
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_athena_extensions_contract.py +18 -2
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_athena_extensions_registry.py +4 -9
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_buffer.py +168 -1
- athena_python_docx-0.15.4/tests/test_docx_exec_lab.py +178 -0
- athena_python_docx-0.15.4/tests/test_docx_exec_lab_server.py +106 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_execution_scope.py +2 -1
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_find_replace_session_open.py +111 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_http_transport.py +86 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_iter_inner_content.py +36 -0
- athena_python_docx-0.15.4/tests/test_validate_find_replace_asset_script.py +109 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_wire_contract.py +12 -2
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/uv.lock +1 -1
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/.gitignore +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/_athena_extension.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/_execution.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/_http.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/_image_utils.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/_postproc.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/_ptc.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/_table_styles.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/api.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/bookmarks.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/charts.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/comments.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/enum/__init__.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/enum/section.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/enum/style.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/enum/table.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/enum/text.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/exceptions.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/fields.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/footnotes.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/math.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/opc/__init__.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/opc/coreprops.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/oxml/__init__.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/revisions.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/sdt.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/section.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/session.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/settings.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/shape.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/shared.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/styles/__init__.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/styles/style.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/styles/styles.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/table.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/text/__init__.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/text/font.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/text/hyperlink.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/text/pagebreak.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/text/paragraph.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/text/parfmt.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/text/run.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/text/tabstops.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/toc.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/docx/typing.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/scripts/publish.sh +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/scripts/release.sh +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/scripts/round_trip_smoke.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/scripts/smoke_test_block_not_found.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/__init__.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/conftest.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/METHODOLOGY.md +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/README.md +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/__init__.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/ab_probe_cases.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/ab_probe_runner.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/auto_gen_cases.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/binary_round_trip.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/cases.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/complex_cases.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/coverage_report.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/extract.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/extreme_cases.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/fake_session.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/firm_templates/README.md +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/firm_templates/__init__.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/firm_templates/_runner.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/firm_templates/extractor.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/local_runner.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/mega_cases.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshot.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/ours_spec.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/parity_crawl.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/parity_diff.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/real_world_cases.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/round_trip_tests.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/runner.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/stock_spec.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/fidelity/test_e2e_against_staging.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/parity/README.md +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/parity/__init__.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/parity/baseline_gaps.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/parity/compare.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/parity/intentional_deviations.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/parity/introspect.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/parity/reports/gap_report.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/parity/run_parity.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/parity/snapshots/athena_latest.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/parity/test_parity_gap.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_add_section_extract_items.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_batching_perf.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_block_not_found_error.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_cell_add_table_not_supported.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_cell_inner_format_stash.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_cell_inner_run_format_stash.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_cell_inner_run_guard.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_cell_text_plain_fastpath.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_cell_text_replace_semantics.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_collapsed_range_format.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_command_dataclasses.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_commands.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_comments.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_document_asset_id_property.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_document_clear.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_document_create.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_document_create_from_template.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_document_factory_validation.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_e2e_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_hyperlink_coalescing.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_insert_deferred.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_list_styles.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_merged_cell_secondary_slot.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_merged_cells.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_oxml_shim.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_paragraph_text_len_cache.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_parity_misc.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_parity_round2.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_phase_a_behavior.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_phase_b_headers_footers.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_phase_c_tables.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_postproc_cell_format_rewrite.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_postproc_ref_restore.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_pr19766_review_fixes.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_ptc.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_python_docx_api_parity.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_revisions.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_add_paragraph_style.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_add_picture.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_add_run.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_comments_add_comment.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_comments_get.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_document_audit.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_document_element.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_enum_section.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_font_audit.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_header_footer.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_hyperlink.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_inline_shape.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_misc.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_paragraph_strict.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_paragraph_style.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_parfmt.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_row_col_cell.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_run_add_break.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_run_bool_setters.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_run_style.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_run_style_strict.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_run_text.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_run_underline.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_section_audit.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_section_dimensions.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_section_onoff.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_settings.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_shared_audit.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_style.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_styles.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_table_audit.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_table_cell.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_table_dimensions.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_silent_stub_table_layout.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_smoke_integration.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_style_acceptance.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_style_font.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_style_setters_contract.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_table_set_cell_perf.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_table_style_id_resolution.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_temporarily_unavailable.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_xml_attr_guard.py +0 -0
- {athena_python_docx-0.15.2 → athena_python_docx-0.15.4}/tests/test_zod_wire_contract.py +0 -0
|
@@ -338,6 +338,13 @@ 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
|
+
|
|
341
348
|
- **`Document.iter_runs(*, in_=None, limit=None, offset=None)
|
|
342
349
|
-> list[dict]`** — server-side run enumeration for large-doc
|
|
343
350
|
inspection. Use this when an agent needs to sample or inspect runs
|
|
@@ -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.4
|
|
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.4"
|
|
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
|
]
|
|
@@ -11,17 +11,11 @@ import concurrent.futures
|
|
|
11
11
|
import threading
|
|
12
12
|
from typing import Any, Coroutine, TypeVar
|
|
13
13
|
|
|
14
|
+
from docx._timeouts import DEFAULT_OP_TIMEOUT_SECONDS
|
|
14
15
|
from docx.errors import OperationTimeoutError
|
|
15
16
|
|
|
16
17
|
T = TypeVar("T")
|
|
17
18
|
|
|
18
|
-
# Default per-op timeout. Individual Superdoc SDK calls should complete well
|
|
19
|
-
# under this — the value is a safety net so a WebSocket stall, Keryx downtime,
|
|
20
|
-
# or pathological large-document remote-read loop surfaces an actionable SDK
|
|
21
|
-
# error instead of wedging the calling thread until the outer Daytona
|
|
22
|
-
# INSTRUCTION_TIMEOUT_SECONDS kill.
|
|
23
|
-
DEFAULT_OP_TIMEOUT_SECONDS: float = 60.0
|
|
24
|
-
|
|
25
19
|
_loop: asyncio.AbstractEventLoop | None = None
|
|
26
20
|
_thread: threading.Thread | None = None
|
|
27
21
|
_lock = threading.Lock()
|
|
@@ -21,9 +21,9 @@ best-effort coalesce — if the loop thread races us we just flush twice
|
|
|
21
21
|
(or zero times; the next loop-thread call drains it).
|
|
22
22
|
|
|
23
23
|
`flush_all` is a process-wide hook used by the Daytona sandbox prelude to
|
|
24
|
-
make sure pending writes hit Keryx
|
|
25
|
-
|
|
26
|
-
alive.
|
|
24
|
+
make sure pending writes hit Keryx and server-side edit sessions are
|
|
25
|
+
checkpointed before the sandbox is suspended. It walks a weak-ref registry
|
|
26
|
+
so dead Document instances don't keep their buffers alive.
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
29
|
from __future__ import annotations
|
|
@@ -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
|
|
|
@@ -178,11 +180,12 @@ def _unregister(buffer: "CommandBuffer") -> None:
|
|
|
178
180
|
|
|
179
181
|
|
|
180
182
|
def flush_all(*, strict: bool = False) -> None:
|
|
181
|
-
"""Flush every live CommandBuffer in this process.
|
|
183
|
+
"""Flush and checkpoint every live CommandBuffer in this process.
|
|
182
184
|
|
|
183
185
|
Used by the Daytona sandbox prelude after user code returns, so
|
|
184
|
-
buffered mutations make it to Keryx
|
|
185
|
-
Safe to call when no
|
|
186
|
+
buffered mutations make it to Keryx and server-side edit sessions are
|
|
187
|
+
durably released before the sandbox is suspended. Safe to call when no
|
|
188
|
+
Buffers exist (no-op).
|
|
186
189
|
|
|
187
190
|
By default, failures are logged and swallowed for backwards
|
|
188
191
|
compatibility with existing local scripts. The executor calls
|
|
@@ -214,27 +217,48 @@ def flush_all(*, strict: bool = False) -> None:
|
|
|
214
217
|
sys.stderr.write(f"[docx-sdk] flush_all: {msg}\n")
|
|
215
218
|
continue
|
|
216
219
|
try:
|
|
217
|
-
buf.
|
|
220
|
+
buf.commit()
|
|
218
221
|
except Exception as e: # noqa: BLE001
|
|
219
|
-
msg = f"buffer {buf.asset_id} flush failed: {e}"
|
|
222
|
+
msg = f"buffer {buf.asset_id} flush/commit failed: {e}"
|
|
220
223
|
if strict:
|
|
221
224
|
failures.append(msg)
|
|
222
225
|
else:
|
|
223
226
|
sys.stderr.write(f"[docx-sdk] flush_all: {msg}\n")
|
|
224
227
|
|
|
225
228
|
if failures:
|
|
226
|
-
raise FlushAllError(failures)
|
|
229
|
+
raise FlushAllError(failures=failures)
|
|
227
230
|
|
|
228
231
|
|
|
229
232
|
# ---------------------------------------------------------------------------
|
|
230
233
|
# CommandBuffer
|
|
231
234
|
# ---------------------------------------------------------------------------
|
|
232
235
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
236
|
+
|
|
237
|
+
def _default_auto_flush_seconds() -> float:
|
|
238
|
+
"""Return the transparent-batching idle window.
|
|
239
|
+
|
|
240
|
+
A very small window streams single edits quickly, but it can also flush
|
|
241
|
+
generated table-population loops halfway through the loop. That is painful
|
|
242
|
+
on large legal documents because each partial table batch can take longer
|
|
243
|
+
than the normal client timeout. Keep the default human-fast while giving
|
|
244
|
+
agent code enough room to coalesce common loops; allow local override for
|
|
245
|
+
debugging.
|
|
246
|
+
"""
|
|
247
|
+
import os
|
|
248
|
+
|
|
249
|
+
raw = os.environ.get("ATHENA_DOCX_AUTO_FLUSH_SECONDS")
|
|
250
|
+
if raw is None:
|
|
251
|
+
return 1.0
|
|
252
|
+
try:
|
|
253
|
+
return max(float(raw), 0.0)
|
|
254
|
+
except ValueError:
|
|
255
|
+
return 1.0
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
# Idle window before a pure-mutation buffer auto-flushes. Agent-generated
|
|
259
|
+
# loops commonly perform dozens of property setters; a 1s window still feels
|
|
260
|
+
# live while avoiding mid-loop table flushes on large documents.
|
|
261
|
+
DEFAULT_AUTO_FLUSH_SECONDS: float = _default_auto_flush_seconds()
|
|
238
262
|
|
|
239
263
|
|
|
240
264
|
class CommandBuffer:
|
|
@@ -305,6 +329,12 @@ class CommandBuffer:
|
|
|
305
329
|
with self._lock:
|
|
306
330
|
return len(self._pending)
|
|
307
331
|
|
|
332
|
+
@property
|
|
333
|
+
def pending_timeout_seconds(self) -> float:
|
|
334
|
+
with self._lock:
|
|
335
|
+
pending = list(self._pending)
|
|
336
|
+
return timeout_for_commands(commands=pending)
|
|
337
|
+
|
|
308
338
|
@property
|
|
309
339
|
def is_stale_for_current_execution(self) -> bool:
|
|
310
340
|
return is_stale_execution(self._execution_id)
|
|
@@ -470,18 +500,35 @@ class CommandBuffer:
|
|
|
470
500
|
_apply_proxy_id_rewrites(results, proxy_id_refs)
|
|
471
501
|
return results
|
|
472
502
|
|
|
503
|
+
def commit(self) -> None:
|
|
504
|
+
"""Flush pending commands and release the server-side edit session.
|
|
505
|
+
|
|
506
|
+
The HTTP command response tells us docx-studio applied the batch to its
|
|
507
|
+
pooled SuperDoc handle. Releasing the handle closes the collaboration
|
|
508
|
+
session without discard on the server, which is the durability
|
|
509
|
+
checkpoint for asset-backed document edits.
|
|
510
|
+
"""
|
|
511
|
+
self._assert_current_execution()
|
|
512
|
+
self.flush()
|
|
513
|
+
release_asset = getattr(self._client, "release_asset", None)
|
|
514
|
+
if callable(release_asset):
|
|
515
|
+
release_asset(self._asset_id)
|
|
516
|
+
|
|
473
517
|
def close(self) -> None:
|
|
474
518
|
"""Flush and disable. Idempotent."""
|
|
475
519
|
if self._closed:
|
|
476
520
|
return
|
|
477
521
|
try:
|
|
478
522
|
if self.is_stale_for_current_execution:
|
|
523
|
+
pending: list[Command]
|
|
479
524
|
with self._lock:
|
|
480
525
|
self._cancel_timer_locked()
|
|
526
|
+
pending = self._pending
|
|
481
527
|
self._pending = []
|
|
482
528
|
self._proxy_id_refs = {}
|
|
529
|
+
_ptc_emit_end_batch(pending, is_error=True)
|
|
483
530
|
else:
|
|
484
|
-
self.
|
|
531
|
+
self.commit()
|
|
485
532
|
finally:
|
|
486
533
|
self._closed = True
|
|
487
534
|
_unregister(self)
|
|
@@ -553,6 +600,8 @@ class CommandBuffer:
|
|
|
553
600
|
self._timer = None
|
|
554
601
|
|
|
555
602
|
def _auto_flush(self) -> None:
|
|
603
|
+
if self.is_stale_for_current_execution:
|
|
604
|
+
return
|
|
556
605
|
try:
|
|
557
606
|
self.flush()
|
|
558
607
|
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:
|
|
@@ -704,6 +699,24 @@ class HttpClient:
|
|
|
704
699
|
except Exception: # noqa: BLE001
|
|
705
700
|
pass
|
|
706
701
|
|
|
702
|
+
def release_asset(self, asset_id: str) -> None:
|
|
703
|
+
"""Ask docx-studio to close its pooled SuperDoc session for ``asset_id``.
|
|
704
|
+
|
|
705
|
+
Command POSTs only prove that the server accepted and applied the
|
|
706
|
+
mutation batch to its live SuperDoc handle. The durable checkpoint for
|
|
707
|
+
collaborative sessions happens when that handle is closed without
|
|
708
|
+
discard, so ``Document.save()`` and the Daytona ``flush_all`` cleanup
|
|
709
|
+
call this endpoint after draining the command buffer.
|
|
710
|
+
"""
|
|
711
|
+
url: str = f"{self._base_url}/docs/{asset_id}/session/release"
|
|
712
|
+
_http_post_json(
|
|
713
|
+
session=self._session,
|
|
714
|
+
url=url,
|
|
715
|
+
api_key=self._api_key,
|
|
716
|
+
body={},
|
|
717
|
+
timeout=30.0,
|
|
718
|
+
)
|
|
719
|
+
|
|
707
720
|
def execute_batch(
|
|
708
721
|
self,
|
|
709
722
|
asset_id: str,
|
|
@@ -749,6 +762,7 @@ class HttpClient:
|
|
|
749
762
|
url=url,
|
|
750
763
|
api_key=self._api_key,
|
|
751
764
|
body=body,
|
|
765
|
+
timeout=timeout_for_commands(commands=commands),
|
|
752
766
|
)
|
|
753
767
|
applied = resp.get("applied")
|
|
754
768
|
# An empty applied list paired with an `error` field is the
|
|
@@ -881,7 +895,6 @@ _OP_TO_COMMAND: dict[str, type[Command]] = {
|
|
|
881
895
|
"track_changes.list": TrackChangesList,
|
|
882
896
|
"track_changes.get": TrackChangesGet,
|
|
883
897
|
"track_changes.decide": TrackChangesDecide,
|
|
884
|
-
|
|
885
898
|
# --- Athena-extension ops (v0.11.0) -----------------------------------
|
|
886
899
|
# Mirror the dotted-path style of the upstream-parity routes above:
|
|
887
900
|
# ``hyperlinks.create`` matches SuperDoc's ``doc.hyperlinks.create``.
|
|
@@ -928,6 +941,7 @@ _OP_TO_COMMAND: dict[str, type[Command]] = {
|
|
|
928
941
|
"sdt.delete": ContentControlsDelete,
|
|
929
942
|
"sections.set_columns": SetSectionColumns,
|
|
930
943
|
"find_replace": FindReplace,
|
|
944
|
+
"find_text": FindText,
|
|
931
945
|
"iter_runs": IterRuns,
|
|
932
946
|
"export_pdf": ExportPDF,
|
|
933
947
|
"numbering.get": NumberingGet,
|
|
@@ -968,6 +982,7 @@ _FIELD_RENAMES: dict[str, dict[str, str]] = {
|
|
|
968
982
|
# path-proxy snake-case transform leaves `in` as a Python keyword
|
|
969
983
|
# collision; rename to `in_` here so the dataclass accepts it.
|
|
970
984
|
"find_replace": {"in": "in_"},
|
|
985
|
+
"find_text": {"in": "in_"},
|
|
971
986
|
"iter_runs": {"in": "in_"},
|
|
972
987
|
"numbering.list": {"in": "in_"},
|
|
973
988
|
}
|
|
@@ -983,9 +998,7 @@ def _build_command(op: str, params: dict[str, Any]) -> Command:
|
|
|
983
998
|
)
|
|
984
999
|
|
|
985
1000
|
# 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
|
-
}
|
|
1001
|
+
snake_params: dict[str, Any] = {_camel_to_snake(k): v for k, v in params.items()}
|
|
989
1002
|
|
|
990
1003
|
# Apply per-op renames (e.g. find: type → node_type).
|
|
991
1004
|
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
|
|
|
@@ -215,8 +231,8 @@ class Session:
|
|
|
215
231
|
handle = self._doc_handle
|
|
216
232
|
buffer = getattr(handle, "buffer", None) if handle is not None else None
|
|
217
233
|
if buffer is not None:
|
|
218
|
-
buffer.
|
|
219
|
-
_log_info(f"Saved {self._asset_id} (buffer drained)")
|
|
234
|
+
buffer.commit()
|
|
235
|
+
_log_info(f"Saved {self._asset_id} (buffer drained and session released)")
|
|
220
236
|
|
|
221
237
|
async def close(self) -> None:
|
|
222
238
|
"""Close the session. Idempotent."""
|