athena-python-docx 0.40.0__tar.gz → 0.42.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.40.0 → athena_python_docx-0.42.0}/AGENTS.md +91 -19
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/PKG-INFO +1 -1
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/__init__.py +1 -1
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/comments.py +62 -1
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/styles/_table.py +67 -7
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/text/paragraph.py +41 -11
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/pyproject.toml +1 -1
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/fake_session.py +5 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/live/fixture_scripts.py +359 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/parity/intentional_deviations.json +47 -7
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_comments.py +132 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_hyperlink_coalescing.py +43 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_rendered_page_breaks.py +24 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_run_table_style_objects.py +58 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/uv.lock +1 -1
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/.gitignore +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/CLAUDE.md +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/DOCX_EXEC_LAB.md +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/README.md +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/_athena_extension.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/_batching.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/_buffer.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/_capabilities.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/_citations.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/_execution.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/_http.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/_http_doc.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/_image_utils.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/_postproc.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/_proxy.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/_ptc.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/_references.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/_run_order.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/_table_styles.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/_timeouts.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/_tracked.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/_xml_text.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/api.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/blkcntnr.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/bookmarks.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/charts.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/client.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/commands.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/document.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/drawing/__init__.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/enum/__init__.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/enum/base.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/enum/dml.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/enum/section.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/enum/shape.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/enum/style.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/enum/table.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/enum/text.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/errors.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/exceptions.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/fields.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/footnotes.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/image/__init__.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/image/exceptions.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/image/image.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/math.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/opc/__init__.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/opc/coreprops.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/oxml/__init__.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/revisions.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/sdt.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/section.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/session.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/settings.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/shape.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/shared.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/styles/__init__.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/styles/latent.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/styles/style.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/styles/styles.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/table.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/text/__init__.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/text/font.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/text/hyperlink.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/text/pagebreak.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/text/parfmt.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/text/run.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/text/tabstops.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/toc.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/docx/typing.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/scripts/docx_exec_lab.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/scripts/docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/scripts/dump_wire_fixtures.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/scripts/release.sh +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/scripts/round_trip_smoke.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/scripts/smoke_test_block_not_found.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/scripts/validate_find_replace_asset.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/__init__.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/conftest.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/METHODOLOGY.md +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/README.md +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/__init__.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/ab_probe/__init__.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/ab_probe/agent_documents.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/ab_probe/default_style_run_formatting.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/ab_probe/header_footer_pictures.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/ab_probe/less_used_writes.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/ab_probe/merged_cell_identity.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/ab_probe/paragraph_text_setter.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/ab_probe/picture_runs.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/ab_probe/proxy_equality.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/ab_probe/return_types.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/ab_probe/xml_incompatible_text.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/ab_probe/zero_and_negative_indents.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/ab_probe_cases.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/ab_probe_runner.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/auto_gen_cases.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/binary_round_trip.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/cases.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/complex_cases.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/coverage_report.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/extract.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/extreme_cases.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/firm_templates/README.md +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/firm_templates/__init__.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/firm_templates/_runner.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/firm_templates/extractor.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/local_runner.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/mega_cases.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshot.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/rw16_header_footer_tables.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/rw17_header_letterhead_filled.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/rw18_header_table_properties.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/rw19_header_table_rows_columns.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/op_snapshots/rw20_header_table_merges.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/ours_spec.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/parity_crawl.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/parity_diff.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/real_world_cases.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/round_trip_tests.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/stock_spec.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fixtures/field_reads.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fixtures/note_reference_reads.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fixtures/ruby_reads.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/fixtures/symbol_reads.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/live/__init__.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/live/baseline.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/live/reader.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/live/runner.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/live/sdk_driver.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/live/stock_driver.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/live/test_live_runner.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/parity/README.md +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/parity/__init__.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/parity/baseline_gaps.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/parity/compare.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/parity/introspect.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/parity/reports/gap_report.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/parity/run_parity.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/parity/snapshots/athena_latest.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/parity/test_parity_gap.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_add_section.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_add_section_extract_items.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_alignment_values.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_athena_extension_creates_session_open.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_athena_extensions_contract.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_athena_extensions_registry.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_batch_chunking.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_batching_perf.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_battery_cases.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_block_not_found_error.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_buffer.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_cell_add_table_not_supported.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_cell_borders_spec.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_cell_fastpath_runs.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_cell_inner_format_stash.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_cell_inner_run_format_stash.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_cell_inner_run_guard.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_cell_merge.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_cell_nested_tables.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_cell_paragraphs_listed.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_cell_text_plain_fastpath.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_cell_text_replace_semantics.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_citations.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_collapsed_range_format.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_color_format_theme.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_command_dataclasses.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_commands.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_comment_paragraphs.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_comments_first_read.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_content_control_value.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_core_properties.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_document_asset_id_property.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_document_clear.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_document_create.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_document_create_from_template.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_document_factory_validation.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_document_settings_session_open.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_docx_exec_lab.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_drawing_image.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_empty_hyperlinks.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_enum_parity.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_execution_scope.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_export_docx_clean.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_export_rescue_anchors.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_field_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_field_refresh.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_field_runs.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_find_replace_session_open.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_flushed_write_failures.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_http_transport.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_image_url_data_uri.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_inline_page_break.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_inline_shape_alt_text.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_inline_shape_size.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_inline_shape_type.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_inline_shapes_floating.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_insert_deferred.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_iter_inner_content.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_latent_style_writes.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_line_clear_break.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_list_styles.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_listed_paragraph_reads.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_literal_no_break_hyphens.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_literal_soft_hyphens.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_merged_cell_identity.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_merged_cell_secondary_slot.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_merged_cells.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_missing_modules.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_nested_table_refused.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_note_reference_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_oxml_shim.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_page_break.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_paragraph_delete.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_paragraph_indent_reads.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_paragraph_runs_hyperlinks.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_paragraph_text_empty_run.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_paragraph_text_len_cache.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_parfmt_clears.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_parity_misc.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_parity_round2.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_pdf_export_and_page_renders.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_phase_a_behavior.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_phase_b_headers_footers.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_phase_c_tables.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_positional_cell_id.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_postproc_ref_restore.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_pr19766_review_fixes.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_proxy_equality.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_ptc.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_python_docx_api_parity.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_read_run_ranges.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_repeated_node_read.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_restart_numbering.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_revisions.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_round_trip_smoke_script.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_ruby_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_run_add_picture_anchor.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_run_break_inner_content.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_run_edges.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_run_formatting_wire.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_run_identity.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_run_later_runs.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_run_mark_inheritance.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_run_page_number_blocks.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_run_text_python_docx.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_run_text_refreshes_paragraph.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_run_unmodeled_toggles.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_sdk_reads_served.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_section_read_lengths.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_sections_empty_list.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_add_picture.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_add_run.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_comments_get.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_document_audit.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_document_element.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_enum_section.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_font_audit.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_header_footer.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_hyperlink.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_inline_shape.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_misc.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_paragraph_style.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_parfmt.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_row_col_cell.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_run_add_break.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_run_style.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_run_style_strict.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_run_text.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_run_underline.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_section_audit.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_section_dimensions.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_section_onoff.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_settings.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_shared_audit.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_style.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_styles.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_table_audit.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_table_cell.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_table_dimensions.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_silent_stub_table_layout.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_smoke_integration.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_style_acceptance.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_style_delete.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_style_font.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_style_formatting_writes.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_style_metadata_writes.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_style_names.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_style_resolution.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_style_setters_contract.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_style_tab_stops.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_style_tab_stops_read.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_symbol_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_tab_stop_writes.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_table_add_row_grid_row.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_table_anchors.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_table_clears.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_table_direction.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_table_length_reads.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_table_listing_reads.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_table_read_reuse.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_table_set_cell_perf.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_table_style_id_resolution.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_temporarily_unavailable.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_text_control_characters.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_tracked_revisions_offsets.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_unmapped_alignment_reads.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_uv_lock_version.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_validate_find_replace_asset_script.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_versioned_read_reuse.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_wire_contract.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_xml_attr_guard.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_xml_incompatible_text.py +0 -0
- {athena_python_docx-0.40.0 → athena_python_docx-0.42.0}/tests/test_zod_wire_contract.py +0 -0
|
@@ -1080,6 +1080,20 @@ architecture or a DOCX Studio constraint:
|
|
|
1080
1080
|
name the document lacks raises `KeyError` unless docx-studio holds Word's
|
|
1081
1081
|
definition of it (`BUILTIN_CHARACTER_STYLE_NAMES` in
|
|
1082
1082
|
`docx/styles/_table.py`: `"Strong"`, `"Intense Emphasis"`, `"Book Title"`…).
|
|
1083
|
+
A character style the uploaded file's runs name without defining it (a
|
|
1084
|
+
generator's links naming `Hyperlink`) reads as the default character
|
|
1085
|
+
style, as python-docx reads it, and the export leaves it undefined, so an
|
|
1086
|
+
unedited download is the file (owner decision, 2026-10-26, #46935 A2).
|
|
1087
|
+
The editor and the server's PDFs still draw those runs in Word's built-in
|
|
1088
|
+
style, blue and underlined. docx-studio records the import's list, and a
|
|
1089
|
+
format that gives a run one of those styles (`run.style = "Hyperlink"`):
|
|
1090
|
+
from then on the export defines it, as Word adds a style's definition when
|
|
1091
|
+
it is applied, and `Info`'s `undefinedRunStyleIds` no longer lists it, so
|
|
1092
|
+
every session reads its runs as the built-in; the SDK asks `Info` again
|
|
1093
|
+
after such a write. An edit in the editor that copies such a run (a paste,
|
|
1094
|
+
the format painter) leaves the style undefined. A room imported before
|
|
1095
|
+
the record (no `undefinedRunStyleIds`) reads them as the built-in and its
|
|
1096
|
+
export adds the definition, as before.
|
|
1083
1097
|
`Document.add_table(style=)` and `Paragraph.add_run(style=)` resolve the
|
|
1084
1098
|
style before creating anything, so a rejected style leaves the document
|
|
1085
1099
|
untouched; python-docx raises after adding the table or run to its
|
|
@@ -1223,7 +1237,9 @@ architecture or a DOCX Studio constraint:
|
|
|
1223
1237
|
offsets in a run's text (`literalCharacters`), which `_word_text` reads as
|
|
1224
1238
|
written; a docx-studio that sends none reads each as the element, as before.
|
|
1225
1239
|
The export writes them back in a `w:t`, and text a script writes holds them
|
|
1226
|
-
as upstream writes it, in a `w:t`.
|
|
1240
|
+
as upstream writes it, in a `w:t`. `Document.find_replace` keeps each hyphen
|
|
1241
|
+
the replacement shares with the match as the match held it, and writes any
|
|
1242
|
+
other in a `w:t`, as upstream writes text.
|
|
1227
1243
|
|
|
1228
1244
|
- **`CoreProperties.modified` is the time of the last save.** The asset is a
|
|
1229
1245
|
live Word document, and docx-studio stamps `dcterms:modified` when it saves
|
|
@@ -1256,6 +1272,30 @@ architecture or a DOCX Studio constraint:
|
|
|
1256
1272
|
changes; the owner may overrule it. Recorded in
|
|
1257
1273
|
`tests/parity/intentional_deviations.json` (`live_deviations`).
|
|
1258
1274
|
|
|
1275
|
+
- **The inner link of a nested link reads with its text.** A `w:hyperlink`
|
|
1276
|
+
inside another (the duplicate wrapper Word writes around a pasted link,
|
|
1277
|
+
each with a relationship of its own; 3 of 6,826 customer uploads) reads
|
|
1278
|
+
its inner link's runs in `Hyperlink.runs` / `text`, `Paragraph.text` and
|
|
1279
|
+
`iter_inner_content()`, as Word shows them; upstream reads only a link's
|
|
1280
|
+
own `w:r` children, so the inner text is invisible to it and
|
|
1281
|
+
`p.text = p.text.replace(...)` would delete it unseen. The same choice as
|
|
1282
|
+
inline wrappers and tracked changes. Owner decision, 2026-10-26 (#46935,
|
|
1283
|
+
A1). Recorded in `tests/parity/intentional_deviations.json`
|
|
1284
|
+
(`live_deviations`) with the synthetic fixture `hyperlinks-nested`.
|
|
1285
|
+
|
|
1286
|
+
- **An emoji written as `w16se:symEx` reads as its run's text.** Word 2016
|
|
1287
|
+
writes an emoji as the run's `mc:AlternateContent`: an `mc:Choice
|
|
1288
|
+
Requires="w16se"` holding `w16se:symEx` (its font and character), and an
|
|
1289
|
+
`mc:Fallback` holding a `w:t` of the emoji (1 of 6,826 customer uploads).
|
|
1290
|
+
`Run.text`, `iter_inner_content()` and the paragraph's text hold the emoji,
|
|
1291
|
+
as Word shows it; upstream reads only a run's own `w:t` children and reads
|
|
1292
|
+
`""`, so a script rewriting the paragraph would delete the emoji unseen.
|
|
1293
|
+
An edited paragraph's export writes the emoji in a plain `w:t`, without
|
|
1294
|
+
the `w16se:symEx` choice, so upstream then reads it there too. Owner
|
|
1295
|
+
decision, 2026-10-26 (#46935, A5). Recorded in
|
|
1296
|
+
`tests/parity/intentional_deviations.json` (`live_deviations`) with the
|
|
1297
|
+
synthetic fixture `symbols-emoji-alternate-content`.
|
|
1298
|
+
|
|
1259
1299
|
- **Two things a source writes that the model holds otherwise read as the
|
|
1260
1300
|
model holds them** (2026-10-22 SDK live parity run over 398 customer
|
|
1261
1301
|
documents). Each is recorded in
|
|
@@ -1292,24 +1332,19 @@ architecture or a DOCX Studio constraint:
|
|
|
1292
1332
|
which the editor draws it with, where upstream reads the default table
|
|
1293
1333
|
style; reading the default would change how such tables draw. The export
|
|
1294
1334
|
leaves the style undefined, as the source has it: an unedited download of
|
|
1295
|
-
a document imported since is the source's bytes (owner, 2026-10-23). The read is kept
|
|
1296
|
-
checked
|
|
1297
|
-
|
|
1335
|
+
a document imported since is the source's bytes (owner, 2026-10-23). The read is kept
|
|
1336
|
+
(`tables-undefined-style`): checked on 2026-10-24 against the one sample
|
|
1337
|
+
document's Word PDF, whose tables draw alike under either style (empty
|
|
1338
|
+
paragraph defaults, the tables' own borders and margins) and match the
|
|
1339
|
+
editor's Table Grid layout, so no corpus document yet tells the two apart.
|
|
1298
1340
|
- A package with no styles part (1 document) reads Word's built-in styles,
|
|
1299
1341
|
which the editor gives it, where upstream reads its own default template's
|
|
1300
1342
|
(`styles-part-absent`). Confirmed by the owner, 2026-10-23.
|
|
1301
1343
|
|
|
1302
|
-
- **
|
|
1303
|
-
(2026-10-22 SDK live parity run over 398 customer documents, round 3).
|
|
1344
|
+
- **One more read follows the model where python-docx reads the source**
|
|
1345
|
+
(2026-10-22 SDK live parity run over 398 customer documents, round 3). It is
|
|
1304
1346
|
recorded in `tests/parity/intentional_deviations.json` (`live_deviations`)
|
|
1305
1347
|
with a synthetic fixture:
|
|
1306
|
-
- A comment whose range ends between paragraphs (`w:commentRangeEnd` a
|
|
1307
|
-
child of `w:body`; 1 document, 11 of 6,826 uploads) reads its reference
|
|
1308
|
-
run at the end of the last paragraph its mark covers, where the export
|
|
1309
|
-
writes it from the model, where upstream reads it in the paragraph after
|
|
1310
|
-
the range end: the model holds a range as a mark on its text and no range
|
|
1311
|
-
end between paragraphs (`comments-range-end-between-paragraphs`).
|
|
1312
|
-
Confirmed by the owner, 2026-10-23.
|
|
1313
1348
|
- A comment's annotation reference run that names no character style
|
|
1314
1349
|
(1 document, 13 of 6,826 uploads) reads in the CommentReference style, in
|
|
1315
1350
|
which the export writes every comment's annotation run when it writes the
|
|
@@ -1317,6 +1352,25 @@ architecture or a DOCX Studio constraint:
|
|
|
1317
1352
|
run as an empty run nothing tells from another
|
|
1318
1353
|
(`comments-annotation-ref-unstyled`). Confirmed by the owner, 2026-10-23.
|
|
1319
1354
|
|
|
1355
|
+
A comment whose range ends between paragraphs (`w:commentRangeEnd` a child
|
|
1356
|
+
of `w:body`; 1 document, 11 of 6,826 uploads) is no longer a deviation, with
|
|
1357
|
+
the other comments whose reference run stands apart from the text their
|
|
1358
|
+
range marks: one over links alone in several paragraphs or over a picture or
|
|
1359
|
+
an object alone, which the model holds no range for, one that ends in a
|
|
1360
|
+
paragraph holding none of its text, and one whose run follows a field's end
|
|
1361
|
+
run. The import records where the source body holds each reference run and
|
|
1362
|
+
range end (`commentReferenceRunPlaces`: the paragraph and how many of its
|
|
1363
|
+
runs come before it), the paragraphs whose text it marks for the comment,
|
|
1364
|
+
and the fingerprint of each body block that holds them. While those blocks
|
|
1365
|
+
are as imported and the comment marks the same paragraphs, the run reads
|
|
1366
|
+
where the source holds it, where the export keeps the blocks' source XML,
|
|
1367
|
+
and a reply added since reads beside the comment's range end, where the
|
|
1368
|
+
export writes it (`comments-range-end-between-paragraphs`,
|
|
1369
|
+
`comments-range-over-links`, `comments-range-end-without-text`,
|
|
1370
|
+
`comments-range-over-object`). A block an edit changed, a comment moved to
|
|
1371
|
+
new text, and a room imported before the record read where the model places
|
|
1372
|
+
the run, as before.
|
|
1373
|
+
|
|
1320
1374
|
A third, runs of one formatting read and exported joined after a run write
|
|
1321
1375
|
in a paragraph with a link, an inline content control, an equation or a
|
|
1322
1376
|
field beside a picture, is no longer a deviation (the owner asked for it to
|
|
@@ -1324,14 +1378,16 @@ architecture or a DOCX Studio constraint:
|
|
|
1324
1378
|
`add_run`, `add_text`) carries the records the read takes of such a
|
|
1325
1379
|
paragraph's runs (`_writtenRunRecords`), each `w:r` kept with the written
|
|
1326
1380
|
run changed as python-docx changes it, and the export writes the paragraph
|
|
1327
|
-
from them. A
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1381
|
+
from them. A format of one of a field's own runs is carried too, into the
|
|
1382
|
+
field's record (2026-10-24, #46477). A write the records cannot describe (a
|
|
1383
|
+
write across a field, a field's result written, a run the offsets cannot
|
|
1384
|
+
tell from an empty one beside it) leaves the paragraph as before: a link
|
|
1385
|
+
paragraph's kept content, else the runs its marks make. Fixture scripts
|
|
1331
1386
|
`fixture-links-run-write-keeps-runs`, `fixture-links-run-writes`,
|
|
1332
1387
|
`fixture-content-controls-inline-run-writes`,
|
|
1333
|
-
`fixture-equations-beside-runs-run-writes
|
|
1334
|
-
`fixture-fields-beside-pictures-run-writes
|
|
1388
|
+
`fixture-equations-beside-runs-run-writes`,
|
|
1389
|
+
`fixture-fields-beside-pictures-run-writes` and
|
|
1390
|
+
`fixture-fields-beside-pictures-field-run-formats`.
|
|
1335
1391
|
|
|
1336
1392
|
- **A citation link Athena added reads as its text, as the export writes it**
|
|
1337
1393
|
(owner decision #40122). Athena's citation flow stamps the links it writes
|
|
@@ -1416,6 +1472,22 @@ architecture or a DOCX Studio constraint:
|
|
|
1416
1472
|
exported file. Against a docx-studio that predates `fieldRun` reads, a
|
|
1417
1473
|
field reads as no runs and no text, as before (`tests/test_field_runs.py`).
|
|
1418
1474
|
|
|
1475
|
+
- **A header or footer run holding text beside a whole field reads as more
|
|
1476
|
+
runs.** A generator writes a page number as one `w:r` holding `Page ` and a
|
|
1477
|
+
`PAGE` field's begin, code, separate and end characters (1 of 6,826
|
|
1478
|
+
customer uploads). Upstream reads one run, `Page `. The import cuts that
|
|
1479
|
+
`w:r` into a run of the text and the field's own run (and a run of any text
|
|
1480
|
+
after the field; `RunParseOptions.cutRunsAtFields`), so the paragraph's
|
|
1481
|
+
text reads as upstream's and the editor and the server's PDFs draw it;
|
|
1482
|
+
before, the field took the whole run and the text was lost, from the reads
|
|
1483
|
+
and from an edited footer's export. `paragraph.runs` holds the extra runs,
|
|
1484
|
+
and an edited paragraph's export writes them as runs of their own: a
|
|
1485
|
+
cosmetic run split. A body run of that shape is still read the old way, as
|
|
1486
|
+
its room stores what its import read. Owner decision, 2026-10-26 (#46935,
|
|
1487
|
+
A4). Recorded in `tests/parity/intentional_deviations.json`
|
|
1488
|
+
(`live_deviations`) with the synthetic fixture
|
|
1489
|
+
`footers-text-and-field-one-run`.
|
|
1490
|
+
|
|
1419
1491
|
- **A run that holds a page number block refuses a tracked whole-run format
|
|
1420
1492
|
and two text writes.** A page number block (`w:pgNum`) reads as a run with
|
|
1421
1493
|
no text, and a run that holds text and blocks (or two blocks) reads as
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: athena-python-docx
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.42.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>
|
|
@@ -40,6 +40,7 @@ from docx._batching import run_sync
|
|
|
40
40
|
from docx._buffer import writes_unapplied
|
|
41
41
|
from docx.blkcntnr import BlockItemContainer
|
|
42
42
|
from docx.errors import DocxError, NotFoundError
|
|
43
|
+
from docx.text.paragraph import _buffer_writes
|
|
43
44
|
|
|
44
45
|
if TYPE_CHECKING:
|
|
45
46
|
from collections.abc import Iterator, Sequence
|
|
@@ -186,6 +187,35 @@ def _coerce_timestamp(raw: object) -> datetime | None:
|
|
|
186
187
|
return None
|
|
187
188
|
|
|
188
189
|
|
|
190
|
+
def _word_date(raw: object) -> datetime | None:
|
|
191
|
+
"""A comment's ``w:date`` as python-docx 1.2.0's ``Comment.timestamp``
|
|
192
|
+
reads it (``ST_DateTime.convert_from_xml``): with a trailing ``Z``, a time
|
|
193
|
+
in UTC; with an offset, a time in that zone; without either, a time in no
|
|
194
|
+
zone, as Word writes the author's local time; one it cannot parse, the
|
|
195
|
+
epoch in UTC. ``None`` for a comment with no ``w:date``."""
|
|
196
|
+
if not isinstance(raw, str):
|
|
197
|
+
return None
|
|
198
|
+
|
|
199
|
+
def parse(text: str) -> datetime:
|
|
200
|
+
if text.endswith("Z"):
|
|
201
|
+
try:
|
|
202
|
+
return datetime.strptime(text, "%Y-%m-%dT%H:%M:%S.%fZ").replace(tzinfo=timezone.utc)
|
|
203
|
+
except ValueError:
|
|
204
|
+
return datetime.strptime(text, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)
|
|
205
|
+
try:
|
|
206
|
+
return datetime.fromisoformat(text)
|
|
207
|
+
except ValueError:
|
|
208
|
+
try:
|
|
209
|
+
return datetime.strptime(text, "%Y-%m-%dT%H:%M:%S.%f")
|
|
210
|
+
except ValueError:
|
|
211
|
+
return datetime.strptime(text, "%Y-%m-%dT%H:%M:%S")
|
|
212
|
+
|
|
213
|
+
try:
|
|
214
|
+
return parse(raw)
|
|
215
|
+
except Exception:
|
|
216
|
+
return datetime(1970, 1, 1, tzinfo=timezone.utc)
|
|
217
|
+
|
|
218
|
+
|
|
189
219
|
# docx-studio's caps (``MAX_COMMENT_AUTHOR_LENGTH`` /
|
|
190
220
|
# ``MAX_COMMENT_INITIALS_LENGTH`` in apps/api/src/commands/types.ts), in
|
|
191
221
|
# UTF-16 code units as the server counts them.
|
|
@@ -274,7 +304,7 @@ def _created_comment_info(
|
|
|
274
304
|
}
|
|
275
305
|
stored = result.get("comment") if isinstance(result, dict) else None
|
|
276
306
|
if isinstance(stored, dict):
|
|
277
|
-
for key in ("creatorName", "initials", "createdTime"):
|
|
307
|
+
for key in ("creatorName", "initials", "createdTime", "wordDate"):
|
|
278
308
|
if key in stored:
|
|
279
309
|
info[key] = stored[key]
|
|
280
310
|
return info
|
|
@@ -291,6 +321,10 @@ class Comment(BlockItemContainer):
|
|
|
291
321
|
) -> None:
|
|
292
322
|
self._session: "Session" = session
|
|
293
323
|
self._info: dict = dict(info or {})
|
|
324
|
+
# The command buffer's ``writes`` count the info was read at: a later
|
|
325
|
+
# write (to the comment's paragraphs or runs, its replies' root, or
|
|
326
|
+
# anything the export writes it anew for) may change its ``w:date``.
|
|
327
|
+
self._dated_at: int | None = _buffer_writes(session)
|
|
294
328
|
|
|
295
329
|
# ---- identity / metadata ----
|
|
296
330
|
@property
|
|
@@ -336,6 +370,29 @@ class Comment(BlockItemContainer):
|
|
|
336
370
|
|
|
337
371
|
@property
|
|
338
372
|
def timestamp(self) -> datetime | None:
|
|
373
|
+
"""The ``w:date`` docx-studio says the export writes for the comment
|
|
374
|
+
(``wordDate``), read as python-docx reads it: the source's own for a
|
|
375
|
+
comment no write changed, which Word writes in the author's local time
|
|
376
|
+
with no zone. A docx-studio that does not say gives the comment's
|
|
377
|
+
time in UTC (``createdTime``). After a write since the comment was
|
|
378
|
+
read (its text, a paragraph added to it, an edit of its paragraphs or
|
|
379
|
+
runs, or any other, which may make the export write it anew), it reads
|
|
380
|
+
the comment again; a read that fails is tried again at the next."""
|
|
381
|
+
if self._info.get("_wordDateStale") or _buffer_writes(self._session) != self._dated_at:
|
|
382
|
+
try:
|
|
383
|
+
fresh: object = run_sync(self._session.doc.comments.get({"id": self._id}))
|
|
384
|
+
except NotFoundError as exc:
|
|
385
|
+
if writes_unapplied(exc):
|
|
386
|
+
raise
|
|
387
|
+
fresh = None
|
|
388
|
+
if isinstance(fresh, dict):
|
|
389
|
+
for key in ("wordDate", "createdTime"):
|
|
390
|
+
if key in fresh:
|
|
391
|
+
self._info[key] = fresh[key]
|
|
392
|
+
self._info.pop("_wordDateStale", None)
|
|
393
|
+
self._dated_at = _buffer_writes(self._session)
|
|
394
|
+
if "wordDate" in self._info:
|
|
395
|
+
return _word_date(self._info.get("wordDate"))
|
|
339
396
|
return _coerce_timestamp(self._info.get("createdTime"))
|
|
340
397
|
|
|
341
398
|
# ---- body ----
|
|
@@ -363,6 +420,8 @@ class Comment(BlockItemContainer):
|
|
|
363
420
|
|
|
364
421
|
run_sync(self._session.doc.comments.patch({"id": self._id, "text": value}))
|
|
365
422
|
self._info["text"] = value
|
|
423
|
+
# The export writes the comment anew, with the date its record holds.
|
|
424
|
+
self._info["_wordDateStale"] = True
|
|
366
425
|
# CommentsPatch is a Command type — referenced for clarity / parity
|
|
367
426
|
# with the bus dispatch path.
|
|
368
427
|
del CommentsPatch
|
|
@@ -459,6 +518,8 @@ class Comment(BlockItemContainer):
|
|
|
459
518
|
node_id = result.get("nodeId") if isinstance(result, dict) else None
|
|
460
519
|
if not isinstance(node_id, str) or not node_id:
|
|
461
520
|
raise CommentsNotImplementedError("Paragraphs added to comments")
|
|
521
|
+
# The export writes the comment anew, with the date its record holds.
|
|
522
|
+
self._info["_wordDateStale"] = True
|
|
462
523
|
paragraph = Paragraph(session=self._session, node_id=node_id, node_type="paragraph", text_len=len(text))
|
|
463
524
|
if style is not None:
|
|
464
525
|
paragraph.style = style
|
|
@@ -159,6 +159,15 @@ _latent: "weakref.WeakKeyDictionary[Any, dict[str, Any] | None]" = weakref.WeakK
|
|
|
159
159
|
#: Each session's paragraph style ids its document's source names without
|
|
160
160
|
#: defining them, as ``Info`` reports them (``undefinedStyleIds``).
|
|
161
161
|
_undefined: "weakref.WeakKeyDictionary[Any, frozenset[str]]" = weakref.WeakKeyDictionary()
|
|
162
|
+
#: Each session's character style ids the runs of its uploaded file name
|
|
163
|
+
#: without the file defining them and no write has given a run, as ``Info``
|
|
164
|
+
#: reports them (``undefinedRunStyleIds``; none from a room imported before the
|
|
165
|
+
#: record): the export leaves them undefined.
|
|
166
|
+
_undefined_runs: "weakref.WeakKeyDictionary[Any, frozenset[str]]" = weakref.WeakKeyDictionary()
|
|
167
|
+
#: Sessions whose script gave a run one of ``_undefined_runs``: docx-studio
|
|
168
|
+
#: records the write (the export defines the style from then on), so the next
|
|
169
|
+
#: read of such a style asks ``Info`` again, after the write.
|
|
170
|
+
_runs_restyled: "weakref.WeakSet[Any]" = weakref.WeakSet()
|
|
162
171
|
|
|
163
172
|
|
|
164
173
|
def _records(info: object) -> tuple[StyleRecord, ...] | None:
|
|
@@ -229,6 +238,12 @@ def style_table(session: Any) -> tuple[StyleRecord, ...] | None:
|
|
|
229
238
|
if isinstance(undefined_ids, list)
|
|
230
239
|
else frozenset()
|
|
231
240
|
)
|
|
241
|
+
undefined_run_ids: object = info.get("undefinedRunStyleIds") if isinstance(info, dict) else None
|
|
242
|
+
_undefined_runs[session] = (
|
|
243
|
+
frozenset(i for i in undefined_run_ids if isinstance(i, str))
|
|
244
|
+
if isinstance(undefined_run_ids, list)
|
|
245
|
+
else frozenset()
|
|
246
|
+
)
|
|
232
247
|
_stale.discard(session)
|
|
233
248
|
# A server that showed it writes styles still does once deletes leave its
|
|
234
249
|
# table without the entries that told: the signal holds for the session.
|
|
@@ -577,11 +592,15 @@ def style_of(
|
|
|
577
592
|
default style, else None.
|
|
578
593
|
|
|
579
594
|
A Word built-in the document does not define reads back as that built-in,
|
|
580
|
-
as ``style_id_for`` lets it be set
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
595
|
+
as ``style_id_for`` lets it be set; not a character style the runs of the
|
|
596
|
+
uploaded file already named without defining it and no write has given a
|
|
597
|
+
run (``Info``'s ``undefinedRunStyleIds``), which python-docx reads as the
|
|
598
|
+
default character style and the export leaves undefined. A document
|
|
599
|
+
without styles reads its defaults as Word's,
|
|
600
|
+
as python-docx's default ``styles.xml`` gives them. Without a style table:
|
|
601
|
+
the stored style as given, else Word's default. The style reads the
|
|
602
|
+
definition it was found as (its record), as python-docx's wraps that
|
|
603
|
+
``w:style``.
|
|
585
604
|
"""
|
|
586
605
|
from docx.styles.style import _style_of_record
|
|
587
606
|
|
|
@@ -592,7 +611,12 @@ def style_of(
|
|
|
592
611
|
name, style_id = builtin or (BabelFish.internal2ui(stored_style_id), stored_style_id)
|
|
593
612
|
return _new_style(session, name, style_id, style_type)
|
|
594
613
|
record = first_of_id(table or (), stored_style_id) if stored_style_id else None
|
|
595
|
-
if
|
|
614
|
+
if (
|
|
615
|
+
record is None
|
|
616
|
+
and builtin is not None
|
|
617
|
+
and _defined_builtin(style_type, builtin[1])
|
|
618
|
+
and not _file_left_undefined(session, stored_style_id, style_type)
|
|
619
|
+
):
|
|
596
620
|
return _new_style(session, *builtin, style_type)
|
|
597
621
|
if record is None or record.type != style_type:
|
|
598
622
|
record = default_style(table, style_type) if table else None
|
|
@@ -601,6 +625,39 @@ def style_of(
|
|
|
601
625
|
return _style_of_record(record, session)
|
|
602
626
|
|
|
603
627
|
|
|
628
|
+
def _file_left_undefined(session: Any, style_id: str | None, style_type: WD_STYLE_TYPE) -> bool:
|
|
629
|
+
"""Whether ``style_id`` is a character style the uploaded file's runs name
|
|
630
|
+
without the file defining it and no write has given a run, as ``Info``
|
|
631
|
+
reports them (``undefinedRunStyleIds``): python-docx reads such a run's
|
|
632
|
+
style as the default character style, and the export leaves the style
|
|
633
|
+
undefined. After the script gave a run one of them, ``Info`` is read again
|
|
634
|
+
first (it sends the write before it), so the answer is docx-studio's
|
|
635
|
+
record, as every later session reads it."""
|
|
636
|
+
if style_type != WD_STYLE_TYPE.CHARACTER or not style_id:
|
|
637
|
+
return False
|
|
638
|
+
try:
|
|
639
|
+
if session in _runs_restyled:
|
|
640
|
+
_runs_restyled.discard(session)
|
|
641
|
+
forget_style_table(session)
|
|
642
|
+
style_table(session)
|
|
643
|
+
return style_id in _undefined_runs.get(session, frozenset())
|
|
644
|
+
except TypeError:
|
|
645
|
+
return False
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
def _note_applied_run_style(session: Any, style_id: str | None, style_type: WD_STYLE_TYPE) -> None:
|
|
649
|
+
"""Note that the script gives a run ``style_id``, one the uploaded file's
|
|
650
|
+
runs name undefined: docx-studio records the write if it applies, so the
|
|
651
|
+
next read of such a style asks ``Info`` again."""
|
|
652
|
+
if style_type != WD_STYLE_TYPE.CHARACTER or not style_id:
|
|
653
|
+
return
|
|
654
|
+
try:
|
|
655
|
+
if style_id in _undefined_runs.get(session, frozenset()):
|
|
656
|
+
_runs_restyled.add(session)
|
|
657
|
+
except TypeError:
|
|
658
|
+
pass
|
|
659
|
+
|
|
660
|
+
|
|
604
661
|
def style_id_for(
|
|
605
662
|
session: Any,
|
|
606
663
|
style_or_name: object,
|
|
@@ -643,7 +700,10 @@ def style_id_for(
|
|
|
643
700
|
f"{owner} requires str, {kind} (with ``.style_id``/``.name``), or None; "
|
|
644
701
|
f"got {type(style_or_name).__name__} {style_or_name!r}",
|
|
645
702
|
)
|
|
646
|
-
|
|
703
|
+
if style_id == _document_default_style_id(session, style_type):
|
|
704
|
+
return None
|
|
705
|
+
_note_applied_run_style(session, style_id, style_type)
|
|
706
|
+
return style_id
|
|
647
707
|
|
|
648
708
|
|
|
649
709
|
def _style_id_of_name(session: Any, requested: str, style_type: WD_STYLE_TYPE) -> str:
|
|
@@ -347,10 +347,24 @@ def _keep_link_address(run_data: dict, inline: dict) -> None:
|
|
|
347
347
|
"""Give a picture's run the address of the link it is in where its read
|
|
348
348
|
sends one apart (``hyperlinkAddress``, for a target that holds a ``#`` of
|
|
349
349
|
its own), as a text run's read holds it: :attr:`Hyperlink.address` reads
|
|
350
|
-
the whole target then, as python-docx does
|
|
350
|
+
the whole target then, as python-docx does; that it opens a link of its
|
|
351
|
+
own (``newLink``, :func:`_opens_link`); and that the link has no target
|
|
352
|
+
(``linkWithoutTarget``)."""
|
|
351
353
|
address: object = inline.get("hyperlinkAddress")
|
|
352
354
|
if isinstance(address, str):
|
|
353
355
|
run_data["hyperlinkAddress"] = address
|
|
356
|
+
if inline.get("newLink") is True:
|
|
357
|
+
run_data["newLink"] = True
|
|
358
|
+
if inline.get("linkWithoutTarget") is True:
|
|
359
|
+
run_data["linkWithoutTarget"] = True
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def _opens_link(run_data: dict) -> bool:
|
|
363
|
+
"""True for the first run of a link that follows another link to the same
|
|
364
|
+
target with nothing between them (``newLink``): two ``w:hyperlink``
|
|
365
|
+
elements side by side, which python-docx reads as two links. Without it,
|
|
366
|
+
runs of one target side by side are one link."""
|
|
367
|
+
return run_data.get("newLink") is True
|
|
354
368
|
|
|
355
369
|
|
|
356
370
|
def _picture_run_data(inline: dict, drawing: dict) -> dict:
|
|
@@ -372,8 +386,9 @@ def _picture_run_data(inline: dict, drawing: dict) -> dict:
|
|
|
372
386
|
|
|
373
387
|
def _rendered_page_break_reads(info: object) -> "list[tuple[dict, int]]":
|
|
374
388
|
"""Each rendered page break docx-studio reads in the runs of the paragraph
|
|
375
|
-
whose node info is `info` (``renderedPageBreaks``), its links'
|
|
376
|
-
order, with its offset in the paragraph's
|
|
389
|
+
whose node info is `info` (``renderedPageBreaks``), its links' and its
|
|
390
|
+
fields' included, in order, with its offset in the paragraph's
|
|
391
|
+
text-address space."""
|
|
377
392
|
if not isinstance(info, dict):
|
|
378
393
|
return []
|
|
379
394
|
node: object = info.get("node")
|
|
@@ -382,6 +397,16 @@ def _rendered_page_break_reads(info: object) -> "list[tuple[dict, int]]":
|
|
|
382
397
|
reads: list[tuple[dict, int]] = []
|
|
383
398
|
offset = 0
|
|
384
399
|
for inline in inlines if isinstance(inlines, list) else []:
|
|
400
|
+
field_run: object = inline.get("fieldRun") if isinstance(inline, dict) else None
|
|
401
|
+
if isinstance(field_run, dict):
|
|
402
|
+
# A run of a field (its begin, code, separate, result or end run):
|
|
403
|
+
# it takes no room in the text-address space, and a break in it
|
|
404
|
+
# stands there.
|
|
405
|
+
field_breaks: object = field_run.get("renderedPageBreaks")
|
|
406
|
+
for brk in field_breaks if isinstance(field_breaks, list) else []:
|
|
407
|
+
if isinstance(brk, dict):
|
|
408
|
+
reads.append((brk, offset))
|
|
409
|
+
continue
|
|
385
410
|
run: object = inline.get("run") if isinstance(inline, dict) else None
|
|
386
411
|
if not isinstance(run, dict):
|
|
387
412
|
# A picture's run: its breaks stand before its drawing, and it holds no text.
|
|
@@ -1015,7 +1040,7 @@ class Paragraph:
|
|
|
1015
1040
|
if isinstance(drawing, dict):
|
|
1016
1041
|
# A picture: python-docx's run holding a ``w:drawing``. It
|
|
1017
1042
|
# takes no room in the text-address space.
|
|
1018
|
-
if self._extract_hyperlink_target(inline):
|
|
1043
|
+
if self._extract_hyperlink_target(inline) is not None:
|
|
1019
1044
|
continue
|
|
1020
1045
|
run_obj = Run(
|
|
1021
1046
|
session=self._session,
|
|
@@ -1037,7 +1062,7 @@ class Paragraph:
|
|
|
1037
1062
|
start: int = offset
|
|
1038
1063
|
end: int = offset + len(text)
|
|
1039
1064
|
offset = end
|
|
1040
|
-
if self._extract_hyperlink_target(run_data) or _deleted(run_data):
|
|
1065
|
+
if self._extract_hyperlink_target(run_data) is not None or _deleted(run_data):
|
|
1041
1066
|
continue
|
|
1042
1067
|
run_obj = Run(
|
|
1043
1068
|
session=self._session,
|
|
@@ -1058,7 +1083,7 @@ class Paragraph:
|
|
|
1058
1083
|
start = offset
|
|
1059
1084
|
end = offset + len(text2)
|
|
1060
1085
|
offset = end
|
|
1061
|
-
if self._extract_hyperlink_target(inline):
|
|
1086
|
+
if self._extract_hyperlink_target(inline) is not None:
|
|
1062
1087
|
continue
|
|
1063
1088
|
runs.append(
|
|
1064
1089
|
Run(
|
|
@@ -1248,6 +1273,11 @@ class Paragraph:
|
|
|
1248
1273
|
maybe = hl.get("url") or hl.get("target")
|
|
1249
1274
|
if isinstance(maybe, str) and maybe:
|
|
1250
1275
|
return maybe
|
|
1276
|
+
# A link with no target (``<w:hyperlink w:history="1">`` holding runs,
|
|
1277
|
+
# with no ``r:id`` and no ``w:anchor``), which python-docx reads as a
|
|
1278
|
+
# link whose address is "" (``linkWithoutTarget``).
|
|
1279
|
+
if run_data.get("linkWithoutTarget") is True:
|
|
1280
|
+
return ""
|
|
1251
1281
|
return None
|
|
1252
1282
|
|
|
1253
1283
|
@property
|
|
@@ -1304,7 +1334,7 @@ class Paragraph:
|
|
|
1304
1334
|
# room in the text-address space.
|
|
1305
1335
|
run_data = _picture_run_data(inline, drawing)
|
|
1306
1336
|
link = self._extract_hyperlink_target(inline)
|
|
1307
|
-
if link:
|
|
1337
|
+
if link is not None:
|
|
1308
1338
|
run_data["hyperlink"] = link
|
|
1309
1339
|
_keep_link_address(run_data, inline)
|
|
1310
1340
|
elif isinstance(maybe_run, dict):
|
|
@@ -1373,7 +1403,7 @@ class Paragraph:
|
|
|
1373
1403
|
|
|
1374
1404
|
# Hyperlinked run. Extend the current span if it shares a
|
|
1375
1405
|
# target, otherwise close it and start a new one.
|
|
1376
|
-
if current is not None and current[0] == target:
|
|
1406
|
+
if current is not None and current[0] == target and not _opens_link(run_data):
|
|
1377
1407
|
current[1].append((run_data, (start, end)))
|
|
1378
1408
|
else:
|
|
1379
1409
|
if current is not None:
|
|
@@ -1523,7 +1553,7 @@ class Paragraph:
|
|
|
1523
1553
|
# link when it carries one.
|
|
1524
1554
|
run_data = _picture_run_data(inline, drawing)
|
|
1525
1555
|
link = self._extract_hyperlink_target(inline)
|
|
1526
|
-
if link:
|
|
1556
|
+
if link is not None:
|
|
1527
1557
|
run_data["hyperlink"] = link
|
|
1528
1558
|
_keep_link_address(run_data, inline)
|
|
1529
1559
|
text = ""
|
|
@@ -1549,11 +1579,11 @@ class Paragraph:
|
|
|
1549
1579
|
offset += len(text)
|
|
1550
1580
|
continue
|
|
1551
1581
|
target = self._extract_hyperlink_target(run_data)
|
|
1552
|
-
if target:
|
|
1582
|
+
if target is not None:
|
|
1553
1583
|
start = offset
|
|
1554
1584
|
offset += len(text)
|
|
1555
1585
|
segment = (run_data, (start, start + len(text)))
|
|
1556
|
-
if current is not None and current[0] == target:
|
|
1586
|
+
if current is not None and current[0] == target and not _opens_link(run_data):
|
|
1557
1587
|
current[1].append(segment)
|
|
1558
1588
|
else:
|
|
1559
1589
|
flushed = _flush_current()
|
|
@@ -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.42.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"
|
|
@@ -140,6 +140,9 @@ class FakeDocState:
|
|
|
140
140
|
# The paragraph style ids the document's source names without defining
|
|
141
141
|
# them, as ``Info`` reports them beside the style table (``undefinedStyleIds``).
|
|
142
142
|
self.undefined_style_ids: list[str] | None = None
|
|
143
|
+
# The character style ids the uploaded file's runs name without defining
|
|
144
|
+
# them, as ``Info`` reports them beside the style table (``undefinedRunStyleIds``).
|
|
145
|
+
self.undefined_run_style_ids: list[str] | None = None
|
|
143
146
|
# What an older server's ``Info`` lists instead: the paragraph styles in use.
|
|
144
147
|
self.paragraph_styles_in_use: list[dict] = []
|
|
145
148
|
# ``docProps/core.xml`` as ``Info`` reports it (``includeCoreProperties``):
|
|
@@ -254,6 +257,8 @@ class FakeDocState:
|
|
|
254
257
|
info["latentStyles"] = self.latent_styles
|
|
255
258
|
if "styleTable" in info and self.undefined_style_ids is not None:
|
|
256
259
|
info["undefinedStyleIds"] = list(self.undefined_style_ids)
|
|
260
|
+
if "styleTable" in info and self.undefined_run_style_ids is not None:
|
|
261
|
+
info["undefinedRunStyleIds"] = list(self.undefined_run_style_ids)
|
|
257
262
|
if (params or {}).get("includeCoreProperties") and self.core_properties is not None:
|
|
258
263
|
info["coreProperties"] = dict(self.core_properties)
|
|
259
264
|
if self.core_properties_default:
|