athena-python-docx 0.30.0__tar.gz → 0.31.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/AGENTS.md +23 -4
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/PKG-INFO +1 -1
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/__init__.py +1 -1
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/_buffer.py +107 -3
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/_capabilities.py +8 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/_http_doc.py +15 -3
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/_postproc.py +217 -1
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/comments.py +7 -2
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/document.py +60 -6
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/drawing/__init__.py +4 -1
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/errors.py +18 -1
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/revisions.py +4 -1
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/section.py +16 -5
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/shape.py +4 -1
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/styles/style.py +107 -18
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/table.py +173 -29
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/text/hyperlink.py +10 -1
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/text/paragraph.py +18 -5
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/text/run.py +3 -2
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/pyproject.toml +1 -1
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/METHODOLOGY.md +35 -9
- athena_python_docx-0.31.0/tests/fidelity/op_snapshots/rw18_header_table_properties.json +34 -0
- athena_python_docx-0.31.0/tests/fidelity/op_snapshots/rw19_header_table_rows_columns.json +38 -0
- athena_python_docx-0.31.0/tests/fidelity/op_snapshots/rw20_header_table_merges.json +28 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/real_world_cases.py +74 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fixtures/field_reads.json +320 -20
- athena_python_docx-0.31.0/tests/live/baseline.json +478 -0
- athena_python_docx-0.31.0/tests/live/fixture_scripts.py +68 -0
- athena_python_docx-0.31.0/tests/live/reader.py +676 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/live/runner.py +140 -15
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/live/test_live_runner.py +220 -2
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/parity/intentional_deviations.json +46 -2
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_cell_merge.py +17 -7
- athena_python_docx-0.31.0/tests/test_cell_nested_tables.py +82 -0
- athena_python_docx-0.31.0/tests/test_comments_first_read.py +101 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_export_docx_clean.py +1 -1
- athena_python_docx-0.31.0/tests/test_export_rescue_anchors.py +348 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_field_reads_python_docx_parity.py +20 -6
- athena_python_docx-0.31.0/tests/test_flushed_write_failures.py +590 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_http_transport.py +1 -1
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_hyperlink_coalescing.py +40 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_iter_inner_content.py +35 -0
- athena_python_docx-0.31.0/tests/test_nested_table_refused.py +95 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_paragraph_delete.py +1 -0
- athena_python_docx-0.31.0/tests/test_style_tab_stops_read.py +127 -0
- athena_python_docx-0.31.0/tests/test_table_anchors.py +219 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_table_read_reuse.py +29 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/uv.lock +7 -7
- athena_python_docx-0.30.0/tests/live/baseline.json +0 -258
- athena_python_docx-0.30.0/tests/live/reader.py +0 -243
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/.gitignore +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/CLAUDE.md +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/DOCX_EXEC_LAB.md +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/README.md +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/_athena_extension.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/_batching.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/_citations.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/_execution.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/_http.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/_image_utils.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/_ptc.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/_references.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/_run_order.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/_table_styles.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/_timeouts.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/_tracked.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/api.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/blkcntnr.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/bookmarks.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/charts.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/client.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/commands.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/enum/__init__.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/enum/base.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/enum/dml.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/enum/section.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/enum/shape.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/enum/style.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/enum/table.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/enum/text.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/exceptions.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/fields.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/footnotes.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/image/__init__.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/image/exceptions.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/image/image.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/math.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/opc/__init__.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/opc/coreprops.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/oxml/__init__.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/sdt.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/session.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/settings.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/shared.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/styles/__init__.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/styles/_table.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/styles/latent.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/styles/styles.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/text/__init__.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/text/font.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/text/pagebreak.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/text/parfmt.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/text/tabstops.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/toc.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/docx/typing.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/scripts/docx_exec_lab.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/scripts/docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/scripts/dump_wire_fixtures.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/scripts/release.sh +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/scripts/round_trip_smoke.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/scripts/smoke_test_block_not_found.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/scripts/validate_find_replace_asset.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/__init__.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/conftest.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/README.md +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/__init__.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/ab_probe_cases.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/ab_probe_runner.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/auto_gen_cases.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/binary_round_trip.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/cases.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/complex_cases.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/coverage_report.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/extract.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/extreme_cases.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/fake_session.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/firm_templates/README.md +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/firm_templates/__init__.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/firm_templates/_runner.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/firm_templates/extractor.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/local_runner.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/mega_cases.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshot.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw16_header_footer_tables.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw17_header_letterhead_filled.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/ours_spec.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/parity_crawl.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/parity_diff.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/round_trip_tests.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/stock_spec.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fixtures/note_reference_reads.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fixtures/ruby_reads.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/fixtures/symbol_reads.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/live/__init__.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/live/sdk_driver.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/live/stock_driver.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/parity/README.md +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/parity/__init__.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/parity/baseline_gaps.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/parity/compare.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/parity/introspect.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/parity/reports/gap_report.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/parity/run_parity.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/parity/snapshots/athena_latest.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/parity/test_parity_gap.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_add_section.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_add_section_extract_items.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_athena_extension_creates_session_open.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_athena_extensions_contract.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_athena_extensions_registry.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_batch_chunking.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_batching_perf.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_block_not_found_error.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_buffer.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_cell_add_table_not_supported.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_cell_borders_spec.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_cell_fastpath_runs.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_cell_inner_format_stash.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_cell_inner_run_format_stash.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_cell_inner_run_guard.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_cell_paragraphs_listed.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_cell_text_plain_fastpath.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_cell_text_replace_semantics.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_citations.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_collapsed_range_format.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_color_format_theme.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_command_dataclasses.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_commands.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_comment_paragraphs.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_comments.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_content_control_value.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_core_properties.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_document_asset_id_property.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_document_clear.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_document_create.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_document_create_from_template.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_document_factory_validation.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_docx_exec_lab.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_drawing_image.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_empty_hyperlinks.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_enum_parity.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_execution_scope.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_field_refresh.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_field_runs.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_find_replace_session_open.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_image_url_data_uri.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_inline_page_break.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_inline_shape_alt_text.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_inline_shape_size.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_inline_shape_type.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_inline_shapes_floating.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_insert_deferred.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_list_styles.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_merged_cell_secondary_slot.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_merged_cells.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_missing_modules.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_note_reference_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_oxml_shim.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_page_break.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_paragraph_runs_hyperlinks.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_paragraph_text_len_cache.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_parity_misc.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_parity_round2.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_pdf_export_and_page_renders.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_phase_a_behavior.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_phase_b_headers_footers.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_phase_c_tables.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_positional_cell_id.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_postproc_ref_restore.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_pr19766_review_fixes.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_ptc.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_python_docx_api_parity.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_read_run_ranges.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_repeated_node_read.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_restart_numbering.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_revisions.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_round_trip_smoke_script.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_ruby_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_run_add_picture_anchor.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_run_edges.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_run_formatting_wire.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_run_identity.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_run_mark_inheritance.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_run_table_style_objects.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_run_text_python_docx.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_run_text_refreshes_paragraph.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_sdk_reads_served.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_section_read_lengths.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_add_picture.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_add_run.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_comments_get.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_document_audit.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_document_element.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_enum_section.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_font_audit.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_header_footer.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_hyperlink.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_inline_shape.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_misc.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_paragraph_style.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_parfmt.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_row_col_cell.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_run_add_break.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_run_style.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_run_style_strict.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_run_text.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_run_underline.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_section_audit.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_section_dimensions.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_section_onoff.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_settings.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_shared_audit.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_style.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_styles.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_table_audit.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_table_cell.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_table_dimensions.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_table_layout.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_smoke_integration.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_style_acceptance.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_style_font.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_style_formatting_writes.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_style_metadata_writes.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_style_names.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_style_setters_contract.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_symbol_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_table_set_cell_perf.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_table_style_id_resolution.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_temporarily_unavailable.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_text_control_characters.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_tracked_revisions_offsets.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_uv_lock_version.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_validate_find_replace_asset_script.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_wire_contract.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_xml_attr_guard.py +0 -0
- {athena_python_docx-0.30.0 → athena_python_docx-0.31.0}/tests/test_zod_wire_contract.py +0 -0
|
@@ -986,13 +986,32 @@ architecture or a DOCX Studio constraint:
|
|
|
986
986
|
`Table.autofit` reads `False` (python-docx leaves autofit on); its columns
|
|
987
987
|
share 9,360 twips, where python-docx shares the section's text width; and
|
|
988
988
|
in the fixed layout a column width the script sets sizes the column's
|
|
989
|
-
cells too, where python-docx sets the `w:gridCol` alone.
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
989
|
+
cells too, where python-docx sets the `w:gridCol` alone. Its cell
|
|
990
|
+
paragraphs carry their own spacing (`w:spacing w:after="0" w:line="240"
|
|
991
|
+
w:lineRule="auto"`: no space after, single lines), so a cell paragraph's
|
|
992
|
+
`paragraph_format.space_after` reads `0` and `line_spacing` `1.0`, where
|
|
993
|
+
python-docx's `add_table` writes none. It also draws borders and a zebra
|
|
994
|
+
fill, which no python-docx read sees. Recorded as `live_deviations` for
|
|
995
|
+
the script cases' `tables.autofit`, `tables.widths`, `tables.cell_format`
|
|
996
|
+
and `tables.cell_paragraph_format` in `tests/parity/intentional_deviations.json`;
|
|
993
997
|
an imported table's gaps stay gaps, and so do the two cases whose
|
|
994
998
|
`cell_format` also differs for another cause (`except`).
|
|
995
999
|
|
|
1000
|
+
- **A held `Table` raises `StaleTableError` once its table is gone.** The SDK
|
|
1001
|
+
holds the anchored ids docx-studio gives it (`t:N/R:C/K…@FIRST.LAST`: the
|
|
1002
|
+
table's place, then the paraIds of its first and last paragraphs), and
|
|
1003
|
+
docx-studio finds the table by those paragraphs, so a table an edit moved (a
|
|
1004
|
+
row added above a table nested in a cell, a table added before it, a merge
|
|
1005
|
+
moving a nested table into the merged cell) is still the one read and
|
|
1006
|
+
written, as python-docx's proxies follow the elements they hold. When no
|
|
1007
|
+
table holds them (the table was deleted, or the cell holding it had its
|
|
1008
|
+
text replaced, which removes the tables it held), docx-studio refuses the
|
|
1009
|
+
batch and the SDK raises `StaleTableError`: "the table this proxy named is
|
|
1010
|
+
gone; re-read `document.tables`". python-docx writes the detached element
|
|
1011
|
+
and changes nothing; raising tells the script instead of losing its write
|
|
1012
|
+
(coordinator, 2026-10-14). A held `_Cell` or `_Row` is still found by its
|
|
1013
|
+
place in its table.
|
|
1014
|
+
|
|
996
1015
|
- **`_Cell.merge` raises `InvalidSpanError` for every span that would cut
|
|
997
1016
|
through a merged cell.** It merges what python-docx merges and refuses
|
|
998
1017
|
what python-docx refuses (an L- or T-shaped pair of corners), but where
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: athena-python-docx
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.31.0
|
|
4
4
|
Summary: Drop-in replacement for python-docx that connects to Athena's DOCX Studio collaborative document stack
|
|
5
5
|
Project-URL: Homepage, https://athenaintelligence.ai
|
|
6
6
|
Author-email: Athena Intelligence <engineering@athenaintelligence.ai>
|
|
@@ -102,6 +102,54 @@ def _apply_proxy_id_rewrites(
|
|
|
102
102
|
pass
|
|
103
103
|
|
|
104
104
|
|
|
105
|
+
_WRITES_UNAPPLIED = "_docx_writes_unapplied"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def writes_unapplied(exc: BaseException) -> bool:
|
|
109
|
+
"""True for the failure of a batch whose buffered writes docx-studio did
|
|
110
|
+
not apply (or may not have): a flush of them, a read they rode with, or
|
|
111
|
+
the idle timer's flush. A read that flushed them raises it rather than
|
|
112
|
+
fall back as it would for a failure of its own, so the script hears of
|
|
113
|
+
the write as python-docx would have failed at the write itself."""
|
|
114
|
+
return getattr(exc, _WRITES_UNAPPLIED, False) is True
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _unapplied_writes(exc: BaseException) -> BaseException:
|
|
118
|
+
"""``exc``, marked as the failure of buffered writes (:func:`writes_unapplied`)."""
|
|
119
|
+
if not writes_unapplied(exc):
|
|
120
|
+
setattr(exc, _WRITES_UNAPPLIED, True)
|
|
121
|
+
exc.add_note("the buffered writes this call sent to docx-studio were not applied")
|
|
122
|
+
return exc
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
_REPORTED_ARGS = "_docx_reported_args"
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def keep_reported_message(exc: BaseException) -> None:
|
|
129
|
+
"""Keep ``exc``'s message as it was raised, before a flush's account of
|
|
130
|
+
what of it docx-studio applied is put ahead of it (``_flush_outcome``), so
|
|
131
|
+
that a read whose buffered writes were sent again on their own can raise
|
|
132
|
+
its failure as its own (:func:`_as_the_read_s_own_failure`)."""
|
|
133
|
+
if not hasattr(exc, _REPORTED_ARGS):
|
|
134
|
+
setattr(exc, _REPORTED_ARGS, exc.args)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _as_the_read_s_own_failure(exc: BaseException, read: Command) -> None:
|
|
138
|
+
"""``exc``, the failure of the read ``read`` whose buffered writes were
|
|
139
|
+
sent again on their own and applied, saying only that the read failed:
|
|
140
|
+
its message as docx-studio gave it, without the flush's account that the
|
|
141
|
+
writes did not land, ``read`` the one command that did not land
|
|
142
|
+
(``unapplied_commands``, ``failed_command``), and nothing applied before
|
|
143
|
+
it (``partial_applied``). A caller re-applying what the error names then
|
|
144
|
+
repeats no write."""
|
|
145
|
+
reported: object = getattr(exc, _REPORTED_ARGS, None)
|
|
146
|
+
if isinstance(reported, tuple):
|
|
147
|
+
exc.args = reported
|
|
148
|
+
exc.unapplied_commands = [read] # type: ignore[attr-defined]
|
|
149
|
+
exc.failed_command = read # type: ignore[attr-defined]
|
|
150
|
+
exc.partial_applied = [] # type: ignore[attr-defined]
|
|
151
|
+
|
|
152
|
+
|
|
105
153
|
def _repeatable_read_key(cmd: Command) -> str | None:
|
|
106
154
|
"""The key a repeat of ``cmd`` answers from (:meth:`CommandBuffer.call`):
|
|
107
155
|
a ``GetNodeById``'s wire form; ``None`` for any other command."""
|
|
@@ -677,9 +725,11 @@ class CommandBuffer:
|
|
|
677
725
|
# See ``_apply_proxy_id_rewrites`` for the full rationale.
|
|
678
726
|
_apply_proxy_id_rewrites(_extract_partial_results(exc), proxy_id_refs)
|
|
679
727
|
_ptc_emit_end_batch(pending, is_error=True)
|
|
728
|
+
_unapplied_writes(exc)
|
|
680
729
|
raise
|
|
681
|
-
except Exception:
|
|
730
|
+
except Exception as exc:
|
|
682
731
|
_ptc_emit_end_batch(pending, is_error=True)
|
|
732
|
+
_unapplied_writes(exc)
|
|
683
733
|
raise
|
|
684
734
|
finally:
|
|
685
735
|
_forget_reads_of_others(self)
|
|
@@ -751,10 +801,17 @@ class CommandBuffer:
|
|
|
751
801
|
# the trailing ``cmd`` would otherwise abandon every
|
|
752
802
|
# buffered Create's rewrite at once.
|
|
753
803
|
_apply_proxy_id_rewrites(_extract_partial_results(exc), proxy_id_refs)
|
|
754
|
-
_ptc_emit_end_batch(
|
|
804
|
+
_ptc_emit_end_batch([cmd], is_error=True)
|
|
805
|
+
if pending:
|
|
806
|
+
self._settle_writes_of_failed_read(
|
|
807
|
+
exc, pending, change_mode=change_mode, user=user, proxy_id_refs=proxy_id_refs
|
|
808
|
+
)
|
|
755
809
|
raise
|
|
756
|
-
except Exception:
|
|
810
|
+
except Exception as exc:
|
|
757
811
|
_ptc_emit_end_batch(all_cmds, is_error=True)
|
|
812
|
+
if pending:
|
|
813
|
+
# Whether the request landed is not known (a timeout, a dropped connection).
|
|
814
|
+
_unapplied_writes(exc)
|
|
758
815
|
raise
|
|
759
816
|
finally:
|
|
760
817
|
# A batch of one node read changes nothing another buffer read.
|
|
@@ -788,6 +845,51 @@ class CommandBuffer:
|
|
|
788
845
|
trailing = results[-1]
|
|
789
846
|
return trailing if trailing is not None else {}
|
|
790
847
|
|
|
848
|
+
def _settle_writes_of_failed_read(
|
|
849
|
+
self,
|
|
850
|
+
exc: DocxError,
|
|
851
|
+
pending: list[Command],
|
|
852
|
+
*,
|
|
853
|
+
change_mode: str | None,
|
|
854
|
+
user: dict[str, str] | None,
|
|
855
|
+
proxy_id_refs: "dict[str, list[tuple[object, str]]]",
|
|
856
|
+
) -> None:
|
|
857
|
+
"""The buffered writes ``pending`` that rode with a read whose batch
|
|
858
|
+
failed (``exc``). docx-studio applies a batch all or nothing, so when
|
|
859
|
+
it reported the read itself failing, it applied none of the writes:
|
|
860
|
+
they are sent again on their own, and the read's failure stays its own
|
|
861
|
+
to report, as a read's fallback expects: once they landed, it names the
|
|
862
|
+
read alone as what did not land (:func:`_as_the_read_s_own_failure`).
|
|
863
|
+
Any other failure (one of the writes, or one not reported) marks
|
|
864
|
+
``exc`` as one of unapplied writes (:func:`writes_unapplied`), and a
|
|
865
|
+
failure of the writes sent again is raised as one."""
|
|
866
|
+
unapplied = getattr(exc, "unapplied_commands", None)
|
|
867
|
+
failed = getattr(exc, "failed_command", None)
|
|
868
|
+
if not (isinstance(unapplied, list) and unapplied and failed is unapplied[-1]):
|
|
869
|
+
_ptc_emit_end_batch(pending, is_error=True)
|
|
870
|
+
_unapplied_writes(exc)
|
|
871
|
+
return
|
|
872
|
+
writes = unapplied[:-1]
|
|
873
|
+
if not writes:
|
|
874
|
+
# An older docx-studio kept the writes ahead of the read.
|
|
875
|
+
_ptc_emit_end_batch(pending, is_error=False)
|
|
876
|
+
return
|
|
877
|
+
try:
|
|
878
|
+
results, _skipped = self._client.execute_batch(
|
|
879
|
+
self._asset_id, writes, change_mode=change_mode, user=user
|
|
880
|
+
)
|
|
881
|
+
except Exception as again:
|
|
882
|
+
# The writes sent again go in requests of at most
|
|
883
|
+
# ``MAX_BATCH_COMMANDS``: those a failed request's earlier ones
|
|
884
|
+
# applied keep their ids, as a flush's do, and only the rest are
|
|
885
|
+
# the failure's unapplied writes.
|
|
886
|
+
_apply_proxy_id_rewrites(_extract_partial_results(again), proxy_id_refs)
|
|
887
|
+
_ptc_emit_end_batch(pending, is_error=True)
|
|
888
|
+
raise _unapplied_writes(again) from exc
|
|
889
|
+
_ptc_emit_end_batch(pending, is_error=False)
|
|
890
|
+
_apply_proxy_id_rewrites(results, proxy_id_refs)
|
|
891
|
+
_as_the_read_s_own_failure(exc, failed)
|
|
892
|
+
|
|
791
893
|
def _reset_timer_locked(self) -> None:
|
|
792
894
|
# Caller must hold self._lock.
|
|
793
895
|
self._cancel_timer_locked()
|
|
@@ -852,6 +954,8 @@ class CommandBuffer:
|
|
|
852
954
|
"raised by a background docx-sdk auto-flush that failed earlier; "
|
|
853
955
|
"the commands in that batch were NOT applied",
|
|
854
956
|
)
|
|
957
|
+
# Marked as unapplied writes by the flush (``_flush_in_order``):
|
|
958
|
+
# the read it surfaces in next raises it rather than fall back.
|
|
855
959
|
with self._lock:
|
|
856
960
|
self._deferred_flush_error = e
|
|
857
961
|
sys.stderr.write(
|
|
@@ -14,6 +14,14 @@ from typing import Any
|
|
|
14
14
|
|
|
15
15
|
from docx._batching import run_sync
|
|
16
16
|
|
|
17
|
+
# What this SDK declares in ``client.capabilities``: it holds the anchored table
|
|
18
|
+
# ids (``t:N/R:C/K…@FIRST.LAST``) a docx-studio gives a client declaring it, and
|
|
19
|
+
# sends them back as given. docx-studio then finds each held table by its
|
|
20
|
+
# paragraphs, wherever an edit moved it, and refuses the command with
|
|
21
|
+
# ``StaleTable`` when no one table holds them (``docx.errors.StaleTableError``).
|
|
22
|
+
# An older docx-studio gives none, so the SDK needs no check.
|
|
23
|
+
TABLE_ANCHORS_CLIENT_CAPABILITY = "tableAnchors"
|
|
24
|
+
|
|
17
25
|
# Session -> the capabilities its server reported, read once.
|
|
18
26
|
_capabilities: "weakref.WeakKeyDictionary[Any, dict[str, Any]]" = weakref.WeakKeyDictionary()
|
|
19
27
|
|
|
@@ -5,7 +5,7 @@ Wire format (matches ``apps/api/src/commands/types.ts``):
|
|
|
5
5
|
POST /docs/:id/commands
|
|
6
6
|
{
|
|
7
7
|
"client": {"type": "python-sdk", "version": "<x.y.z>",
|
|
8
|
-
"capabilities": ["trackedAuthoring", "trackedMinimalParagraphText"]},
|
|
8
|
+
"capabilities": ["trackedAuthoring", "trackedMinimalParagraphText", "tableAnchors"]},
|
|
9
9
|
"commands": [
|
|
10
10
|
{"type": "CreateParagraph", "text": "Hi", "at": {"kind": "documentEnd"}},
|
|
11
11
|
...
|
|
@@ -42,8 +42,9 @@ from requests.adapters import HTTPAdapter
|
|
|
42
42
|
from urllib3.util.retry import Retry
|
|
43
43
|
|
|
44
44
|
from docx._batching import call_abandoned, note_progress
|
|
45
|
-
from docx._buffer import CommandBuffer
|
|
45
|
+
from docx._buffer import CommandBuffer, keep_reported_message
|
|
46
46
|
from docx._tracked import TRACKED_AUTHORING_CAPABILITY, TRACKED_MINIMAL_PARAGRAPH_TEXT_CAPABILITY
|
|
47
|
+
from docx._capabilities import TABLE_ANCHORS_CLIENT_CAPABILITY
|
|
47
48
|
from docx._timeouts import (
|
|
48
49
|
DEFAULT_OP_TIMEOUT_SECONDS,
|
|
49
50
|
LONG_RUNNING_COMMAND_TIMEOUT_SECONDS,
|
|
@@ -194,6 +195,7 @@ from docx.errors import (
|
|
|
194
195
|
NotFoundError,
|
|
195
196
|
NotNumberedListItemError,
|
|
196
197
|
SessionError,
|
|
198
|
+
StaleTableError,
|
|
197
199
|
_missing_block_id,
|
|
198
200
|
)
|
|
199
201
|
from docx.exceptions import InvalidSpanError
|
|
@@ -284,6 +286,7 @@ def _flush_outcome(
|
|
|
284
286
|
f" applied{not_sent}.{offered} Re-read the document to see whether that"
|
|
285
287
|
" request landed before re-applying anything."
|
|
286
288
|
)
|
|
289
|
+
keep_reported_message(exc)
|
|
287
290
|
exc.args = (f"{prefix}\n\n{exc.args[0] if exc.args else ''}", *exc.args[1:])
|
|
288
291
|
return exc
|
|
289
292
|
applied = min(start + landed, total)
|
|
@@ -333,6 +336,7 @@ def _flush_outcome(
|
|
|
333
336
|
f" nothing{not_sent}. `exc.unapplied_commands` holds the {len(unapplied)}"
|
|
334
337
|
f" that did not land{which}. {redo}"
|
|
335
338
|
)
|
|
339
|
+
keep_reported_message(exc)
|
|
336
340
|
exc.args = (f"{prefix}\n\n{exc.args[0] if exc.args else ''}", *exc.args[1:])
|
|
337
341
|
return exc
|
|
338
342
|
|
|
@@ -838,6 +842,13 @@ def _http_post_json(
|
|
|
838
842
|
err_obj,
|
|
839
843
|
partial_applied,
|
|
840
844
|
)
|
|
845
|
+
# A held ``Table`` whose table is gone (``docx.table`` anchored ids).
|
|
846
|
+
if err_obj.get("error") == "StaleTable":
|
|
847
|
+
raise _reported(
|
|
848
|
+
StaleTableError(str(err_obj.get("message") or base_msg)),
|
|
849
|
+
err_obj,
|
|
850
|
+
partial_applied,
|
|
851
|
+
)
|
|
841
852
|
# ``Paragraph.restart_numbering`` on a paragraph that takes no number.
|
|
842
853
|
if err_obj.get("error") == "NotNumberedListItem":
|
|
843
854
|
raise _reported(
|
|
@@ -1198,10 +1209,11 @@ class HttpClient:
|
|
|
1198
1209
|
"version": _sdk_version(),
|
|
1199
1210
|
# Keeps its offsets as a tracked edit moves them, and reads a
|
|
1200
1211
|
# paragraph's length back after a tracked ``paragraph.text``
|
|
1201
|
-
# (``docx._tracked``).
|
|
1212
|
+
# (``docx._tracked``); holds anchored table ids (``docx.table``).
|
|
1202
1213
|
"capabilities": [
|
|
1203
1214
|
TRACKED_AUTHORING_CAPABILITY,
|
|
1204
1215
|
TRACKED_MINIMAL_PARAGRAPH_TEXT_CAPABILITY,
|
|
1216
|
+
TABLE_ANCHORS_CLIENT_CAPABILITY,
|
|
1205
1217
|
],
|
|
1206
1218
|
},
|
|
1207
1219
|
"commands": [
|
|
@@ -24,6 +24,7 @@ from __future__ import annotations
|
|
|
24
24
|
|
|
25
25
|
import io
|
|
26
26
|
import re as _re
|
|
27
|
+
import warnings
|
|
27
28
|
import zipfile
|
|
28
29
|
from dataclasses import dataclass, field, replace
|
|
29
30
|
from typing import Any, Iterable
|
|
@@ -32,6 +33,41 @@ from xml.etree import ElementTree as ET
|
|
|
32
33
|
|
|
33
34
|
WORD_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
|
|
34
35
|
W_NS = f"{{{WORD_NS}}}"
|
|
36
|
+
W14_NS = "{http://schemas.microsoft.com/office/word/2010/wordml}"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class UnidentifiedTableRewriteWarning(UserWarning):
|
|
40
|
+
"""Emitted when the export rescue skips its rewrites of a table the
|
|
41
|
+
script created because it cannot find that table in the exported
|
|
42
|
+
``word/document.xml``: no one table holds the paragraphs its anchors
|
|
43
|
+
name (a table was added before it, or it is gone), or, its id carrying
|
|
44
|
+
no anchors (a docx-studio from before anchored table ids), the table at
|
|
45
|
+
its place is not provably it, or more than one table looks like it. The
|
|
46
|
+
table keeps the formatting docx-studio wrote for it; no other table is
|
|
47
|
+
patched in its place."""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(frozen=True)
|
|
51
|
+
class CreatedTableIdentity:
|
|
52
|
+
"""How the export rescue tells, in the exported body, a table the script
|
|
53
|
+
created (``Document.add_table``).
|
|
54
|
+
|
|
55
|
+
``anchors``: the ``w14:paraId``s of its first and last own paragraphs,
|
|
56
|
+
from the id docx-studio gives it now; the table is the one holding them.
|
|
57
|
+
From a docx-studio without anchored ids, ``position`` (its ``t:N``) names
|
|
58
|
+
a candidate, which is taken only when it holds what the script knows of
|
|
59
|
+
its table and no other table of the body does, nested ones included:
|
|
60
|
+
``rows`` × ``columns`` cells, and the ``texts`` the script wrote to them;
|
|
61
|
+
with none written, the size it was ``created`` with and an empty first
|
|
62
|
+
cell. Neither (``CreatedTableIdentity()``): not to be found.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
anchors: tuple[str, ...] = ()
|
|
66
|
+
position: int | None = None
|
|
67
|
+
rows: int | None = None
|
|
68
|
+
columns: int | None = None
|
|
69
|
+
texts: tuple[tuple[int, int, str], ...] = ()
|
|
70
|
+
created: tuple[int, int] | None = None
|
|
35
71
|
|
|
36
72
|
|
|
37
73
|
# Every well-known OOXML namespace that may appear in a DOCX Studio-exported
|
|
@@ -249,6 +285,134 @@ class CellAddress:
|
|
|
249
285
|
part_key: str | None = None
|
|
250
286
|
|
|
251
287
|
|
|
288
|
+
def _own_paragraph_ids(tbl: ET.Element) -> set[str]:
|
|
289
|
+
"""The ``w14:paraId`` of each paragraph ``tbl`` holds as its own: not
|
|
290
|
+
one of a table nested in it."""
|
|
291
|
+
ids: set[str] = set()
|
|
292
|
+
pending = list(tbl)
|
|
293
|
+
while pending:
|
|
294
|
+
element = pending.pop()
|
|
295
|
+
if element.tag == f"{W_NS}tbl":
|
|
296
|
+
continue
|
|
297
|
+
if element.tag == f"{W_NS}p":
|
|
298
|
+
para_id = element.get(f"{W14_NS}paraId")
|
|
299
|
+
if para_id:
|
|
300
|
+
ids.add(para_id)
|
|
301
|
+
continue
|
|
302
|
+
pending.extend(list(element))
|
|
303
|
+
return ids
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _cell_text(tbl: ET.Element, row: int, col: int) -> str | None:
|
|
307
|
+
"""The text of the cell of ``tbl`` starting at grid (``row``, ``col``), its
|
|
308
|
+
own paragraphs' joined with ``"\\n"`` (a tab ``"\\t"``, a line break
|
|
309
|
+
``"\\n"``); ``None`` when no cell starts there."""
|
|
310
|
+
rows = tbl.findall(f"{W_NS}tr")
|
|
311
|
+
if not 0 <= row < len(rows):
|
|
312
|
+
return None
|
|
313
|
+
cell = next((tc for start, tc in _cells_by_grid_column(rows[row]) if start == col), None)
|
|
314
|
+
if cell is None:
|
|
315
|
+
return None
|
|
316
|
+
paragraphs: list[str] = []
|
|
317
|
+
for paragraph in cell.findall(f"{W_NS}p"):
|
|
318
|
+
parts: list[str] = []
|
|
319
|
+
for run in paragraph.iter(f"{W_NS}r"):
|
|
320
|
+
for element in run:
|
|
321
|
+
if element.tag == f"{W_NS}t":
|
|
322
|
+
parts.append(element.text or "")
|
|
323
|
+
elif element.tag == f"{W_NS}tab":
|
|
324
|
+
parts.append("\t")
|
|
325
|
+
elif element.tag == f"{W_NS}br":
|
|
326
|
+
parts.append("\n")
|
|
327
|
+
paragraphs.append("".join(parts))
|
|
328
|
+
return "\n".join(paragraphs)
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def _mismatch(tbl: ET.Element, identity: CreatedTableIdentity) -> str | None:
|
|
332
|
+
"""Why the table ``tbl`` is not provably the created table ``identity``
|
|
333
|
+
tells (:class:`CreatedTableIdentity`); ``None`` when it is."""
|
|
334
|
+
if identity.rows is None or identity.columns is None:
|
|
335
|
+
return "the script does not know its table's size"
|
|
336
|
+
size = (len(tbl.findall(f"{W_NS}tr")), len(tbl.findall(f"{W_NS}tblGrid/{W_NS}gridCol")))
|
|
337
|
+
if size != (identity.rows, identity.columns):
|
|
338
|
+
return f"it holds {size[0]} × {size[1]} cells, the script's table {identity.rows} × {identity.columns}"
|
|
339
|
+
for row, col, text in identity.texts:
|
|
340
|
+
if _cell_text(tbl, row, col) != text:
|
|
341
|
+
return f"its cell ({row}, {col}) does not hold the text the script wrote there"
|
|
342
|
+
if not identity.texts and (identity.created != size or _cell_text(tbl, 0, 0) != ""):
|
|
343
|
+
return "the script wrote no text to tell it by, and it is not as the script created it"
|
|
344
|
+
return None
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def _locate_body_tables(
|
|
348
|
+
body: ET.Element, created_tables: "dict[int, CreatedTableIdentity | None]"
|
|
349
|
+
) -> dict[int, int | None]:
|
|
350
|
+
"""Where each created table is among the body's tables now
|
|
351
|
+
(``created_tables``, by ``Table._doc_index``): the one table holding a
|
|
352
|
+
paragraph its anchors name, or, without anchors, the table at its
|
|
353
|
+
position when it is what the script knows of its table
|
|
354
|
+
(:func:`_mismatch`) and no other table of the body is, nested ones
|
|
355
|
+
included (with a look-alike, a position that may have shifted proves
|
|
356
|
+
nothing); ``None``, with an
|
|
357
|
+
:class:`UnidentifiedTableRewriteWarning`, for a table that is gone
|
|
358
|
+
(``None``), or that cannot be told."""
|
|
359
|
+
tables = body.findall(f"{W_NS}tbl")
|
|
360
|
+
every_table = list(body.iter(f"{W_NS}tbl"))
|
|
361
|
+
owned = [_own_paragraph_ids(tbl) for tbl in tables]
|
|
362
|
+
located: dict[int, int | None] = {}
|
|
363
|
+
for doc_index, identity in created_tables.items():
|
|
364
|
+
wanted = set(identity.anchors) if identity is not None else set()
|
|
365
|
+
holders = [index for index, ids in enumerate(owned) if wanted & ids]
|
|
366
|
+
located[doc_index] = holders[0] if len(holders) == 1 else None
|
|
367
|
+
reason = ""
|
|
368
|
+
if identity is None:
|
|
369
|
+
reason = "docx-studio refused its id, so the table is gone"
|
|
370
|
+
elif wanted:
|
|
371
|
+
if located[doc_index] is None:
|
|
372
|
+
held = "no" if not holders else str(len(holders))
|
|
373
|
+
reason = f"{held} tables of the export hold the paragraphs its anchors name"
|
|
374
|
+
elif identity.position is None or not 0 <= identity.position < len(tables):
|
|
375
|
+
reason = "its id carries no anchors (a docx-studio from before anchored table ids) and names no table of the export"
|
|
376
|
+
else:
|
|
377
|
+
alike = sum(1 for tbl in every_table if _mismatch(tbl, identity) is None)
|
|
378
|
+
mismatch = _mismatch(tables[identity.position], identity)
|
|
379
|
+
if alike > 1:
|
|
380
|
+
reason = (
|
|
381
|
+
f"ambiguous: {alike} tables of the export match what the script knows of it"
|
|
382
|
+
)
|
|
383
|
+
elif mismatch is None:
|
|
384
|
+
located[doc_index] = identity.position
|
|
385
|
+
else:
|
|
386
|
+
reason = (
|
|
387
|
+
f"the table at its place (t:{identity.position}) is not provably it: {mismatch}"
|
|
388
|
+
)
|
|
389
|
+
if located[doc_index] is None:
|
|
390
|
+
warnings.warn(
|
|
391
|
+
UnidentifiedTableRewriteWarning(
|
|
392
|
+
f"The export rescue skipped its rewrites of table {doc_index} this script created: "
|
|
393
|
+
f"{reason}. The table keeps the formatting docx-studio wrote for it, and no other "
|
|
394
|
+
"table was patched in its place."
|
|
395
|
+
),
|
|
396
|
+
stacklevel=4,
|
|
397
|
+
)
|
|
398
|
+
return located
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def _relocated_ops(ops: dict, located: dict[int, int | None]) -> dict:
|
|
402
|
+
"""``ops`` with each body address's ``table_doc_index`` re-addressed to
|
|
403
|
+
where its table is now (``located``), dropping those of a table that
|
|
404
|
+
cannot be found; a header or footer address (``part_key``) as it is."""
|
|
405
|
+
out: dict = {}
|
|
406
|
+
for address, value in ops.items():
|
|
407
|
+
if getattr(address, "part_key", None) is not None:
|
|
408
|
+
out[address] = value
|
|
409
|
+
continue
|
|
410
|
+
index = located.get(address.table_doc_index)
|
|
411
|
+
if index is not None:
|
|
412
|
+
out[replace(address, table_doc_index=index)] = value
|
|
413
|
+
return out
|
|
414
|
+
|
|
415
|
+
|
|
252
416
|
def _cells_by_grid_column(tr: ET.Element) -> list[tuple[int, ET.Element]]:
|
|
253
417
|
"""Each ``<w:tc>`` of a row with the grid column it starts at.
|
|
254
418
|
|
|
@@ -505,6 +669,26 @@ class PostExportOps:
|
|
|
505
669
|
_header_footer_relocations: list["HeaderFooterRelocation"] = field(
|
|
506
670
|
default_factory=list,
|
|
507
671
|
)
|
|
672
|
+
# The tables ``Document.add_table`` created, by ``Table._doc_index``:
|
|
673
|
+
# the export reads each one's id, whose anchors find it in the exported
|
|
674
|
+
# body (:func:`_locate_body_tables`).
|
|
675
|
+
_body_tables: dict[int, Any] = field(default_factory=dict)
|
|
676
|
+
|
|
677
|
+
def register_body_table(self, doc_index: int, table: Any) -> None:
|
|
678
|
+
"""Remember the table ``Document.add_table`` created at ``doc_index``."""
|
|
679
|
+
self._body_tables[doc_index] = table
|
|
680
|
+
|
|
681
|
+
def body_tables_rewritten(self) -> dict[int, Any]:
|
|
682
|
+
"""The created tables a pending body rewrite addresses, by ``doc_index``
|
|
683
|
+
(``None`` for an index no created table holds)."""
|
|
684
|
+
indexes: set[int] = set()
|
|
685
|
+
for ops in (self._cell_format_ops, self._cell_run_format_ops, self._cell_hyperlink_ops):
|
|
686
|
+
indexes.update(address.table_doc_index for address in ops if address.part_key is None)
|
|
687
|
+
indexes.update(address.table_doc_index for address in self._cell_shading_ops)
|
|
688
|
+
indexes.update(self._table_borders_ops)
|
|
689
|
+
indexes.update(address.table_doc_index for address in self._row_height_ops)
|
|
690
|
+
indexes.update(address.table_doc_index for address in self._row_cant_split_ops)
|
|
691
|
+
return {index: self._body_tables.get(index) for index in sorted(indexes)}
|
|
508
692
|
|
|
509
693
|
def has_pending(self) -> bool:
|
|
510
694
|
return (
|
|
@@ -549,6 +733,7 @@ class PostExportOps:
|
|
|
549
733
|
self._row_height_ops.clear()
|
|
550
734
|
self._row_cant_split_ops.clear()
|
|
551
735
|
self._header_footer_relocations.clear()
|
|
736
|
+
self._body_tables.clear()
|
|
552
737
|
|
|
553
738
|
def add_row_cant_split(
|
|
554
739
|
self,
|
|
@@ -1045,7 +1230,12 @@ class PostExportOps:
|
|
|
1045
1230
|
),
|
|
1046
1231
|
)
|
|
1047
1232
|
|
|
1048
|
-
def apply_to_docx(
|
|
1233
|
+
def apply_to_docx(
|
|
1234
|
+
self,
|
|
1235
|
+
docx_bytes: bytes,
|
|
1236
|
+
*,
|
|
1237
|
+
created_tables: "dict[int, CreatedTableIdentity | None] | None" = None,
|
|
1238
|
+
) -> bytes:
|
|
1049
1239
|
"""Return ``docx_bytes`` with every recorded rewrite applied.
|
|
1050
1240
|
|
|
1051
1241
|
``Document.export_docx`` calls it only while a rewrite is pending
|
|
@@ -1053,9 +1243,17 @@ class PostExportOps:
|
|
|
1053
1243
|
it is: every ``w:sectPr`` in a paragraph ends a real section of the
|
|
1054
1244
|
room (``Document.add_section`` writes them; the page-break markers
|
|
1055
1245
|
DOCX Studio 1.8.1 exported in their place are gone with that engine).
|
|
1246
|
+
|
|
1247
|
+
``created_tables`` tells, for each created table a body rewrite
|
|
1248
|
+
addresses (:meth:`body_tables_rewritten`), how to find it
|
|
1249
|
+
(:class:`CreatedTableIdentity`): the rewrite finds the table wherever
|
|
1250
|
+
it is in the exported body, and skips
|
|
1251
|
+
(:class:`UnidentifiedTableRewriteWarning`) one it cannot. Without it,
|
|
1252
|
+
a table is found by its index among the body's tables.
|
|
1056
1253
|
"""
|
|
1057
1254
|
return _rewrite_word_document_xml(
|
|
1058
1255
|
docx_bytes,
|
|
1256
|
+
created_tables=created_tables,
|
|
1059
1257
|
cell_format_ops=self._cell_format_ops,
|
|
1060
1258
|
cell_run_format_ops=self._cell_run_format_ops,
|
|
1061
1259
|
cell_hyperlink_ops=self._cell_hyperlink_ops,
|
|
@@ -1089,6 +1287,7 @@ def _rewrite_word_document_xml(
|
|
|
1089
1287
|
header_footer_relocations: "list[HeaderFooterRelocation] | None" = None,
|
|
1090
1288
|
row_height_ops: "dict[RowHeightAddress, RowHeightProps] | None" = None,
|
|
1091
1289
|
row_cant_split_ops: "dict[RowHeightAddress, bool] | None" = None,
|
|
1290
|
+
created_tables: "dict[int, CreatedTableIdentity | None] | None" = None,
|
|
1092
1291
|
) -> bytes:
|
|
1093
1292
|
"""Unzip the ``.docx``, apply rewrites to ``word/document.xml``,
|
|
1094
1293
|
re-zip and return the result.
|
|
@@ -1209,6 +1408,23 @@ def _rewrite_word_document_xml(
|
|
|
1209
1408
|
body, header_footer_relocations,
|
|
1210
1409
|
)
|
|
1211
1410
|
|
|
1411
|
+
# The body rewrites address a created table by ``Table._doc_index``;
|
|
1412
|
+
# with anchors, each is re-addressed to where its paragraphs are in
|
|
1413
|
+
# the body now, and those of a table that cannot be found are dropped.
|
|
1414
|
+
if created_tables is not None:
|
|
1415
|
+
located = _locate_body_tables(body, created_tables)
|
|
1416
|
+
body_cell_format_ops = _relocated_ops(body_cell_format_ops, located)
|
|
1417
|
+
cell_run_format_ops = _relocated_ops(cell_run_format_ops, located)
|
|
1418
|
+
cell_hyperlink_ops = _relocated_ops(cell_hyperlink_ops, located)
|
|
1419
|
+
cell_shading_ops = _relocated_ops(cell_shading_ops, located)
|
|
1420
|
+
table_borders_ops = {
|
|
1421
|
+
index: edges
|
|
1422
|
+
for doc_index, edges in table_borders_ops.items()
|
|
1423
|
+
if (index := located.get(doc_index)) is not None
|
|
1424
|
+
}
|
|
1425
|
+
row_height_ops = _relocated_ops(row_height_ops or {}, located)
|
|
1426
|
+
row_cant_split_ops = _relocated_ops(row_cant_split_ops or {}, located)
|
|
1427
|
+
|
|
1212
1428
|
# Body cell-format ops apply AFTER relocation so the body
|
|
1213
1429
|
# table indices align with ``Table._doc_index``. Pre-relocation,
|
|
1214
1430
|
# body table 0 was the misplaced header/footer table (which
|
|
@@ -37,6 +37,7 @@ from datetime import datetime, timezone
|
|
|
37
37
|
from typing import TYPE_CHECKING
|
|
38
38
|
|
|
39
39
|
from docx._batching import run_sync
|
|
40
|
+
from docx._buffer import writes_unapplied
|
|
40
41
|
from docx.blkcntnr import BlockItemContainer
|
|
41
42
|
from docx.errors import DocxError, NotFoundError
|
|
42
43
|
|
|
@@ -330,7 +331,9 @@ class Comment(BlockItemContainer):
|
|
|
330
331
|
comment range listed without one, with empty text)."""
|
|
331
332
|
try:
|
|
332
333
|
result: object = run_sync(self._session.doc.comments.get({"id": self.comment_id}))
|
|
333
|
-
except NotFoundError:
|
|
334
|
+
except NotFoundError as exc:
|
|
335
|
+
if writes_unapplied(exc):
|
|
336
|
+
raise
|
|
334
337
|
result = None
|
|
335
338
|
read = result.get("text") if isinstance(result, dict) else None
|
|
336
339
|
if isinstance(read, str):
|
|
@@ -540,7 +543,9 @@ class Comments:
|
|
|
540
543
|
result: object = run_sync(
|
|
541
544
|
self._session.doc.comments.get({"id": str(comment_id)}),
|
|
542
545
|
)
|
|
543
|
-
except NotFoundError:
|
|
546
|
+
except NotFoundError as exc:
|
|
547
|
+
if writes_unapplied(exc):
|
|
548
|
+
raise
|
|
544
549
|
return None
|
|
545
550
|
if isinstance(result, dict):
|
|
546
551
|
# DOCX Studio may surface the id under ``id`` or ``commentId`` —
|