athena-python-docx 0.29.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.29.0 → athena_python_docx-0.31.0}/AGENTS.md +67 -15
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/PKG-INFO +1 -1
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/__init__.py +1 -1
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_buffer.py +107 -3
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_capabilities.py +8 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_http_doc.py +25 -5
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_postproc.py +242 -1
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_tracked.py +11 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/commands.py +20 -2
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/comments.py +11 -4
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/document.py +62 -7
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/drawing/__init__.py +4 -1
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/errors.py +18 -1
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/revisions.py +4 -1
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/section.py +16 -5
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/shape.py +4 -1
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/styles/style.py +107 -18
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/table.py +173 -29
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/text/hyperlink.py +10 -1
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/text/paragraph.py +96 -8
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/text/run.py +3 -2
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/pyproject.toml +1 -1
- {athena_python_docx-0.29.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.29.0 → athena_python_docx-0.31.0}/tests/fidelity/real_world_cases.py +74 -0
- {athena_python_docx-0.29.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.29.0 → athena_python_docx-0.31.0}/tests/live/runner.py +140 -15
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/live/test_live_runner.py +220 -2
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/intentional_deviations.json +46 -2
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_athena_extensions_registry.py +1 -0
- {athena_python_docx-0.29.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.29.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.29.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.29.0 → athena_python_docx-0.31.0}/tests/test_http_transport.py +1 -1
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_hyperlink_coalescing.py +40 -0
- {athena_python_docx-0.29.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.31.0/tests/test_paragraph_delete.py +187 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_postproc_ref_restore.py +48 -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.29.0 → athena_python_docx-0.31.0}/tests/test_table_read_reuse.py +29 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_tracked_revisions_offsets.py +28 -15
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_wire_contract.py +1 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/uv.lock +7 -7
- athena_python_docx-0.29.0/tests/live/baseline.json +0 -262
- athena_python_docx-0.29.0/tests/live/reader.py +0 -243
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/.gitignore +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/CLAUDE.md +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/DOCX_EXEC_LAB.md +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/README.md +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_athena_extension.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_batching.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_citations.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_execution.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_http.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_image_utils.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_ptc.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_references.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_run_order.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_table_styles.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_timeouts.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/api.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/blkcntnr.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/bookmarks.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/charts.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/client.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/enum/__init__.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/enum/base.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/enum/dml.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/enum/section.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/enum/shape.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/enum/style.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/enum/table.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/enum/text.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/exceptions.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/fields.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/footnotes.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/image/__init__.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/image/exceptions.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/image/image.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/math.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/opc/__init__.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/opc/coreprops.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/oxml/__init__.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/sdt.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/session.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/settings.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/shared.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/styles/__init__.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/styles/_table.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/styles/latent.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/styles/styles.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/text/__init__.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/text/font.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/text/pagebreak.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/text/parfmt.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/text/tabstops.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/toc.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/typing.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/scripts/docx_exec_lab.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/scripts/docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/scripts/dump_wire_fixtures.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/scripts/release.sh +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/scripts/round_trip_smoke.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/scripts/smoke_test_block_not_found.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/scripts/validate_find_replace_asset.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/__init__.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/conftest.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/README.md +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/__init__.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/ab_probe_cases.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/ab_probe_runner.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/auto_gen_cases.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/binary_round_trip.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/cases.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/complex_cases.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/coverage_report.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/extract.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/extreme_cases.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/fake_session.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/firm_templates/README.md +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/firm_templates/__init__.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/firm_templates/_runner.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/firm_templates/extractor.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/local_runner.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/mega_cases.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshot.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
- {athena_python_docx-0.29.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.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw16_header_footer_tables.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw17_header_letterhead_filled.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/ours_spec.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/parity_crawl.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/parity_diff.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/round_trip_tests.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/stock_spec.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fixtures/note_reference_reads.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fixtures/ruby_reads.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fixtures/symbol_reads.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/live/__init__.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/live/sdk_driver.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/live/stock_driver.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/README.md +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/__init__.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/baseline_gaps.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/compare.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/introspect.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/reports/gap_report.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/run_parity.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/snapshots/athena_latest.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/test_parity_gap.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_add_section.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_add_section_extract_items.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_athena_extension_creates_session_open.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_athena_extensions_contract.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_batch_chunking.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_batching_perf.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_block_not_found_error.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_buffer.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_add_table_not_supported.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_borders_spec.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_fastpath_runs.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_inner_format_stash.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_inner_run_format_stash.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_inner_run_guard.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_paragraphs_listed.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_text_plain_fastpath.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_text_replace_semantics.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_citations.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_collapsed_range_format.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_color_format_theme.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_command_dataclasses.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_commands.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_comment_paragraphs.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_comments.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_content_control_value.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_core_properties.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_document_asset_id_property.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_document_clear.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_document_create.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_document_create_from_template.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_document_factory_validation.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_docx_exec_lab.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_drawing_image.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_empty_hyperlinks.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_enum_parity.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_execution_scope.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_field_refresh.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_field_runs.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_find_replace_session_open.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_image_url_data_uri.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_inline_page_break.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_inline_shape_alt_text.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_inline_shape_size.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_inline_shape_type.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_inline_shapes_floating.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_insert_deferred.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_list_styles.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_merged_cell_secondary_slot.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_merged_cells.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_missing_modules.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_note_reference_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_oxml_shim.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_page_break.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_paragraph_runs_hyperlinks.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_paragraph_text_len_cache.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_parity_misc.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_parity_round2.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_pdf_export_and_page_renders.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_phase_a_behavior.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_phase_b_headers_footers.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_phase_c_tables.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_positional_cell_id.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_pr19766_review_fixes.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_ptc.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_python_docx_api_parity.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_read_run_ranges.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_repeated_node_read.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_restart_numbering.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_revisions.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_round_trip_smoke_script.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_ruby_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_run_add_picture_anchor.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_run_edges.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_run_formatting_wire.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_run_identity.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_run_mark_inheritance.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_run_table_style_objects.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_run_text_python_docx.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_run_text_refreshes_paragraph.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_sdk_reads_served.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_section_read_lengths.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_add_picture.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_add_run.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_comments_get.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_document_audit.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_document_element.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_enum_section.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_font_audit.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_header_footer.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_hyperlink.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_inline_shape.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_misc.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_paragraph_style.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_parfmt.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_row_col_cell.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_run_add_break.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_run_style.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_run_style_strict.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_run_text.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_run_underline.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_section_audit.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_section_dimensions.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_section_onoff.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_settings.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_shared_audit.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_style.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_styles.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_table_audit.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_table_cell.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_table_dimensions.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_table_layout.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_smoke_integration.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_style_acceptance.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_style_font.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_style_formatting_writes.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_style_metadata_writes.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_style_names.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_style_setters_contract.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_symbol_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_table_set_cell_perf.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_table_style_id_resolution.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_temporarily_unavailable.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_text_control_characters.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_uv_lock_version.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_validate_find_replace_asset_script.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_xml_attr_guard.py +0 -0
- {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_zod_wire_contract.py +0 -0
|
@@ -204,14 +204,21 @@ not file-backed. Each is documented in the relevant docstring.
|
|
|
204
204
|
are sent with `changeMode: "tracked"`, and docx-studio records their text
|
|
205
205
|
edits as Word revisions (`w:ins` / `w:del`): run and paragraph text,
|
|
206
206
|
`find_replace` in the body, new paragraphs and headings (an inserted
|
|
207
|
-
paragraph mark),
|
|
207
|
+
paragraph mark), whole paragraphs deleted (`paragraph.delete()`, 0.30.0+:
|
|
208
|
+
its content, fields included, and its paragraph mark), tabs and line breaks,
|
|
209
|
+
run formatting (`w:rPrChange`) and
|
|
208
210
|
`paragraph_format` properties (`w:pPrChange`). The replaced text stays, deleted,
|
|
209
211
|
and the new text follows it, so the SDK keeps its paragraph lengths and
|
|
210
212
|
run ranges that way (`docx/_tracked.py`): a tracked `run.text = value`
|
|
211
213
|
leaves the run over `value`, after its deleted text, and the paragraph
|
|
212
|
-
grows by `len(value)`.
|
|
213
|
-
|
|
214
|
-
|
|
214
|
+
grows by `len(value)`. `find_replace` and (0.30.0+) `paragraph.text = value`
|
|
215
|
+
mark only the words that change, the words the old and new text share at
|
|
216
|
+
their start and end left as they are, so after a tracked `paragraph.text`
|
|
217
|
+
the SDK reads the paragraph's length back. Every batch says so to docx-studio
|
|
218
|
+
(`client.capabilities: ["trackedAuthoring", "trackedMinimalParagraphText"]`);
|
|
219
|
+
a docx-studio refuses a tracked edit from an SDK that does not declare
|
|
220
|
+
`trackedAuthoring`, and replaces a paragraph's whole text for one that
|
|
221
|
+
does not declare `trackedMinimalParagraphText`. A batch holding an edit it cannot
|
|
215
222
|
record yet (paragraph styles, tables, pictures, lists, sections, notes,
|
|
216
223
|
page breaks, header/footer/comment text) is refused (HTTP 422,
|
|
217
224
|
`TrackedChangesUnsupported`) and applies nothing; reads, exports,
|
|
@@ -233,9 +240,11 @@ not file-backed. Each is documented in the relevant docstring.
|
|
|
233
240
|
|
|
234
241
|
- **`Document.user_name: str | None` / `Document.user_email: str |
|
|
235
242
|
None`** — the name sent with each batch, which DOCX Studio uses as the
|
|
236
|
-
author of a comment that names none
|
|
237
|
-
|
|
238
|
-
|
|
243
|
+
author of a comment that names none (unset, such a comment is by the
|
|
244
|
+
signed-in user, as "Jane Doe (Athena)"). It is not a tracked change's
|
|
245
|
+
author: DOCX Studio stamps those with the signed-in user agora's access
|
|
246
|
+
check names, as an agent writes for them ("Jane Doe (Athena)"), so a
|
|
247
|
+
script cannot choose who a redline's changes are by.
|
|
239
248
|
Separate from `Document.core_properties.last_modified_by`, the
|
|
240
249
|
`docProps/core.xml` metadata the SDK reads and writes as python-docx
|
|
241
250
|
does.
|
|
@@ -451,6 +460,26 @@ Issue numbers reference `python-openxml/python-docx`.
|
|
|
451
460
|
numbering reads. python-docx issue #471 (13 👍) demands access to
|
|
452
461
|
list metadata. Routes through `NumberingGet`.
|
|
453
462
|
|
|
463
|
+
- **`Paragraph.delete()`** (0.30.0+) — delete the paragraph, its
|
|
464
|
+
paragraph mark included. python-docx has no paragraph delete (issue #33);
|
|
465
|
+
code written for it removes the element
|
|
466
|
+
(`p._element.getparent().remove(p._element)`), which this SDK cannot reach.
|
|
467
|
+
Routes through `DeleteParagraph`, sent in a request of its own after the
|
|
468
|
+
buffered edits (response-bearing, so a refusal raises at the call). With
|
|
469
|
+
tracking off docx-studio removes the paragraph, with the page and column
|
|
470
|
+
breaks the import keeps beside it; with `track_revisions` on it records
|
|
471
|
+
Word's tracked deletion of a whole paragraph: its content in `w:del`
|
|
472
|
+
(cross-reference and other fields with their codes and results, links,
|
|
473
|
+
note references) and its paragraph mark deleted, one revision. Accept All
|
|
474
|
+
removes it and the numbered paragraphs after it number on; Reject All
|
|
475
|
+
restores it. `paragraph.text = ""` leaves an empty, still numbered,
|
|
476
|
+
paragraph instead. docx-studio refuses (`DocxError`, nothing applied) the
|
|
477
|
+
document's last paragraph, one that ends a section, the only paragraph of
|
|
478
|
+
a table cell or content control, one holding part of a field that spans
|
|
479
|
+
paragraphs, one in a header, footer, comment or text box, and, tracked,
|
|
480
|
+
one in a table cell or holding a page break, a content control, a shape or
|
|
481
|
+
an equation (`DOCX_STUDIO_LIMITATIONS.md` § 32).
|
|
482
|
+
|
|
454
483
|
- **`Paragraph.restart_numbering(start=1)`** (0.26.0+) — restart the
|
|
455
484
|
paragraph's numbered list at `start`, as Word's "Restart at 1" does
|
|
456
485
|
("Set Numbering Value" for another start). All `"List Number"`
|
|
@@ -648,8 +677,11 @@ Issue numbers reference `python-openxml/python-docx`.
|
|
|
648
677
|
leaves out the author, last-modified-by, revision, company, manager,
|
|
649
678
|
template, editing-time and hyperlink-base properties, rsids, document
|
|
650
679
|
variables, custom properties, Custom XML (a content control bound to it
|
|
651
|
-
keeps its content, not its binding), the attached template
|
|
652
|
-
thumbnail; ``clear_title=True`` drops the title too.
|
|
680
|
+
keeps its content, not its binding), the attached template, the attached
|
|
681
|
+
XML schema and the thumbnail; ``clear_title=True`` drops the title too.
|
|
682
|
+
Bookmarks, the creation date and the sensitivity label stay (the route's
|
|
683
|
+
``removeSensitivityLabel=1`` drops the label; the SDK does not send it).
|
|
684
|
+
The live document is
|
|
653
685
|
not changed. The SDK refuses (``DocxUnsupportedCommandError``) an answer
|
|
654
686
|
without ``X-Docx-Clean-Copy: 1``, the plain export an older docx-studio
|
|
655
687
|
sends, and raises ``DocxError`` with docx-studio's reason when a header,
|
|
@@ -954,13 +986,32 @@ architecture or a DOCX Studio constraint:
|
|
|
954
986
|
`Table.autofit` reads `False` (python-docx leaves autofit on); its columns
|
|
955
987
|
share 9,360 twips, where python-docx shares the section's text width; and
|
|
956
988
|
in the fixed layout a column width the script sets sizes the column's
|
|
957
|
-
cells too, where python-docx sets the `w:gridCol` alone.
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
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`;
|
|
961
997
|
an imported table's gaps stay gaps, and so do the two cases whose
|
|
962
998
|
`cell_format` also differs for another cause (`except`).
|
|
963
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
|
+
|
|
964
1015
|
- **`_Cell.merge` raises `InvalidSpanError` for every span that would cut
|
|
965
1016
|
through a merged cell.** It merges what python-docx merges and refuses
|
|
966
1017
|
what python-docx refuses (an L- or T-shaped pair of corners), but where
|
|
@@ -990,8 +1041,9 @@ architecture or a DOCX Studio constraint:
|
|
|
990
1041
|
`""`.** `Document.add_comment` / `Comments.add_comment` send `author`
|
|
991
1042
|
and `initials` and docx-studio stores them as `w:author` /
|
|
992
1043
|
`w:initials`, as python-docx does; with none (python-docx's default
|
|
993
|
-
`""`) the author is `Document.user_name`, else
|
|
994
|
-
|
|
1044
|
+
`""`) the author is `Document.user_name`, else the signed-in user agora's
|
|
1045
|
+
access check names as an agent writes for them, "Jane Doe (Athena)" (the
|
|
1046
|
+
author of the batch's tracked changes), else `"Athena"`, and no initials are stored (`initials` then reads `None`). The SDK raises
|
|
995
1047
|
`ValueError` for what lxml refuses in an attribute, as python-docx
|
|
996
1048
|
does, and also past docx-studio's caps (255 UTF-16 code units for an
|
|
997
1049
|
author, 32 for initials); docx-studio turns tabs, line breaks and other
|
|
@@ -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
|
|
|
@@ -4,7 +4,8 @@ Wire format (matches ``apps/api/src/commands/types.ts``):
|
|
|
4
4
|
|
|
5
5
|
POST /docs/:id/commands
|
|
6
6
|
{
|
|
7
|
-
"client": {"type": "python-sdk", "version": "<x.y.z>",
|
|
7
|
+
"client": {"type": "python-sdk", "version": "<x.y.z>",
|
|
8
|
+
"capabilities": ["trackedAuthoring", "trackedMinimalParagraphText", "tableAnchors"]},
|
|
8
9
|
"commands": [
|
|
9
10
|
{"type": "CreateParagraph", "text": "Hi", "at": {"kind": "documentEnd"}},
|
|
10
11
|
...
|
|
@@ -41,8 +42,9 @@ from requests.adapters import HTTPAdapter
|
|
|
41
42
|
from urllib3.util.retry import Retry
|
|
42
43
|
|
|
43
44
|
from docx._batching import call_abandoned, note_progress
|
|
44
|
-
from docx._buffer import CommandBuffer
|
|
45
|
-
from docx._tracked import TRACKED_AUTHORING_CAPABILITY
|
|
45
|
+
from docx._buffer import CommandBuffer, keep_reported_message
|
|
46
|
+
from docx._tracked import TRACKED_AUTHORING_CAPABILITY, TRACKED_MINIMAL_PARAGRAPH_TEXT_CAPABILITY
|
|
47
|
+
from docx._capabilities import TABLE_ANCHORS_CLIENT_CAPABILITY
|
|
46
48
|
from docx._timeouts import (
|
|
47
49
|
DEFAULT_OP_TIMEOUT_SECONDS,
|
|
48
50
|
LONG_RUNNING_COMMAND_TIMEOUT_SECONDS,
|
|
@@ -89,6 +91,7 @@ from docx.commands import (
|
|
|
89
91
|
CreateTOC,
|
|
90
92
|
CreateWatermark,
|
|
91
93
|
DeleteBookmark,
|
|
94
|
+
DeleteParagraph,
|
|
92
95
|
DeleteWatermark,
|
|
93
96
|
EndnotesDelete,
|
|
94
97
|
EndnotesGet,
|
|
@@ -192,6 +195,7 @@ from docx.errors import (
|
|
|
192
195
|
NotFoundError,
|
|
193
196
|
NotNumberedListItemError,
|
|
194
197
|
SessionError,
|
|
198
|
+
StaleTableError,
|
|
195
199
|
_missing_block_id,
|
|
196
200
|
)
|
|
197
201
|
from docx.exceptions import InvalidSpanError
|
|
@@ -282,6 +286,7 @@ def _flush_outcome(
|
|
|
282
286
|
f" applied{not_sent}.{offered} Re-read the document to see whether that"
|
|
283
287
|
" request landed before re-applying anything."
|
|
284
288
|
)
|
|
289
|
+
keep_reported_message(exc)
|
|
285
290
|
exc.args = (f"{prefix}\n\n{exc.args[0] if exc.args else ''}", *exc.args[1:])
|
|
286
291
|
return exc
|
|
287
292
|
applied = min(start + landed, total)
|
|
@@ -331,6 +336,7 @@ def _flush_outcome(
|
|
|
331
336
|
f" nothing{not_sent}. `exc.unapplied_commands` holds the {len(unapplied)}"
|
|
332
337
|
f" that did not land{which}. {redo}"
|
|
333
338
|
)
|
|
339
|
+
keep_reported_message(exc)
|
|
334
340
|
exc.args = (f"{prefix}\n\n{exc.args[0] if exc.args else ''}", *exc.args[1:])
|
|
335
341
|
return exc
|
|
336
342
|
|
|
@@ -836,6 +842,13 @@ def _http_post_json(
|
|
|
836
842
|
err_obj,
|
|
837
843
|
partial_applied,
|
|
838
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
|
+
)
|
|
839
852
|
# ``Paragraph.restart_numbering`` on a paragraph that takes no number.
|
|
840
853
|
if err_obj.get("error") == "NotNumberedListItem":
|
|
841
854
|
raise _reported(
|
|
@@ -1194,8 +1207,14 @@ class HttpClient:
|
|
|
1194
1207
|
"client": {
|
|
1195
1208
|
"type": "python-sdk",
|
|
1196
1209
|
"version": _sdk_version(),
|
|
1197
|
-
# Keeps its offsets as a tracked edit moves them
|
|
1198
|
-
|
|
1210
|
+
# Keeps its offsets as a tracked edit moves them, and reads a
|
|
1211
|
+
# paragraph's length back after a tracked ``paragraph.text``
|
|
1212
|
+
# (``docx._tracked``); holds anchored table ids (``docx.table``).
|
|
1213
|
+
"capabilities": [
|
|
1214
|
+
TRACKED_AUTHORING_CAPABILITY,
|
|
1215
|
+
TRACKED_MINIMAL_PARAGRAPH_TEXT_CAPABILITY,
|
|
1216
|
+
TABLE_ANCHORS_CLIENT_CAPABILITY,
|
|
1217
|
+
],
|
|
1199
1218
|
},
|
|
1200
1219
|
"commands": [
|
|
1201
1220
|
_with_real_ids(c.to_dict(), ids) if ids else c.to_dict() for c in commands
|
|
@@ -1475,6 +1494,7 @@ _OP_TO_COMMAND: dict[str, type[Command]] = {
|
|
|
1475
1494
|
"sections.set_columns": SetSectionColumns,
|
|
1476
1495
|
"find_replace": FindReplace,
|
|
1477
1496
|
"clear_body": ClearBody,
|
|
1497
|
+
"delete_paragraph": DeleteParagraph,
|
|
1478
1498
|
"find_text": FindText,
|
|
1479
1499
|
"iter_runs": IterRuns,
|
|
1480
1500
|
"export_pdf": ExportPDF,
|