athena-python-docx 0.26.0__tar.gz → 0.27.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.26.0 → athena_python_docx-0.27.0}/AGENTS.md +43 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/PKG-INFO +1 -1
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/__init__.py +1 -1
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/errors.py +10 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/text/paragraph.py +94 -10
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/text/run.py +142 -1
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/pyproject.toml +1 -1
- athena_python_docx-0.27.0/tests/fixtures/field_reads.json +863 -0
- athena_python_docx-0.27.0/tests/test_field_reads_python_docx_parity.py +105 -0
- athena_python_docx-0.27.0/tests/test_field_runs.py +386 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_revisions.py +38 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/uv.lock +1 -1
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/.gitignore +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/CLAUDE.md +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/DOCX_EXEC_LAB.md +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/README.md +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_athena_extension.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_batching.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_buffer.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_citations.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_execution.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_http.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_http_doc.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_image_utils.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_postproc.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_ptc.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_references.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_run_order.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_table_styles.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_timeouts.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/api.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/blkcntnr.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/bookmarks.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/charts.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/client.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/commands.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/comments.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/document.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/drawing/__init__.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/enum/__init__.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/enum/base.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/enum/dml.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/enum/section.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/enum/shape.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/enum/style.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/enum/table.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/enum/text.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/exceptions.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/fields.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/footnotes.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/image/__init__.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/image/exceptions.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/image/image.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/math.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/opc/__init__.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/opc/coreprops.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/oxml/__init__.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/revisions.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/sdt.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/section.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/session.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/settings.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/shape.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/shared.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/styles/__init__.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/styles/_table.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/styles/latent.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/styles/style.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/styles/styles.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/table.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/text/__init__.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/text/font.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/text/hyperlink.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/text/pagebreak.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/text/parfmt.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/text/tabstops.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/toc.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/typing.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/scripts/docx_exec_lab.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/scripts/docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/scripts/dump_wire_fixtures.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/scripts/release.sh +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/scripts/round_trip_smoke.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/scripts/smoke_test_block_not_found.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/scripts/validate_find_replace_asset.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/__init__.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/conftest.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/METHODOLOGY.md +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/README.md +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/__init__.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/ab_probe_cases.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/ab_probe_runner.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/auto_gen_cases.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/binary_round_trip.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/cases.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/complex_cases.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/coverage_report.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/extract.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/extreme_cases.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/fake_session.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/firm_templates/README.md +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/firm_templates/__init__.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/firm_templates/_runner.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/firm_templates/extractor.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/local_runner.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/mega_cases.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshot.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/ours_spec.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/parity_crawl.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/parity_diff.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/real_world_cases.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/round_trip_tests.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/stock_spec.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/README.md +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/__init__.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/baseline_gaps.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/compare.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/intentional_deviations.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/introspect.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/reports/gap_report.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/run_parity.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/snapshots/athena_latest.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/test_parity_gap.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_add_section.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_add_section_extract_items.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_athena_extension_creates_session_open.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_athena_extensions_contract.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_athena_extensions_registry.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_batching_perf.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_block_not_found_error.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_buffer.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_add_table_not_supported.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_borders_spec.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_fastpath_runs.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_inner_format_stash.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_inner_run_format_stash.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_inner_run_guard.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_merge.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_paragraphs_listed.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_text_plain_fastpath.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_text_replace_semantics.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_citations.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_collapsed_range_format.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_color_format_theme.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_command_dataclasses.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_commands.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_comments.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_core_properties.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_document_asset_id_property.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_document_clear.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_document_create.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_document_create_from_template.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_document_factory_validation.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_docx_exec_lab.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_drawing_image.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_enum_parity.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_execution_scope.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_find_replace_session_open.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_http_transport.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_hyperlink_coalescing.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_image_url_data_uri.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_inline_shape_alt_text.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_inline_shape_size.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_insert_deferred.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_iter_inner_content.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_list_styles.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_merged_cell_secondary_slot.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_merged_cells.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_missing_modules.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_oxml_shim.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_page_break.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_paragraph_runs_hyperlinks.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_paragraph_text_len_cache.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_parity_misc.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_parity_round2.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_pdf_export_and_page_renders.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_phase_a_behavior.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_phase_b_headers_footers.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_phase_c_tables.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_positional_cell_id.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_postproc_ref_restore.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_pr19766_review_fixes.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_ptc.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_python_docx_api_parity.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_restart_numbering.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_run_add_picture_anchor.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_run_formatting_wire.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_run_identity.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_run_mark_inheritance.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_run_table_style_objects.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_run_text_python_docx.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_run_text_refreshes_paragraph.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_sdk_reads_served.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_section_read_lengths.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_add_picture.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_add_run.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_comments_get.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_document_audit.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_document_element.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_enum_section.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_font_audit.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_header_footer.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_hyperlink.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_inline_shape.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_misc.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_paragraph_style.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_parfmt.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_row_col_cell.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_run_add_break.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_run_style.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_run_style_strict.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_run_text.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_run_underline.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_section_audit.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_section_dimensions.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_section_onoff.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_settings.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_shared_audit.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_style.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_styles.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_table_audit.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_table_cell.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_table_dimensions.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_table_layout.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_smoke_integration.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_style_acceptance.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_style_font.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_style_formatting_writes.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_style_metadata_writes.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_style_names.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_style_setters_contract.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_table_set_cell_perf.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_table_style_id_resolution.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_temporarily_unavailable.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_text_control_characters.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_uv_lock_version.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_validate_find_replace_asset_script.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_wire_contract.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_xml_attr_guard.py +0 -0
- {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_zod_wire_contract.py +0 -0
|
@@ -224,6 +224,11 @@ not file-backed. Each is documented in the relevant docstring.
|
|
|
224
224
|
`Revision.reject()` resolve a single change. python-docx upstream
|
|
225
225
|
has zero track-changes API, so this entire surface is an Athena
|
|
226
226
|
extension wired to DOCX Studio's `doc.trackChanges.{list,get,decide}`.
|
|
227
|
+
A run's tracked formatting change (`w:rPrChange`) and a paragraph's
|
|
228
|
+
(`w:pPrChange`) are `"format"` revisions; `reject()` gives the run or
|
|
229
|
+
paragraph exactly the earlier properties the change recorded
|
|
230
|
+
(`tests/test_revisions.py`, docx-studio's
|
|
231
|
+
`tracked-run-property-changes.test.ts`).
|
|
227
232
|
|
|
228
233
|
- **Style writes refuse what would write a broken document.** python-docx
|
|
229
234
|
writes these into `styles.xml`; the SDK raises before anything is sent,
|
|
@@ -968,6 +973,44 @@ architecture or a DOCX Studio constraint:
|
|
|
968
973
|
Markup) show insertions and hide deletions, as these reads do.
|
|
969
974
|
`doc.revisions` lists every change (`tests/test_run_text_python_docx.py`).
|
|
970
975
|
|
|
976
|
+
- **A field's runs read as python-docx reads them; some writes to them
|
|
977
|
+
differ.** `Paragraph.text` includes a complex field's displayed result and
|
|
978
|
+
`Paragraph.runs` holds its begin, field code, separate, result and end runs
|
|
979
|
+
(each with its own formatting), as python-docx reads the `w:r` children of
|
|
980
|
+
a `w:p`; a simple field (`w:fldSimple`) reads as nothing, and a field in a
|
|
981
|
+
link is the link's, as in python-docx. Each paragraph of a field that spans
|
|
982
|
+
paragraphs (a table of contents) holds the field character runs it has
|
|
983
|
+
(`tests/test_field_reads_python_docx_parity.py`, against stock python-docx
|
|
984
|
+
on the same `.docx`; `DOCX_STUDIO_LIMITATIONS.md` § 27). A field takes no
|
|
985
|
+
room in the text-address space, so its runs stand at one offset. Writes:
|
|
986
|
+
- setting a result run's `.text` changes the field's displayed result,
|
|
987
|
+
as in python-docx (`Replace` with a `field` target), but the export
|
|
988
|
+
writes a changed result of several runs as one run, in the first run's
|
|
989
|
+
formatting, and a result run read before the field's result changed
|
|
990
|
+
(through another read's run, or by a collaborator) raises
|
|
991
|
+
`FieldRunWriteError` rather than write over that change, where
|
|
992
|
+
python-docx's run is live;
|
|
993
|
+
- formatting a result run formats the field's whole result (`FormatApply`
|
|
994
|
+
with a `field` target), from its source formatting, where python-docx
|
|
995
|
+
formats that run alone;
|
|
996
|
+
- `.text` (and `clear()`) on a field character or field code run raises
|
|
997
|
+
`docx.errors.FieldRunWriteError` naming the field: python-docx replaces
|
|
998
|
+
the run's `w:fldChar` or `w:instrText` with the text, which breaks the
|
|
999
|
+
field;
|
|
1000
|
+
- formatting set on a field character or field code run is not applied and
|
|
1001
|
+
emits `PendingFieldRunFormatWarning` (those runs show nothing; docx-studio
|
|
1002
|
+
has no command that writes their own `w:rPr`), where python-docx writes
|
|
1003
|
+
it, so `for run in p.runs: run.bold = True` formats the text and the
|
|
1004
|
+
field's result and warns for the rest;
|
|
1005
|
+
- `add_text`, `add_tab`, `add_break`, `add_picture`, `add_field`,
|
|
1006
|
+
`add_footnote`, `add_endnote`, `add_math` and `add_cross_reference` on
|
|
1007
|
+
any of a field's runs raise `FieldRunWriteError`; python-docx appends to
|
|
1008
|
+
that `w:r`.
|
|
1009
|
+
A field that `Run.add_field` adds is a simple field, so it reads as no
|
|
1010
|
+
runs and no text, though docx-studio's export writes it as a complex field.
|
|
1011
|
+
Against a docx-studio that predates `fieldRun` reads, a field reads as no
|
|
1012
|
+
runs and no text, as before (`tests/test_field_runs.py`).
|
|
1013
|
+
|
|
971
1014
|
### If you need a deviation
|
|
972
1015
|
|
|
973
1016
|
If there is a genuine technical reason why a deviation from python-docx is necessary:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: athena-python-docx
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.27.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>
|
|
@@ -129,6 +129,16 @@ class NotNumberedListItemError(DocxError, ValueError):
|
|
|
129
129
|
"""
|
|
130
130
|
|
|
131
131
|
|
|
132
|
+
class FieldRunWriteError(DocxError, ValueError):
|
|
133
|
+
"""Raised for a write to a run of a Word field that the SDK refuses:
|
|
134
|
+
text written to a field character or field code run (python-docx's write
|
|
135
|
+
replaces the run's ``w:fldChar`` or ``w:instrText`` and breaks the field),
|
|
136
|
+
and content appended to any of a field's runs. The message names the
|
|
137
|
+
field. Subclasses :class:`ValueError` so ``except ValueError`` also
|
|
138
|
+
catches it.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
|
|
132
142
|
class SessionError(DocxError):
|
|
133
143
|
"""Raised when the DOCX Studio SDK session cannot be established or used."""
|
|
134
144
|
|
|
@@ -199,8 +199,10 @@ def _extract_text(info: object, *, final: bool = False) -> str:
|
|
|
199
199
|
returns: tracked changes as Word's final view shows them, a deletion's
|
|
200
200
|
runs left out and an insertion's read (python-docx leaves out both, as
|
|
201
201
|
it reads only a ``w:p``'s direct ``w:r`` and ``w:hyperlink`` children),
|
|
202
|
-
|
|
203
|
-
(:func:`_word_text`)
|
|
202
|
+
each run's text read as python-docx's ``Run.text`` reads it
|
|
203
|
+
(:func:`_word_text`), and a complex field's displayed result (the text of
|
|
204
|
+
its result runs, never its field code), which takes no room in the
|
|
205
|
+
text-address space.
|
|
204
206
|
"""
|
|
205
207
|
if not isinstance(info, dict):
|
|
206
208
|
return ""
|
|
@@ -217,6 +219,12 @@ def _extract_text(info: object, *, final: bool = False) -> str:
|
|
|
217
219
|
for inline in inlines:
|
|
218
220
|
if not isinstance(inline, dict):
|
|
219
221
|
continue
|
|
222
|
+
field_run: object = inline.get("fieldRun")
|
|
223
|
+
if isinstance(field_run, dict):
|
|
224
|
+
shown: object = field_run.get("text")
|
|
225
|
+
if final and isinstance(shown, str) and not _deleted(field_run):
|
|
226
|
+
parts.append(_word_text(shown, _symbol_font(field_run)))
|
|
227
|
+
continue
|
|
220
228
|
run: object = inline.get("run")
|
|
221
229
|
if isinstance(run, dict):
|
|
222
230
|
text: object = run.get("text")
|
|
@@ -273,6 +281,61 @@ def _walk_inlines(info: object) -> list[dict]:
|
|
|
273
281
|
return []
|
|
274
282
|
|
|
275
283
|
|
|
284
|
+
def _inline_width(inline: dict) -> int:
|
|
285
|
+
"""The room an inline takes in the text-address space (see ``runs``)."""
|
|
286
|
+
run: object = inline.get("run")
|
|
287
|
+
if isinstance(run, dict):
|
|
288
|
+
text: object = run.get("text")
|
|
289
|
+
return len(text) if isinstance(text, str) else 0
|
|
290
|
+
kind: object = inline.get("type")
|
|
291
|
+
if kind == "text":
|
|
292
|
+
flat: object = inline.get("text")
|
|
293
|
+
return len(flat) if isinstance(flat, str) else 0
|
|
294
|
+
return 1 if kind in ("tab", "lineBreak") else 0
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def _field_run_proxies(
|
|
298
|
+
paragraph: "Paragraph", inlines: list[dict], *, marks_known: bool
|
|
299
|
+
) -> dict[int, "Run"]:
|
|
300
|
+
"""The :class:`Run` of each field run of ``inlines`` (a ``fieldRun``
|
|
301
|
+
read), by its index there: a field's begin, code, separate, result or end
|
|
302
|
+
run, which python-docx reads as a run of the paragraph. A field takes no
|
|
303
|
+
room in the text-address space, so each stands at one offset. All are built
|
|
304
|
+
before any is handed out, so each result run of a field holds all of them
|
|
305
|
+
(``_field_results``) and setting one's text keeps the others'. A run of a
|
|
306
|
+
tracked deletion has none, as Word's final view shows it.
|
|
307
|
+
"""
|
|
308
|
+
from docx.text.run import Run
|
|
309
|
+
|
|
310
|
+
proxies: dict[int, Run] = {}
|
|
311
|
+
results: dict[int, list[Run]] = {}
|
|
312
|
+
offset = 0
|
|
313
|
+
for position, inline in enumerate(inlines):
|
|
314
|
+
field_run: object = inline.get("fieldRun")
|
|
315
|
+
if not isinstance(field_run, dict):
|
|
316
|
+
offset += _inline_width(inline)
|
|
317
|
+
continue
|
|
318
|
+
if _deleted(field_run):
|
|
319
|
+
continue
|
|
320
|
+
run = Run(
|
|
321
|
+
session=paragraph._session,
|
|
322
|
+
block_id=paragraph._node_id,
|
|
323
|
+
range_=(offset, offset),
|
|
324
|
+
inline=field_run,
|
|
325
|
+
paragraph=paragraph,
|
|
326
|
+
in_cell=paragraph._in_cell,
|
|
327
|
+
cell_ooxml_address=paragraph._cell_ooxml_address,
|
|
328
|
+
)
|
|
329
|
+
run._marks_known = marks_known
|
|
330
|
+
index: object = field_run.get("fieldIndex")
|
|
331
|
+
if field_run.get("part") == "result" and isinstance(index, int):
|
|
332
|
+
siblings = results.setdefault(index, [])
|
|
333
|
+
siblings.append(run)
|
|
334
|
+
run._field_results = siblings # type: ignore[attr-defined]
|
|
335
|
+
proxies[position] = run
|
|
336
|
+
return proxies
|
|
337
|
+
|
|
338
|
+
|
|
276
339
|
def _create_page_break_paragraph(session: Any, client_node_id: str, *, at: dict) -> None:
|
|
277
340
|
"""Create, at ``at``, the paragraph python-docx writes for a page break:
|
|
278
341
|
``<w:p><w:r><w:br w:type="page"/></w:r></w:p>``.
|
|
@@ -505,9 +568,10 @@ class Paragraph:
|
|
|
505
568
|
def text(self) -> str:
|
|
506
569
|
"""The paragraph's text: its runs' and hyperlinks' text as
|
|
507
570
|
python-docx's ``Paragraph.text`` reads it (a non-breaking hyphen as
|
|
508
|
-
``-``, a soft hyphen and a symbol left out
|
|
509
|
-
|
|
510
|
-
|
|
571
|
+
``-``, a soft hyphen and a symbol left out, a complex field's displayed
|
|
572
|
+
result in and its field code out, a simple field's result out), with
|
|
573
|
+
tracked changes as Word's final view shows them: an insertion's text
|
|
574
|
+
in, a deletion's out."""
|
|
511
575
|
return _extract_text(self._node_info(), final=True)
|
|
512
576
|
|
|
513
577
|
@text.setter
|
|
@@ -565,13 +629,19 @@ class Paragraph:
|
|
|
565
629
|
As in python-docx, these are the paragraph's own runs: the runs of a
|
|
566
630
|
hyperlink are the hyperlink's (:attr:`hyperlinks`,
|
|
567
631
|
:meth:`iter_inner_content`), while :attr:`text` includes its text.
|
|
632
|
+
A complex field's runs are the paragraph's runs, in order: its begin,
|
|
633
|
+
field code and separate runs (``""``), its result runs with their
|
|
634
|
+
text, and its end run (``""``), each with its own formatting; so are
|
|
635
|
+
the field character runs of a field that begins or ends in another
|
|
636
|
+
paragraph (a table of contents). A simple field (``w:fldSimple``) has
|
|
637
|
+
no runs here, as in python-docx.
|
|
568
638
|
|
|
569
639
|
Run offsets are computed against DOCX Studio's text-address space,
|
|
570
640
|
which counts tab and lineBreak inlines as 1 character each
|
|
571
641
|
(so ``insert_tab({offset})`` and the run-range arithmetic stay
|
|
572
|
-
consistent). A
|
|
573
|
-
paragraph's, as Word's final view shows it,
|
|
574
|
-
a tracked insertion's runs are runs.
|
|
642
|
+
consistent). A field's runs take no room in it. A run of a tracked
|
|
643
|
+
deletion is no run of the paragraph's, as Word's final view shows it,
|
|
644
|
+
but keeps its offsets; a tracked insertion's runs are runs.
|
|
575
645
|
"""
|
|
576
646
|
from docx.text.run import Run
|
|
577
647
|
|
|
@@ -602,8 +672,13 @@ class Paragraph:
|
|
|
602
672
|
inlines: list[dict] = _walk_inlines(info)
|
|
603
673
|
marks_known: bool = _reports_run_marks(info)
|
|
604
674
|
runs: list["Run"] = []
|
|
675
|
+
field_runs = _field_run_proxies(self, inlines, marks_known=marks_known)
|
|
605
676
|
offset: int = 0
|
|
606
|
-
for inline in inlines:
|
|
677
|
+
for position, inline in enumerate(inlines):
|
|
678
|
+
if isinstance(inline.get("fieldRun"), dict):
|
|
679
|
+
if position in field_runs:
|
|
680
|
+
runs.append(field_runs[position])
|
|
681
|
+
continue
|
|
607
682
|
drawing: object = inline.get("drawing")
|
|
608
683
|
if isinstance(drawing, dict):
|
|
609
684
|
# A picture: python-docx's run holding a ``w:drawing``. It
|
|
@@ -1012,6 +1087,7 @@ class Paragraph:
|
|
|
1012
1087
|
self._session.doc.get_node_by_id({"id": self._node_id}),
|
|
1013
1088
|
)
|
|
1014
1089
|
inlines = _walk_inlines(info)
|
|
1090
|
+
field_runs = _field_run_proxies(self, inlines, marks_known=_reports_run_marks(info))
|
|
1015
1091
|
offset: int = 0
|
|
1016
1092
|
# Accumulator for the in-flight hyperlink span. Mirrors the
|
|
1017
1093
|
# coalesce logic in ``Paragraph.hyperlinks`` — yields a single
|
|
@@ -1032,9 +1108,17 @@ class Paragraph:
|
|
|
1032
1108
|
segments=segments,
|
|
1033
1109
|
)
|
|
1034
1110
|
|
|
1035
|
-
for inline in inlines:
|
|
1111
|
+
for position, inline in enumerate(inlines):
|
|
1036
1112
|
if not isinstance(inline, dict):
|
|
1037
1113
|
continue
|
|
1114
|
+
if isinstance(inline.get("fieldRun"), dict):
|
|
1115
|
+
# A field's ``w:r`` is a run of the paragraph, never of a link.
|
|
1116
|
+
flushed = _flush_current()
|
|
1117
|
+
if flushed is not None:
|
|
1118
|
+
yield flushed
|
|
1119
|
+
if position in field_runs:
|
|
1120
|
+
yield field_runs[position]
|
|
1121
|
+
continue
|
|
1038
1122
|
# Two DOCX Studio inline shapes are accepted by the rest of the
|
|
1039
1123
|
# paragraph code (``_extract_text``, ``runs``):
|
|
1040
1124
|
# 1. typed-wrapper: {"run": {"text": "...", ...}}
|
|
@@ -70,6 +70,20 @@ class PendingColorTypeWarning(UserWarning):
|
|
|
70
70
|
``theme_color`` without a warning."""
|
|
71
71
|
|
|
72
72
|
|
|
73
|
+
class PendingFieldRunFormatWarning(UserWarning):
|
|
74
|
+
"""Emitted when formatting is set on a field character or field code run
|
|
75
|
+
(a field's begin, code, separate or end run). python-docx writes it into
|
|
76
|
+
that run's ``w:rPr``, which shows nothing; docx-studio keeps those runs'
|
|
77
|
+
own properties from the source and has no command that writes them, so
|
|
78
|
+
the setter changes nothing. Formatting a result run formats the field's
|
|
79
|
+
result.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
#: The parts of a field whose runs ``Paragraph.runs`` reads (``fieldRun`` reads).
|
|
84
|
+
_FIELD_RUN_PARTS: frozenset[str] = frozenset({"begin", "code", "separate", "result", "end"})
|
|
85
|
+
|
|
86
|
+
|
|
73
87
|
if TYPE_CHECKING:
|
|
74
88
|
from docx.client import Session
|
|
75
89
|
from docx.enum.dml import MSO_COLOR_TYPE, MSO_THEME_COLOR
|
|
@@ -399,6 +413,114 @@ class Run:
|
|
|
399
413
|
self._note_paragraph_len_delta(delta)
|
|
400
414
|
note_length_change(self, at, delta, replaced=replaced, before=_before_image(sent))
|
|
401
415
|
|
|
416
|
+
def _field_part(self) -> str | None:
|
|
417
|
+
"""The part of a field this run is (``begin``, ``code``, ``separate``,
|
|
418
|
+
``result`` or ``end``), for a run ``Paragraph.runs`` read from a field;
|
|
419
|
+
else None."""
|
|
420
|
+
part: object = getattr(self, "_inline_snapshot", {}).get("part")
|
|
421
|
+
return part if isinstance(part, str) and part in _FIELD_RUN_PARTS else None
|
|
422
|
+
|
|
423
|
+
def _field_name(self) -> str:
|
|
424
|
+
instruction: object = self._inline_snapshot.get("instruction")
|
|
425
|
+
if isinstance(instruction, str) and instruction:
|
|
426
|
+
return f"the field {{{instruction}}}"
|
|
427
|
+
return "a field that begins or ends in another paragraph"
|
|
428
|
+
|
|
429
|
+
def _refuse_field_write(self, op: str) -> None:
|
|
430
|
+
"""Raise :class:`docx.errors.FieldRunWriteError` for ``op`` on a run of a field."""
|
|
431
|
+
part = self._field_part()
|
|
432
|
+
if part is None:
|
|
433
|
+
return
|
|
434
|
+
from docx.errors import FieldRunWriteError
|
|
435
|
+
|
|
436
|
+
if op == "text":
|
|
437
|
+
raise FieldRunWriteError(
|
|
438
|
+
f"Run.text = ... on the {part} run of {self._field_name()}: python-docx "
|
|
439
|
+
"replaces the run's field character or field code with the text, which "
|
|
440
|
+
"breaks the field. Set the text of the field's result run to change "
|
|
441
|
+
"what it shows.",
|
|
442
|
+
)
|
|
443
|
+
raise FieldRunWriteError(
|
|
444
|
+
f"Run.{op} on the {part} run of {self._field_name()}: content cannot be "
|
|
445
|
+
"added to a field's run here. Add a run after the field "
|
|
446
|
+
"(Paragraph.add_run), or set a result run's text.",
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
def _field_target(self) -> dict | None:
|
|
450
|
+
"""The ``field`` target of this run's field node, for a result run."""
|
|
451
|
+
index: object = self._inline_snapshot.get("fieldIndex")
|
|
452
|
+
if self._field_part() != "result" or not isinstance(index, int):
|
|
453
|
+
return None
|
|
454
|
+
return {"kind": "field", "blockId": self._block_id, "field": index}
|
|
455
|
+
|
|
456
|
+
def _set_field_text(self, value: str) -> None:
|
|
457
|
+
"""``Run.text = value`` on a result run: the field shows ``value`` in
|
|
458
|
+
this run's place, its other result runs' text kept, and its field code
|
|
459
|
+
stays, as python-docx writes it.
|
|
460
|
+
|
|
461
|
+
The field's result is written whole, so it is composed from the result
|
|
462
|
+
runs of this run's read only while the field still shows what they
|
|
463
|
+
hold: a field another read's run (or a collaborator) changed since
|
|
464
|
+
raises :class:`docx.errors.FieldRunWriteError` instead of losing that
|
|
465
|
+
change.
|
|
466
|
+
"""
|
|
467
|
+
target = self._field_target()
|
|
468
|
+
if target is None:
|
|
469
|
+
self._refuse_field_write("text")
|
|
470
|
+
return
|
|
471
|
+
siblings: list[Run] = getattr(self, "_field_results", None) or [self]
|
|
472
|
+
live: object = run_sync(self._session.doc.get_node_by_id({"id": self._block_id}))
|
|
473
|
+
if self._shown_result(live) != "".join(run._raw_text() for run in siblings):
|
|
474
|
+
from docx.errors import FieldRunWriteError
|
|
475
|
+
|
|
476
|
+
raise FieldRunWriteError(
|
|
477
|
+
f"Run.text = ... on a result run of {self._field_name()}: the field's "
|
|
478
|
+
"result changed since this run was read. Read the paragraph's runs "
|
|
479
|
+
"again and set the text of a run from that read.",
|
|
480
|
+
)
|
|
481
|
+
shown = "".join(value if run is self else run._raw_text() for run in siblings)
|
|
482
|
+
run_sync(self._session.doc.replace({"target": target, "text": shown}))
|
|
483
|
+
self._inline_snapshot["text"] = value
|
|
484
|
+
self._note_paragraph_len_delta(0)
|
|
485
|
+
|
|
486
|
+
def _shown_result(self, info: object) -> str:
|
|
487
|
+
"""The displayed result a paragraph read gives this run's field."""
|
|
488
|
+
from docx.text.paragraph import _walk_inlines
|
|
489
|
+
|
|
490
|
+
index: object = self._inline_snapshot.get("fieldIndex")
|
|
491
|
+
parts: list[str] = []
|
|
492
|
+
for inline in _walk_inlines(info):
|
|
493
|
+
field_run: object = inline.get("fieldRun")
|
|
494
|
+
if (
|
|
495
|
+
isinstance(field_run, dict)
|
|
496
|
+
and field_run.get("part") == "result"
|
|
497
|
+
and field_run.get("fieldIndex") == index
|
|
498
|
+
):
|
|
499
|
+
text: object = field_run.get("text")
|
|
500
|
+
parts.append(text if isinstance(text, str) else "")
|
|
501
|
+
return "".join(parts)
|
|
502
|
+
|
|
503
|
+
def _format_field(self, kwargs: dict) -> None:
|
|
504
|
+
"""Formatting set on a run of a field: a result run's formats the
|
|
505
|
+
field node, so every result run of the field takes it."""
|
|
506
|
+
target = self._field_target()
|
|
507
|
+
if target is None:
|
|
508
|
+
import warnings
|
|
509
|
+
|
|
510
|
+
warnings.warn(
|
|
511
|
+
f"Run formatting on the {self._field_part()} run of {self._field_name()} "
|
|
512
|
+
"was not applied: docx-studio keeps the field character and field code "
|
|
513
|
+
"runs' own properties from the source and cannot write them (they show "
|
|
514
|
+
"nothing). Format a result run to format the field's result.",
|
|
515
|
+
PendingFieldRunFormatWarning,
|
|
516
|
+
stacklevel=4,
|
|
517
|
+
)
|
|
518
|
+
return
|
|
519
|
+
run_sync(self._session.doc.format.apply({"target": target, "inline": kwargs}))
|
|
520
|
+
for run in getattr(self, "_field_results", None) or [self]:
|
|
521
|
+
run._note_inline_applied(kwargs)
|
|
522
|
+
self._note_paragraph_len_delta(0)
|
|
523
|
+
|
|
402
524
|
@property
|
|
403
525
|
def _text_range(self) -> dict:
|
|
404
526
|
return {
|
|
@@ -449,7 +571,11 @@ class Run:
|
|
|
449
571
|
return self._inline_snapshot.get("symbolFont") or self._read_inline_attr("fontFamily")
|
|
450
572
|
|
|
451
573
|
def _raw_text(self) -> str:
|
|
452
|
-
"""The run's slice of its paragraph's text-address space
|
|
574
|
+
"""The run's slice of its paragraph's text-address space; for a run of
|
|
575
|
+
a field, which takes none, the text its read holds (a result run's)."""
|
|
576
|
+
if self._field_part() is not None:
|
|
577
|
+
shown: object = self._inline_snapshot.get("text")
|
|
578
|
+
return shown if isinstance(shown, str) else ""
|
|
453
579
|
# Fast-path runs (see ``Table._fastpath_cell_text``) carry their
|
|
454
580
|
# known text — the whole point is zero read round-trips. Mutations
|
|
455
581
|
# through this proxy keep the attr in sync or clear it.
|
|
@@ -525,6 +651,9 @@ class Run:
|
|
|
525
651
|
f"Run.text requires str; got {type(value).__name__} "
|
|
526
652
|
f"{value!r}",
|
|
527
653
|
)
|
|
654
|
+
if self._field_part() is not None:
|
|
655
|
+
self._set_field_text(value)
|
|
656
|
+
return
|
|
528
657
|
if self._check_cell_inner("text"):
|
|
529
658
|
# Warn-and-skip: nothing mutated, so the fast-path text cache
|
|
530
659
|
# (when present) still reflects reality — keep it.
|
|
@@ -594,6 +723,7 @@ class Run:
|
|
|
594
723
|
f"Run.add_text requires str; got {type(text).__name__} "
|
|
595
724
|
f"{text!r}",
|
|
596
725
|
)
|
|
726
|
+
self._refuse_field_write("add_text")
|
|
597
727
|
if self._check_cell_inner("add_text"):
|
|
598
728
|
# Return a stub Run with the same in_cell flag so chained
|
|
599
729
|
# ops on the result also short-circuit cleanly. Propagate
|
|
@@ -678,6 +808,7 @@ class Run:
|
|
|
678
808
|
|
|
679
809
|
def add_tab(self) -> None:
|
|
680
810
|
"""Insert a tab character at the end of the run, in its formatting."""
|
|
811
|
+
self._refuse_field_write("add_tab")
|
|
681
812
|
if self._check_cell_inner("add_tab"):
|
|
682
813
|
return
|
|
683
814
|
end: int = self._range[1]
|
|
@@ -721,6 +852,7 @@ class Run:
|
|
|
721
852
|
"""
|
|
722
853
|
import warnings
|
|
723
854
|
|
|
855
|
+
self._refuse_field_write("add_break")
|
|
724
856
|
if self._check_cell_inner("add_break"):
|
|
725
857
|
return
|
|
726
858
|
end: int = self._range[1]
|
|
@@ -843,6 +975,7 @@ class Run:
|
|
|
843
975
|
from docx.shape import InlineShape
|
|
844
976
|
import base64
|
|
845
977
|
|
|
978
|
+
self._refuse_field_write("add_picture")
|
|
846
979
|
image_bytes, _fallback_path = load_image_bytes(image_path_or_stream)
|
|
847
980
|
# The image's signature, as python-docx reads it: a stream with none
|
|
848
981
|
# (an empty one, or one read to its end already) raises
|
|
@@ -918,6 +1051,7 @@ class Run:
|
|
|
918
1051
|
from docx.commands import CreateField
|
|
919
1052
|
from docx.fields import Field
|
|
920
1053
|
|
|
1054
|
+
self._refuse_field_write("add_field")
|
|
921
1055
|
if self._check_cell_inner("add_field"):
|
|
922
1056
|
return Field(
|
|
923
1057
|
session=self._session,
|
|
@@ -1000,6 +1134,7 @@ class Run:
|
|
|
1000
1134
|
from docx.commands import CreateFootnote
|
|
1001
1135
|
from docx.footnotes import Footnote
|
|
1002
1136
|
|
|
1137
|
+
self._refuse_field_write("add_footnote")
|
|
1003
1138
|
end: int = self._range[1]
|
|
1004
1139
|
cursor: dict = {
|
|
1005
1140
|
"kind": "text",
|
|
@@ -1036,6 +1171,7 @@ class Run:
|
|
|
1036
1171
|
from docx.commands import CreateEndnote
|
|
1037
1172
|
from docx.footnotes import Endnote
|
|
1038
1173
|
|
|
1174
|
+
self._refuse_field_write("add_endnote")
|
|
1039
1175
|
end: int = self._range[1]
|
|
1040
1176
|
cursor: dict = {
|
|
1041
1177
|
"kind": "text",
|
|
@@ -1086,6 +1222,7 @@ class Run:
|
|
|
1086
1222
|
from docx.commands import CreateCrossReference
|
|
1087
1223
|
from docx.fields import Field
|
|
1088
1224
|
|
|
1225
|
+
self._refuse_field_write("add_cross_reference")
|
|
1089
1226
|
end: int = self._range[1]
|
|
1090
1227
|
cursor: dict = {
|
|
1091
1228
|
"kind": "text",
|
|
@@ -1132,6 +1269,7 @@ class Run:
|
|
|
1132
1269
|
from docx.commands import CreateMath
|
|
1133
1270
|
from docx.math import MathEquation
|
|
1134
1271
|
|
|
1272
|
+
self._refuse_field_write("add_math")
|
|
1135
1273
|
end: int = self._range[1]
|
|
1136
1274
|
cursor: dict = {
|
|
1137
1275
|
"kind": "text",
|
|
@@ -1298,6 +1436,9 @@ class Run:
|
|
|
1298
1436
|
# target range.`` Stash the attrs and apply them once the run
|
|
1299
1437
|
# has text — see ``_pending_inline`` in ``__init__`` for the
|
|
1300
1438
|
# python-docx parity rationale.
|
|
1439
|
+
if self._field_part() is not None:
|
|
1440
|
+
self._format_field(kwargs)
|
|
1441
|
+
return
|
|
1301
1442
|
if getattr(self, "_in_cell", False):
|
|
1302
1443
|
# Stash for the post-export OOXML rewrite FIRST so the formatting
|
|
1303
1444
|
# lands in the exported ``.docx`` regardless of engine (and is the
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "athena-python-docx"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.27.0"
|
|
8
8
|
description = "Drop-in replacement for python-docx that connects to Athena's DOCX Studio collaborative document stack"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|