athena-python-docx 0.14.0__tar.gz → 0.15.1__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.14.0 → athena_python_docx-0.15.1}/CLAUDE.md +23 -1
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/PKG-INFO +1 -1
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/__init__.py +3 -1
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/_buffer.py +59 -8
- athena_python_docx-0.15.1/docx/_execution.py +64 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/_http.py +19 -8
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/client.py +23 -4
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/document.py +83 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/errors.py +13 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/table.py +67 -10
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/pyproject.toml +1 -1
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_athena_extensions_registry.py +1 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_buffer.py +79 -2
- athena_python_docx-0.15.1/tests/test_cell_text_replace_semantics.py +116 -0
- athena_python_docx-0.15.1/tests/test_document_clear.py +132 -0
- athena_python_docx-0.15.1/tests/test_execution_scope.py +63 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/uv.lock +1 -1
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/.gitignore +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/README.md +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/_athena_extension.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/_batching.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/_http_doc.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/_image_utils.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/_postproc.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/_ptc.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/_table_styles.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/api.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/bookmarks.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/charts.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/commands.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/comments.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/enum/__init__.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/enum/section.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/enum/style.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/enum/table.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/enum/text.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/exceptions.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/fields.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/footnotes.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/math.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/opc/__init__.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/opc/coreprops.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/oxml/__init__.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/revisions.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/sdt.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/section.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/session.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/settings.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/shape.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/shared.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/styles/__init__.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/styles/style.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/styles/styles.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/text/__init__.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/text/font.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/text/hyperlink.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/text/pagebreak.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/text/paragraph.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/text/parfmt.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/text/run.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/text/tabstops.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/toc.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/docx/typing.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/scripts/publish.sh +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/scripts/release.sh +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/scripts/round_trip_smoke.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/scripts/smoke_test_block_not_found.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/__init__.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/conftest.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/METHODOLOGY.md +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/README.md +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/__init__.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/ab_probe_cases.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/ab_probe_runner.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/auto_gen_cases.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/binary_round_trip.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/cases.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/complex_cases.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/coverage_report.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/extract.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/extreme_cases.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/fake_session.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/firm_templates/README.md +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/firm_templates/__init__.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/firm_templates/_runner.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/firm_templates/extractor.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/local_runner.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/mega_cases.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshot.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/ours_spec.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/parity_crawl.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/parity_diff.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/real_world_cases.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/round_trip_tests.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/runner.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/stock_spec.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/fidelity/test_e2e_against_staging.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/parity/README.md +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/parity/__init__.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/parity/baseline_gaps.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/parity/compare.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/parity/intentional_deviations.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/parity/introspect.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/parity/reports/gap_report.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/parity/run_parity.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/parity/snapshots/athena_latest.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/parity/test_parity_gap.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_add_section_extract_items.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_athena_extensions_contract.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_batching_perf.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_block_not_found_error.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_cell_add_table_not_supported.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_cell_inner_format_stash.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_cell_inner_run_format_stash.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_cell_inner_run_guard.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_cell_text_plain_fastpath.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_collapsed_range_format.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_command_dataclasses.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_commands.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_comments.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_document_asset_id_property.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_document_create.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_document_create_from_template.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_document_factory_validation.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_e2e_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_find_replace_session_open.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_http_transport.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_hyperlink_coalescing.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_insert_deferred.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_iter_inner_content.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_list_styles.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_merged_cell_secondary_slot.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_merged_cells.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_oxml_shim.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_paragraph_text_len_cache.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_parity_misc.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_parity_round2.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_phase_a_behavior.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_phase_b_headers_footers.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_phase_c_tables.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_postproc_cell_format_rewrite.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_postproc_ref_restore.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_pr19766_review_fixes.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_ptc.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_python_docx_api_parity.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_revisions.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_add_paragraph_style.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_add_picture.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_add_run.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_comments_add_comment.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_comments_get.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_document_audit.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_document_element.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_enum_section.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_font_audit.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_header_footer.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_hyperlink.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_inline_shape.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_misc.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_paragraph_strict.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_paragraph_style.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_parfmt.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_row_col_cell.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_run_add_break.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_run_bool_setters.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_run_style.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_run_style_strict.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_run_text.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_run_underline.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_section_audit.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_section_dimensions.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_section_onoff.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_settings.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_shared_audit.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_style.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_styles.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_table_audit.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_table_cell.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_table_dimensions.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_silent_stub_table_layout.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_smoke_integration.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_style_acceptance.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_style_font.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_style_setters_contract.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_table_set_cell_perf.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_table_style_id_resolution.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_temporarily_unavailable.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_wire_contract.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_xml_attr_guard.py +0 -0
- {athena_python_docx-0.14.0 → athena_python_docx-0.15.1}/tests/test_zod_wire_contract.py +0 -0
|
@@ -349,6 +349,18 @@ Issue numbers reference `python-openxml/python-docx`.
|
|
|
349
349
|
wontfix upstream but the request keeps recurring. Routes through
|
|
350
350
|
`ExportPDF` (SuperDoc handles the actual conversion).
|
|
351
351
|
|
|
352
|
+
- **`Document.clear()`** (0.14.0+) — empty the document body so subsequent
|
|
353
|
+
writes start from a clean slate. python-docx has no equivalent (it
|
|
354
|
+
operates on local files where the caller can simply construct a new
|
|
355
|
+
``Document``). Iterates every top-level paragraph and table cell and
|
|
356
|
+
empties their text content. Block structure is preserved (SuperDoc has
|
|
357
|
+
no wire op to delete blocks today), so post-clear the doc holds N
|
|
358
|
+
empty paragraphs rather than collapsing to a single one — for a truly
|
|
359
|
+
empty body, call ``Document.create()`` to mint a fresh asset instead.
|
|
360
|
+
Closes the gap that prompted the 0.14 series' agent-side ``find_replace``
|
|
361
|
+
cleanup loops, which left horizontal-rule and scratch-content artifacts
|
|
362
|
+
in the document during failed-run recoveries.
|
|
363
|
+
|
|
352
364
|
- **`Document.export_docx(path=None, *, include_revisions=False)
|
|
353
365
|
-> bytes`** (0.11.4+) — export the document's current state as
|
|
354
366
|
``.docx`` bytes, optionally writing to ``path``. Closes the gap
|
|
@@ -602,6 +614,16 @@ This is a **thin HTTP client** that mimics the sync python-docx API.
|
|
|
602
614
|
the context-manager exit drain explicitly; `docx.flush_all()` is
|
|
603
615
|
the Daytona-prelude hook that flushes every live buffer in the
|
|
604
616
|
process.
|
|
617
|
+
- Execution scope: executor sandboxes may keep Python globals
|
|
618
|
+
between tool calls so imports, constants, and helper functions can be
|
|
619
|
+
reused. Live SDK objects are different. The executor calls
|
|
620
|
+
`docx.begin_execution(request_id)` at the start of each tool call;
|
|
621
|
+
`Session` and `CommandBuffer` stamp that id and raise
|
|
622
|
+
`StaleDocumentHandleError` if a later call tries to reuse old
|
|
623
|
+
`Document`/paragraph/run/table/cell handles. `docx.flush_all(strict=True)`
|
|
624
|
+
is the executor path and raises `FlushAllError` for failed flushes or
|
|
625
|
+
stale pending buffers; plain `docx.flush_all()` keeps the historical
|
|
626
|
+
log-and-swallow behavior for local scripts.
|
|
605
627
|
- **Insert deferral (0.8.0+):** `Insert` was removed from the
|
|
606
628
|
response-bearing set because every in-tree caller
|
|
607
629
|
(`Paragraph._insert_run_text_segments`, `Run.add_text`, `_Cell.text`)
|
|
@@ -650,7 +672,7 @@ This is a **thin HTTP client** that mimics the sync python-docx API.
|
|
|
650
672
|
follow-up — the wire format is typed end-to-end either way.
|
|
651
673
|
- **Programmatic Tool Calling (PTC):** every `CommandBuffer.call` and
|
|
652
674
|
`flush` emits begin/end events to agora via `docx/_ptc.py` when run
|
|
653
|
-
inside
|
|
675
|
+
inside an execution sandbox with `ATHENA_PTC_URL` set. Each method call
|
|
654
676
|
surfaces as a nested sub-tool-card under the parent
|
|
655
677
|
`run_python_code` tool. See
|
|
656
678
|
[`docs/PROGRAMMATIC_TOOL_CALLING_GUIDE.md`](../../docs/PROGRAMMATIC_TOOL_CALLING_GUIDE.md)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: athena-python-docx
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.15.1
|
|
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>
|
|
@@ -6,8 +6,9 @@ See CLAUDE.md for the API parity contract.
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
-
__version__ = "0.
|
|
9
|
+
__version__ = "0.15.1"
|
|
10
10
|
|
|
11
|
+
from docx._execution import begin_execution
|
|
11
12
|
from docx.api import Document
|
|
12
13
|
from docx._buffer import flush_all
|
|
13
14
|
# Re-exports python-docx ships at docx top-level for convenience.
|
|
@@ -23,6 +24,7 @@ __all__ = [
|
|
|
23
24
|
"Twips",
|
|
24
25
|
"Length",
|
|
25
26
|
"RGBColor",
|
|
27
|
+
"begin_execution",
|
|
26
28
|
"flush_all",
|
|
27
29
|
"__version__",
|
|
28
30
|
]
|
|
@@ -34,8 +34,13 @@ import weakref
|
|
|
34
34
|
from typing import TYPE_CHECKING, Any
|
|
35
35
|
|
|
36
36
|
from docx import _ptc
|
|
37
|
+
from docx._execution import (
|
|
38
|
+
assert_current_execution,
|
|
39
|
+
current_execution_id,
|
|
40
|
+
is_stale_execution,
|
|
41
|
+
)
|
|
37
42
|
from docx.commands import Command, is_response_bearing, must_flush_immediately
|
|
38
|
-
from docx.errors import DocxError
|
|
43
|
+
from docx.errors import DocxError, FlushAllError
|
|
39
44
|
|
|
40
45
|
|
|
41
46
|
def _apply_proxy_id_rewrites(
|
|
@@ -172,13 +177,17 @@ def _unregister(buffer: "CommandBuffer") -> None:
|
|
|
172
177
|
]
|
|
173
178
|
|
|
174
179
|
|
|
175
|
-
def flush_all() -> None:
|
|
180
|
+
def flush_all(*, strict: bool = False) -> None:
|
|
176
181
|
"""Flush every live CommandBuffer in this process.
|
|
177
182
|
|
|
178
183
|
Used by the Daytona sandbox prelude after user code returns, so
|
|
179
184
|
buffered mutations make it to Keryx before the sandbox is suspended.
|
|
180
|
-
Safe to call when no Buffers exist (no-op).
|
|
181
|
-
|
|
185
|
+
Safe to call when no Buffers exist (no-op).
|
|
186
|
+
|
|
187
|
+
By default, failures are logged and swallowed for backwards
|
|
188
|
+
compatibility with existing local scripts. The executor calls
|
|
189
|
+
``flush_all(strict=True)`` so failed or stale writes surface as real tool
|
|
190
|
+
failures instead of being hidden in stderr.
|
|
182
191
|
"""
|
|
183
192
|
with _registry_lock:
|
|
184
193
|
snapshot = list(_active_buffers)
|
|
@@ -186,16 +195,35 @@ def flush_all() -> None:
|
|
|
186
195
|
live = [ref for ref in snapshot if ref() is not None]
|
|
187
196
|
_active_buffers[:] = live
|
|
188
197
|
|
|
198
|
+
failures: list[str] = []
|
|
189
199
|
for ref in live:
|
|
190
200
|
buf = ref()
|
|
191
201
|
if buf is None:
|
|
192
202
|
continue
|
|
203
|
+
if buf.is_stale_for_current_execution:
|
|
204
|
+
pending_count = buf.pending_count
|
|
205
|
+
if pending_count > 0:
|
|
206
|
+
msg = (
|
|
207
|
+
f"buffer {buf.asset_id} belongs to a previous "
|
|
208
|
+
f"execute_word_document_code call and still has "
|
|
209
|
+
f"{pending_count} unflushed command(s)"
|
|
210
|
+
)
|
|
211
|
+
if strict:
|
|
212
|
+
failures.append(msg)
|
|
213
|
+
else:
|
|
214
|
+
sys.stderr.write(f"[docx-sdk] flush_all: {msg}\n")
|
|
215
|
+
continue
|
|
193
216
|
try:
|
|
194
217
|
buf.flush()
|
|
195
218
|
except Exception as e: # noqa: BLE001
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
219
|
+
msg = f"buffer {buf.asset_id} flush failed: {e}"
|
|
220
|
+
if strict:
|
|
221
|
+
failures.append(msg)
|
|
222
|
+
else:
|
|
223
|
+
sys.stderr.write(f"[docx-sdk] flush_all: {msg}\n")
|
|
224
|
+
|
|
225
|
+
if failures:
|
|
226
|
+
raise FlushAllError(failures)
|
|
199
227
|
|
|
200
228
|
|
|
201
229
|
# ---------------------------------------------------------------------------
|
|
@@ -240,6 +268,7 @@ class CommandBuffer:
|
|
|
240
268
|
self._timer: threading.Timer | None = None
|
|
241
269
|
self._auto_flush_seconds: float = auto_flush_seconds
|
|
242
270
|
self._closed: bool = False
|
|
271
|
+
self._execution_id: str | None = current_execution_id()
|
|
243
272
|
# Track-changes envelope state — propagated as request-level
|
|
244
273
|
# ``changeMode`` and ``user`` on every batch. ``Document``
|
|
245
274
|
# mutates these via :meth:`set_change_mode` / :meth:`set_user`,
|
|
@@ -263,6 +292,7 @@ class CommandBuffer:
|
|
|
263
292
|
the per-cmd result for ``real_node_id`` / ``real_entity_id``
|
|
264
293
|
echoes and applies setattr to every registered ref.
|
|
265
294
|
"""
|
|
295
|
+
self._assert_current_execution()
|
|
266
296
|
with self._lock:
|
|
267
297
|
self._proxy_id_refs.setdefault(client_id, []).append((proxy, attr))
|
|
268
298
|
|
|
@@ -275,6 +305,10 @@ class CommandBuffer:
|
|
|
275
305
|
with self._lock:
|
|
276
306
|
return len(self._pending)
|
|
277
307
|
|
|
308
|
+
@property
|
|
309
|
+
def is_stale_for_current_execution(self) -> bool:
|
|
310
|
+
return is_stale_execution(self._execution_id)
|
|
311
|
+
|
|
278
312
|
@property
|
|
279
313
|
def change_mode(self) -> str | None:
|
|
280
314
|
return self._change_mode
|
|
@@ -291,6 +325,7 @@ class CommandBuffer:
|
|
|
291
325
|
retro-actively re-tag earlier mutations. ``mode`` is one of
|
|
292
326
|
``"direct"``, ``"tracked"``, or ``None`` to clear.
|
|
293
327
|
"""
|
|
328
|
+
self._assert_current_execution()
|
|
294
329
|
if mode is not None and mode not in ("direct", "tracked"):
|
|
295
330
|
raise ValueError(f"change_mode must be 'direct', 'tracked', or None; got {mode!r}")
|
|
296
331
|
with self._lock:
|
|
@@ -311,6 +346,7 @@ class CommandBuffer:
|
|
|
311
346
|
a pooled session ignore the value, but the payload is still
|
|
312
347
|
included so a fresh session picks it up.
|
|
313
348
|
"""
|
|
349
|
+
self._assert_current_execution()
|
|
314
350
|
if name is None:
|
|
315
351
|
with self._lock:
|
|
316
352
|
self._user = None
|
|
@@ -339,6 +375,7 @@ class CommandBuffer:
|
|
|
339
375
|
``clientIdMap`` on flush and the buffer rewrites each registered
|
|
340
376
|
proxy's ``_node_id`` in-place.
|
|
341
377
|
"""
|
|
378
|
+
self._assert_current_execution()
|
|
342
379
|
if self._closed:
|
|
343
380
|
raise RuntimeError(
|
|
344
381
|
f"CommandBuffer for {self._asset_id} is closed",
|
|
@@ -397,6 +434,7 @@ class CommandBuffer:
|
|
|
397
434
|
proxies pick up real ids without the caller ever seeing the
|
|
398
435
|
UUID swap.
|
|
399
436
|
"""
|
|
437
|
+
self._assert_current_execution()
|
|
400
438
|
with self._lock:
|
|
401
439
|
self._cancel_timer_locked()
|
|
402
440
|
pending = self._pending
|
|
@@ -437,7 +475,13 @@ class CommandBuffer:
|
|
|
437
475
|
if self._closed:
|
|
438
476
|
return
|
|
439
477
|
try:
|
|
440
|
-
self.
|
|
478
|
+
if self.is_stale_for_current_execution:
|
|
479
|
+
with self._lock:
|
|
480
|
+
self._cancel_timer_locked()
|
|
481
|
+
self._pending = []
|
|
482
|
+
self._proxy_id_refs = {}
|
|
483
|
+
else:
|
|
484
|
+
self.flush()
|
|
441
485
|
finally:
|
|
442
486
|
self._closed = True
|
|
443
487
|
_unregister(self)
|
|
@@ -453,6 +497,7 @@ class CommandBuffer:
|
|
|
453
497
|
still get their real ids written back via the same flush-time
|
|
454
498
|
rewrite that :meth:`flush` does.
|
|
455
499
|
"""
|
|
500
|
+
self._assert_current_execution()
|
|
456
501
|
with self._lock:
|
|
457
502
|
self._cancel_timer_locked()
|
|
458
503
|
pending = self._pending
|
|
@@ -515,5 +560,11 @@ class CommandBuffer:
|
|
|
515
560
|
f"[docx-sdk] auto-flush failed for {self._asset_id}: {e}\n",
|
|
516
561
|
)
|
|
517
562
|
|
|
563
|
+
def _assert_current_execution(self) -> None:
|
|
564
|
+
assert_current_execution(
|
|
565
|
+
self._execution_id,
|
|
566
|
+
object_name=f"CommandBuffer for {self._asset_id}",
|
|
567
|
+
)
|
|
568
|
+
|
|
518
569
|
|
|
519
570
|
__all__ = ["CommandBuffer", "flush_all", "DEFAULT_AUTO_FLUSH_SECONDS"]
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Execution-scope helpers for long-lived sandbox processes.
|
|
2
|
+
|
|
3
|
+
Document execution sandboxes intentionally keep Python globals alive between
|
|
4
|
+
tool calls so agents can reuse helper functions and imports. Live SDK handles
|
|
5
|
+
are different: a ``Document``/``Paragraph``/``Run`` proxy captures a transport
|
|
6
|
+
session and a snapshot of node ids from the moment it was opened. Reusing that
|
|
7
|
+
proxy in a later tool call can overwrite manual user edits made between calls.
|
|
8
|
+
|
|
9
|
+
The executor calls :func:`begin_execution` once per tool call. Sessions and
|
|
10
|
+
buffers created after that stamp themselves with the active execution id; any
|
|
11
|
+
later operation under a different id fails before issuing HTTP.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from docx.errors import StaleDocumentHandleError
|
|
17
|
+
|
|
18
|
+
_current_execution_id: str | None = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def begin_execution(execution_id: str | None) -> None:
|
|
22
|
+
"""Mark the start of a logical executor call.
|
|
23
|
+
|
|
24
|
+
Passing ``None`` disables stale-handle checks, which keeps local scripts
|
|
25
|
+
and existing tests behaving like normal in-memory Python programs.
|
|
26
|
+
"""
|
|
27
|
+
global _current_execution_id
|
|
28
|
+
_current_execution_id = str(execution_id) if execution_id is not None else None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def current_execution_id() -> str | None:
|
|
32
|
+
"""Return the active executor id, if one has been declared."""
|
|
33
|
+
return _current_execution_id
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def is_stale_execution(owner_execution_id: str | None) -> bool:
|
|
37
|
+
"""Whether an object stamped with ``owner_execution_id`` is stale."""
|
|
38
|
+
current = current_execution_id()
|
|
39
|
+
return current is not None and owner_execution_id != current
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def assert_current_execution(
|
|
43
|
+
owner_execution_id: str | None,
|
|
44
|
+
*,
|
|
45
|
+
object_name: str,
|
|
46
|
+
) -> None:
|
|
47
|
+
"""Raise if an SDK object belongs to a prior executor call."""
|
|
48
|
+
if not is_stale_execution(owner_execution_id):
|
|
49
|
+
return
|
|
50
|
+
raise StaleDocumentHandleError(
|
|
51
|
+
f"{object_name} belongs to a previous execute_word_document_code call. "
|
|
52
|
+
"Reopen the document inside the current tool call with "
|
|
53
|
+
"Document('asset_xxx') and pass that fresh Document into reusable "
|
|
54
|
+
"helper functions. Imports, constants, and function definitions can "
|
|
55
|
+
"still persist across calls."
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
__all__ = [
|
|
60
|
+
"assert_current_execution",
|
|
61
|
+
"begin_execution",
|
|
62
|
+
"current_execution_id",
|
|
63
|
+
"is_stale_execution",
|
|
64
|
+
]
|
|
@@ -100,17 +100,24 @@ def create_empty_document(
|
|
|
100
100
|
DocxError: any other 4xx/5xx from the server.
|
|
101
101
|
"""
|
|
102
102
|
resolved_base: str | None = base_url or os.environ.get(_BASE_URL_ENV)
|
|
103
|
-
|
|
103
|
+
# api_key falls back to ATHENA_API_KEY (the canonical Athena user API
|
|
104
|
+
# key) so code running in sandboxes that only inject ATHENA_API_KEY
|
|
105
|
+
# still authenticates without an extra env var.
|
|
106
|
+
resolved_key: str | None = (
|
|
107
|
+
api_key or os.environ.get(_API_KEY_ENV) or os.environ.get("ATHENA_API_KEY")
|
|
108
|
+
)
|
|
104
109
|
|
|
105
110
|
if not resolved_base:
|
|
106
111
|
raise SessionError(
|
|
107
112
|
f"Missing base_url and {_BASE_URL_ENV} env var. "
|
|
108
|
-
"Pass base_url= to Document.create() or set the env var
|
|
113
|
+
"Pass base_url= to Document.create() or set the env var "
|
|
114
|
+
'(e.g. ATHENA_DOCX_BASE_URL="https://docx-studio.prd.athenaintel.com").',
|
|
109
115
|
)
|
|
110
116
|
if not resolved_key:
|
|
111
117
|
raise AuthenticationError(
|
|
112
118
|
f"Missing api_key and {_API_KEY_ENV} env var. "
|
|
113
|
-
"Pass api_key= to Document.create() or set the env var
|
|
119
|
+
"Pass api_key= to Document.create() or set the env var "
|
|
120
|
+
"(or set ATHENA_API_KEY as a fallback).",
|
|
114
121
|
)
|
|
115
122
|
|
|
116
123
|
url: str = resolved_base.rstrip("/") + "/docs/empty"
|
|
@@ -256,7 +263,12 @@ def upload_document(
|
|
|
256
263
|
import uuid
|
|
257
264
|
|
|
258
265
|
resolved_docx_base: str | None = docx_base_url or os.environ.get(_BASE_URL_ENV)
|
|
259
|
-
|
|
266
|
+
# api_key falls back to ATHENA_API_KEY (the canonical Athena user API
|
|
267
|
+
# key) so code running in sandboxes that only inject ATHENA_API_KEY
|
|
268
|
+
# still authenticates without an extra env var.
|
|
269
|
+
resolved_key: str | None = (
|
|
270
|
+
api_key or os.environ.get(_API_KEY_ENV) or os.environ.get("ATHENA_API_KEY")
|
|
271
|
+
)
|
|
260
272
|
resolved_agora_base: str | None = (
|
|
261
273
|
agora_base_url
|
|
262
274
|
or os.environ.get(_AGORA_BASE_URL_ENV)
|
|
@@ -273,7 +285,8 @@ def upload_document(
|
|
|
273
285
|
if not resolved_key:
|
|
274
286
|
raise AuthenticationError(
|
|
275
287
|
f"Missing api_key and {_API_KEY_ENV} env var. "
|
|
276
|
-
"Pass api_key= to Document.create() or set the env var
|
|
288
|
+
"Pass api_key= to Document.create() or set the env var "
|
|
289
|
+
"(or set ATHENA_API_KEY as a fallback).",
|
|
277
290
|
)
|
|
278
291
|
|
|
279
292
|
if not os.path.exists(docx_path):
|
|
@@ -297,9 +310,7 @@ def upload_document(
|
|
|
297
310
|
qs["parent_folder_id"] = parent_folder_id
|
|
298
311
|
|
|
299
312
|
url: str = (
|
|
300
|
-
resolved_agora_base.rstrip("/")
|
|
301
|
-
+ "/api/super-docs/create-from-upload?"
|
|
302
|
-
+ urlencode(qs)
|
|
313
|
+
resolved_agora_base.rstrip("/") + "/api/super-docs/create-from-upload?" + urlencode(qs)
|
|
303
314
|
)
|
|
304
315
|
|
|
305
316
|
# Build a minimal multipart/form-data body without pulling in
|
|
@@ -22,6 +22,7 @@ import os
|
|
|
22
22
|
from contextlib import asynccontextmanager
|
|
23
23
|
from typing import TYPE_CHECKING, Any
|
|
24
24
|
|
|
25
|
+
from docx._execution import assert_current_execution, current_execution_id
|
|
25
26
|
from docx.errors import (
|
|
26
27
|
AuthenticationError,
|
|
27
28
|
DocumentClosedError,
|
|
@@ -80,6 +81,7 @@ class Session:
|
|
|
80
81
|
self._doc_handle: Any | None = None
|
|
81
82
|
self._opened: bool = False
|
|
82
83
|
self._closed: bool = False
|
|
84
|
+
self._execution_id: str | None = current_execution_id()
|
|
83
85
|
# Collector for post-export OOXML rewrites (cell-inner format
|
|
84
86
|
# ops + REF-field restorations + future Tier-B fidelity fixes).
|
|
85
87
|
# Lives here so every proxy that reaches the session can stash;
|
|
@@ -109,6 +111,7 @@ class Session:
|
|
|
109
111
|
AuthenticationError: missing or invalid api_key.
|
|
110
112
|
SessionError: missing base_url or downstream open failure.
|
|
111
113
|
"""
|
|
114
|
+
self._assert_current_execution()
|
|
112
115
|
if self._closed:
|
|
113
116
|
raise DocumentClosedError(
|
|
114
117
|
f"Session for asset {self._asset_id} was already closed.",
|
|
@@ -121,18 +124,25 @@ class Session:
|
|
|
121
124
|
base_url: str | None = self._http_base_url or os.environ.get(
|
|
122
125
|
"ATHENA_DOCX_BASE_URL",
|
|
123
126
|
)
|
|
124
|
-
api_key
|
|
125
|
-
|
|
127
|
+
# api_key falls back to ATHENA_API_KEY (the canonical Athena user API
|
|
128
|
+
# key) so code running in sandboxes that only inject ATHENA_API_KEY
|
|
129
|
+
# still authenticates without an extra env var.
|
|
130
|
+
api_key: str | None = (
|
|
131
|
+
self._http_api_key
|
|
132
|
+
or os.environ.get("ATHENA_DOCX_API_KEY")
|
|
133
|
+
or os.environ.get("ATHENA_API_KEY")
|
|
126
134
|
)
|
|
127
135
|
if not base_url:
|
|
128
136
|
raise SessionError(
|
|
129
137
|
"Missing base_url. Pass base_url= to Document(...) or set "
|
|
130
|
-
"ATHENA_DOCX_BASE_URL."
|
|
138
|
+
"the ATHENA_DOCX_BASE_URL environment variable (e.g. "
|
|
139
|
+
'"https://docx-studio.prd.athenaintel.com").',
|
|
131
140
|
)
|
|
132
141
|
if not api_key:
|
|
133
142
|
raise AuthenticationError(
|
|
134
143
|
"Missing api_key. Pass api_key= to Document(...) or set "
|
|
135
|
-
"ATHENA_DOCX_API_KEY
|
|
144
|
+
"ATHENA_DOCX_API_KEY (or ATHENA_API_KEY as a fallback) in "
|
|
145
|
+
"the environment.",
|
|
136
146
|
)
|
|
137
147
|
client = HttpClient(base_url=base_url, api_key=api_key)
|
|
138
148
|
self._client = client
|
|
@@ -143,6 +153,7 @@ class Session:
|
|
|
143
153
|
@property
|
|
144
154
|
def doc(self) -> Any:
|
|
145
155
|
"""Return the opened doc handle (the path-proxy entry point)."""
|
|
156
|
+
self._assert_current_execution()
|
|
146
157
|
if not self._opened:
|
|
147
158
|
raise SessionError(
|
|
148
159
|
"Session not yet opened; call await session.open() first "
|
|
@@ -170,6 +181,7 @@ class Session:
|
|
|
170
181
|
call shape that the rest of the SDK uses; in practice every
|
|
171
182
|
SDK call site wraps this in :func:`docx._batching.run_sync`.
|
|
172
183
|
"""
|
|
184
|
+
self._assert_current_execution()
|
|
173
185
|
if not self._opened:
|
|
174
186
|
raise SessionError(
|
|
175
187
|
"Session not yet opened; call await session.open() first "
|
|
@@ -194,6 +206,7 @@ class Session:
|
|
|
194
206
|
``in_place`` is accepted for back-compat with the legacy direct
|
|
195
207
|
transport; writes are always against the asset's Y.Doc.
|
|
196
208
|
"""
|
|
209
|
+
self._assert_current_execution()
|
|
197
210
|
if not self._opened:
|
|
198
211
|
raise SessionError("Cannot save a session that was never opened.")
|
|
199
212
|
if self._closed:
|
|
@@ -234,6 +247,12 @@ class Session:
|
|
|
234
247
|
self._closed = True
|
|
235
248
|
_log_info(f"Closed {self._asset_id}")
|
|
236
249
|
|
|
250
|
+
def _assert_current_execution(self) -> None:
|
|
251
|
+
assert_current_execution(
|
|
252
|
+
self._execution_id,
|
|
253
|
+
object_name=f"Document session for {self._asset_id}",
|
|
254
|
+
)
|
|
255
|
+
|
|
237
256
|
|
|
238
257
|
@asynccontextmanager
|
|
239
258
|
async def open_session(
|
|
@@ -1246,6 +1246,89 @@ class Document:
|
|
|
1246
1246
|
# \r\n → \n\n (preserves paragraph-break semantics)
|
|
1247
1247
|
return text.replace("\r\n", "\n\n").replace("\r", "\n")
|
|
1248
1248
|
|
|
1249
|
+
@athena_extension(
|
|
1250
|
+
since="0.14.0",
|
|
1251
|
+
description="Document.clear — empty paragraphs + cells so writes start from a clean slate.",
|
|
1252
|
+
)
|
|
1253
|
+
def clear(self) -> None:
|
|
1254
|
+
"""Clear the document body so subsequent writes start from a clean slate.
|
|
1255
|
+
|
|
1256
|
+
Athena extension — python-docx has no equivalent. Empties every
|
|
1257
|
+
top-level paragraph's text content and every table cell's text
|
|
1258
|
+
content. The block structure (paragraphs and tables themselves)
|
|
1259
|
+
is preserved because SuperDoc does not yet expose a wire op to
|
|
1260
|
+
delete blocks; an agent recovering from a failed run can call
|
|
1261
|
+
``doc.clear()`` then ``doc.add_paragraph(...)`` etc. without
|
|
1262
|
+
worrying about stale content concatenating onto its new writes.
|
|
1263
|
+
|
|
1264
|
+
Notes / caveats:
|
|
1265
|
+
- The post-clear document holds N empty paragraphs (one per
|
|
1266
|
+
previously-existing block-level paragraph) plus any empty
|
|
1267
|
+
tables. For most agent flows this is invisible because the
|
|
1268
|
+
first ``add_paragraph`` / ``add_heading`` extends the doc
|
|
1269
|
+
rather than overwriting block positions.
|
|
1270
|
+
- To get a TRULY empty body (single empty paragraph, no tables)
|
|
1271
|
+
create a fresh asset via ``Document.create()`` instead.
|
|
1272
|
+
- This method is best-effort: per-paragraph clear errors are
|
|
1273
|
+
swallowed so a partial clear doesn't take down the whole call.
|
|
1274
|
+
"""
|
|
1275
|
+
from docx.text.paragraph import Paragraph as _Paragraph
|
|
1276
|
+
|
|
1277
|
+
self._ensure_open()
|
|
1278
|
+
|
|
1279
|
+
# Clear the list-chain state — the next add_paragraph after
|
|
1280
|
+
# clear() should start a new list, not attach to whatever the
|
|
1281
|
+
# previous run was doing.
|
|
1282
|
+
self._last_list_item_id = None
|
|
1283
|
+
self._last_list_kind = None
|
|
1284
|
+
|
|
1285
|
+
for paragraph in self.paragraphs:
|
|
1286
|
+
try:
|
|
1287
|
+
if not isinstance(paragraph, _Paragraph):
|
|
1288
|
+
continue
|
|
1289
|
+
if paragraph.text:
|
|
1290
|
+
paragraph.clear()
|
|
1291
|
+
except Exception as err:
|
|
1292
|
+
_log_warn(f"Document.clear: skipped a paragraph: {err!r}")
|
|
1293
|
+
|
|
1294
|
+
# Cell clearing must NOT go through ``cell.text = ""``: ``self.tables``
|
|
1295
|
+
# materializes fresh ephemeral ``Table`` proxies whose
|
|
1296
|
+
# ``_written_cells`` set is empty, so the setter would take the
|
|
1297
|
+
# fast (append) path — ``tables.set_cell insideEnd`` with an empty
|
|
1298
|
+
# paragraph fragment — which leaves prior cell content untouched.
|
|
1299
|
+
# Instead, drive ``doc.replace`` directly against each cell's
|
|
1300
|
+
# inner paragraphs (mirroring the clear-then-insert branch in
|
|
1301
|
+
# ``_Cell.text``) so the clear semantics don't depend on the
|
|
1302
|
+
# in-session write tracking that this code path bypasses.
|
|
1303
|
+
from docx.text.paragraph import _node_text
|
|
1304
|
+
|
|
1305
|
+
for table in self.tables:
|
|
1306
|
+
for row in table.rows:
|
|
1307
|
+
for cell in row.cells:
|
|
1308
|
+
try:
|
|
1309
|
+
existing_pids = cell._inner_paragraph_ids()
|
|
1310
|
+
for pid in existing_pids:
|
|
1311
|
+
existing_text = _node_text(self._session, pid)
|
|
1312
|
+
if not existing_text:
|
|
1313
|
+
continue
|
|
1314
|
+
run_sync(
|
|
1315
|
+
self._session.doc.replace(
|
|
1316
|
+
{
|
|
1317
|
+
"target": {
|
|
1318
|
+
"kind": "text",
|
|
1319
|
+
"blockId": pid,
|
|
1320
|
+
"range": {
|
|
1321
|
+
"start": 0,
|
|
1322
|
+
"end": len(existing_text),
|
|
1323
|
+
},
|
|
1324
|
+
},
|
|
1325
|
+
"text": "",
|
|
1326
|
+
},
|
|
1327
|
+
),
|
|
1328
|
+
)
|
|
1329
|
+
except Exception as err:
|
|
1330
|
+
_log_warn(f"Document.clear: skipped a cell: {err!r}")
|
|
1331
|
+
|
|
1249
1332
|
def add_paragraph(
|
|
1250
1333
|
self,
|
|
1251
1334
|
text: str = "",
|
|
@@ -111,6 +111,19 @@ class DocumentClosedError(DocxError):
|
|
|
111
111
|
"""Raised when operating on a closed Document."""
|
|
112
112
|
|
|
113
113
|
|
|
114
|
+
class StaleDocumentHandleError(DocxError):
|
|
115
|
+
"""Raised when an SDK proxy from an earlier executor call is reused."""
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class FlushAllError(DocxError):
|
|
119
|
+
"""Raised by ``docx.flush_all(strict=True)`` when any live buffer fails."""
|
|
120
|
+
|
|
121
|
+
def __init__(self, failures: list[str]) -> None:
|
|
122
|
+
self.failures: list[str] = list(failures)
|
|
123
|
+
detail = "; ".join(self.failures)
|
|
124
|
+
super().__init__(f"docx.flush_all() failed: {detail}")
|
|
125
|
+
|
|
126
|
+
|
|
114
127
|
class LocalSaveTargetNotSupportedError(DocxError, NotImplementedError):
|
|
115
128
|
"""Raised when ``Document.save(path_or_stream=...)`` is called with a
|
|
116
129
|
non-None argument.
|