athena-python-docx 0.41.0__tar.gz → 0.42.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/AGENTS.md +54 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/PKG-INFO +1 -1
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/__init__.py +1 -1
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/_buffer.py +71 -7
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/_http.py +20 -2
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/_http_doc.py +18 -1
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/styles/_table.py +67 -7
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/pyproject.toml +1 -1
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/fake_session.py +5 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/live/fixture_scripts.py +79 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/parity/intentional_deviations.json +48 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_block_not_found_error.py +24 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_buffer.py +86 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_document_create.py +42 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_document_create_from_template.py +9 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_run_table_style_objects.py +58 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/uv.lock +1 -1
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/.gitignore +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/CLAUDE.md +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/DOCX_EXEC_LAB.md +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/README.md +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/_athena_extension.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/_batching.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/_capabilities.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/_citations.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/_execution.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/_image_utils.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/_postproc.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/_proxy.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/_ptc.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/_references.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/_run_order.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/_table_styles.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/_timeouts.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/_tracked.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/_xml_text.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/api.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/blkcntnr.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/bookmarks.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/charts.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/client.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/commands.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/comments.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/document.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/drawing/__init__.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/enum/__init__.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/enum/base.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/enum/dml.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/enum/section.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/enum/shape.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/enum/style.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/enum/table.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/enum/text.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/errors.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/exceptions.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/fields.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/footnotes.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/image/__init__.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/image/exceptions.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/image/image.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/math.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/opc/__init__.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/opc/coreprops.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/oxml/__init__.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/revisions.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/sdt.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/section.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/session.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/settings.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/shape.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/shared.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/styles/__init__.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/styles/latent.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/styles/style.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/styles/styles.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/table.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/text/__init__.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/text/font.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/text/hyperlink.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/text/pagebreak.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/text/paragraph.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/text/parfmt.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/text/run.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/text/tabstops.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/toc.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/docx/typing.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/scripts/docx_exec_lab.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/scripts/docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/scripts/dump_wire_fixtures.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/scripts/release.sh +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/scripts/round_trip_smoke.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/scripts/smoke_test_block_not_found.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/scripts/validate_find_replace_asset.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/__init__.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/conftest.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/METHODOLOGY.md +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/README.md +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/__init__.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/ab_probe/__init__.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/ab_probe/agent_documents.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/ab_probe/default_style_run_formatting.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/ab_probe/header_footer_pictures.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/ab_probe/less_used_writes.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/ab_probe/merged_cell_identity.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/ab_probe/paragraph_text_setter.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/ab_probe/picture_runs.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/ab_probe/proxy_equality.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/ab_probe/return_types.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/ab_probe/xml_incompatible_text.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/ab_probe/zero_and_negative_indents.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/ab_probe_cases.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/ab_probe_runner.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/auto_gen_cases.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/binary_round_trip.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/cases.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/complex_cases.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/coverage_report.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/extract.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/extreme_cases.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/firm_templates/README.md +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/firm_templates/__init__.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/firm_templates/_runner.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/firm_templates/extractor.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/local_runner.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/mega_cases.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshot.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/rw16_header_footer_tables.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/rw17_header_letterhead_filled.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/rw18_header_table_properties.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/rw19_header_table_rows_columns.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/op_snapshots/rw20_header_table_merges.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/ours_spec.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/parity_crawl.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/parity_diff.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/real_world_cases.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/round_trip_tests.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/stock_spec.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fidelity/test_e2e_against_staging.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fixtures/field_reads.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fixtures/note_reference_reads.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fixtures/ruby_reads.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/fixtures/symbol_reads.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/live/__init__.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/live/baseline.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/live/reader.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/live/runner.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/live/sdk_driver.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/live/stock_driver.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/live/test_live_runner.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/parity/README.md +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/parity/__init__.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/parity/baseline_gaps.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/parity/compare.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/parity/introspect.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/parity/reports/gap_report.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/parity/run_parity.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/parity/snapshots/athena_latest.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/parity/test_parity_gap.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_add_section.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_add_section_extract_items.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_alignment_values.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_athena_extension_creates_session_open.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_athena_extensions_contract.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_athena_extensions_registry.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_batch_chunking.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_batching_perf.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_battery_cases.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_cell_add_table_not_supported.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_cell_borders_spec.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_cell_fastpath_runs.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_cell_inner_format_stash.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_cell_inner_run_format_stash.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_cell_inner_run_guard.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_cell_merge.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_cell_nested_tables.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_cell_paragraphs_listed.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_cell_text_plain_fastpath.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_cell_text_replace_semantics.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_citations.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_collapsed_range_format.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_color_format_theme.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_command_dataclasses.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_commands.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_comment_paragraphs.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_comments.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_comments_first_read.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_content_control_value.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_core_properties.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_document_asset_id_property.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_document_clear.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_document_factory_validation.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_document_settings_session_open.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_docx_exec_lab.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_drawing_image.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_e2e_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_empty_hyperlinks.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_enum_parity.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_execution_scope.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_export_docx_clean.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_export_rescue_anchors.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_field_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_field_refresh.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_field_runs.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_find_replace_session_open.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_flushed_write_failures.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_http_transport.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_hyperlink_coalescing.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_image_url_data_uri.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_inline_page_break.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_inline_shape_alt_text.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_inline_shape_size.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_inline_shape_type.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_inline_shapes_floating.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_insert_deferred.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_iter_inner_content.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_latent_style_writes.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_line_clear_break.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_list_styles.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_listed_paragraph_reads.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_literal_no_break_hyphens.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_literal_soft_hyphens.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_merged_cell_identity.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_merged_cell_secondary_slot.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_merged_cells.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_missing_modules.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_nested_table_refused.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_note_reference_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_oxml_shim.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_page_break.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_paragraph_delete.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_paragraph_indent_reads.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_paragraph_runs_hyperlinks.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_paragraph_text_empty_run.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_paragraph_text_len_cache.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_parfmt_clears.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_parity_misc.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_parity_round2.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_pdf_export_and_page_renders.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_phase_a_behavior.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_phase_b_headers_footers.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_phase_c_tables.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_positional_cell_id.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_postproc_cell_format_rewrite.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_postproc_ref_restore.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_pr19766_review_fixes.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_proxy_equality.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_ptc.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_python_docx_api_parity.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_read_run_ranges.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_rendered_page_breaks.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_repeated_node_read.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_restart_numbering.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_revisions.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_round_trip_smoke_script.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_ruby_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_run_add_picture_anchor.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_run_break_inner_content.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_run_edges.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_run_formatting_wire.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_run_identity.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_run_later_runs.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_run_mark_inheritance.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_run_page_number_blocks.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_run_text_python_docx.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_run_text_refreshes_paragraph.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_run_unmodeled_toggles.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_sdk_reads_served.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_section_read_lengths.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_sections_empty_list.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_add_paragraph_style.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_add_picture.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_add_run.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_comments_add_comment.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_comments_get.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_document_audit.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_document_element.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_enum_section.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_font_audit.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_header_footer.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_hyperlink.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_inline_shape.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_misc.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_paragraph_strict.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_paragraph_style.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_parfmt.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_row_col_cell.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_run_add_break.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_run_bool_setters.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_run_style.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_run_style_strict.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_run_text.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_run_underline.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_section_audit.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_section_dimensions.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_section_onoff.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_settings.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_shared_audit.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_style.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_styles.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_table_audit.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_table_cell.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_table_dimensions.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_silent_stub_table_layout.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_smoke_integration.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_style_acceptance.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_style_delete.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_style_font.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_style_formatting_writes.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_style_metadata_writes.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_style_names.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_style_resolution.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_style_setters_contract.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_style_tab_stops.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_style_tab_stops_read.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_symbol_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_tab_stop_writes.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_table_add_row_grid_row.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_table_anchors.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_table_clears.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_table_direction.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_table_length_reads.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_table_listing_reads.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_table_read_reuse.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_table_set_cell_perf.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_table_style_id_resolution.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_temporarily_unavailable.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_text_control_characters.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_tracked_revisions_offsets.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_unmapped_alignment_reads.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_uv_lock_version.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_validate_find_replace_asset_script.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_versioned_read_reuse.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_wire_contract.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_xml_attr_guard.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/tests/test_xml_incompatible_text.py +0 -0
- {athena_python_docx-0.41.0 → athena_python_docx-0.42.1}/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
|
|
@@ -1258,6 +1272,30 @@ architecture or a DOCX Studio constraint:
|
|
|
1258
1272
|
changes; the owner may overrule it. Recorded in
|
|
1259
1273
|
`tests/parity/intentional_deviations.json` (`live_deviations`).
|
|
1260
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
|
+
|
|
1261
1299
|
- **Two things a source writes that the model holds otherwise read as the
|
|
1262
1300
|
model holds them** (2026-10-22 SDK live parity run over 398 customer
|
|
1263
1301
|
documents). Each is recorded in
|
|
@@ -1434,6 +1472,22 @@ architecture or a DOCX Studio constraint:
|
|
|
1434
1472
|
exported file. Against a docx-studio that predates `fieldRun` reads, a
|
|
1435
1473
|
field reads as no runs and no text, as before (`tests/test_field_runs.py`).
|
|
1436
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
|
+
|
|
1437
1491
|
- **A run that holds a page number block refuses a tracked whole-run format
|
|
1438
1492
|
and two text writes.** A page number block (`w:pgNum`) reads as a run with
|
|
1439
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.1
|
|
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>
|
|
@@ -29,6 +29,7 @@ alive.
|
|
|
29
29
|
from __future__ import annotations
|
|
30
30
|
|
|
31
31
|
import copy
|
|
32
|
+
import dataclasses
|
|
32
33
|
import json
|
|
33
34
|
import sys
|
|
34
35
|
import threading
|
|
@@ -58,6 +59,7 @@ from docx.errors import DocxError, DocxUnsupportedCommandError, FlushAllError
|
|
|
58
59
|
def _apply_proxy_id_rewrites(
|
|
59
60
|
results: list[Any],
|
|
60
61
|
proxy_id_refs: "dict[str, list[tuple[object, str]]]",
|
|
62
|
+
resolved: "dict[str, str] | None" = None,
|
|
61
63
|
) -> None:
|
|
62
64
|
"""Walk ``results`` and rewrite registered proxy ids from client UUIDs
|
|
63
65
|
to the real DOCX Studio ids the applier echoed back.
|
|
@@ -79,7 +81,7 @@ def _apply_proxy_id_rewrites(
|
|
|
79
81
|
``clientIdMap`` rewrite still resolves them server-side on the
|
|
80
82
|
*next* call.
|
|
81
83
|
"""
|
|
82
|
-
if not proxy_id_refs:
|
|
84
|
+
if not proxy_id_refs and resolved is None:
|
|
83
85
|
return
|
|
84
86
|
for result in results:
|
|
85
87
|
if not isinstance(result, dict):
|
|
@@ -92,6 +94,8 @@ def _apply_proxy_id_rewrites(
|
|
|
92
94
|
real = result.get(real_key)
|
|
93
95
|
if not (isinstance(cli, str) and isinstance(real, str)):
|
|
94
96
|
continue
|
|
97
|
+
if resolved is not None:
|
|
98
|
+
resolved[cli] = real
|
|
95
99
|
refs = proxy_id_refs.pop(cli, [])
|
|
96
100
|
for proxy, attr in refs:
|
|
97
101
|
try:
|
|
@@ -104,6 +108,44 @@ def _apply_proxy_id_rewrites(
|
|
|
104
108
|
pass
|
|
105
109
|
|
|
106
110
|
|
|
111
|
+
_MINTED_ID_FIELDS: frozenset[str] = frozenset({"client_node_id", "client_entity_id"})
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _with_resolved_ids(value: Any, resolved: "dict[str, str]") -> Any:
|
|
115
|
+
"""``value`` with every string that is a client id in ``resolved`` swapped
|
|
116
|
+
for its real id; the same object when nothing changed."""
|
|
117
|
+
if isinstance(value, str):
|
|
118
|
+
return resolved.get(value, value)
|
|
119
|
+
if isinstance(value, dict):
|
|
120
|
+
out = {k: _with_resolved_ids(v, resolved) for k, v in value.items()}
|
|
121
|
+
return value if all(out[k] is value[k] for k in value) else out
|
|
122
|
+
if isinstance(value, list):
|
|
123
|
+
items = [_with_resolved_ids(v, resolved) for v in value]
|
|
124
|
+
return value if all(a is b for a, b in zip(items, value)) else items
|
|
125
|
+
return value
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _resolve_client_ids(cmd: Command, resolved: "dict[str, str]") -> None:
|
|
129
|
+
"""Point ``cmd`` at the real ids of the creates an earlier batch landed.
|
|
130
|
+
|
|
131
|
+
A proxy's own id is rewritten when its create's batch returns, but a copy
|
|
132
|
+
of the client id taken before then (a ``Run``'s paragraph id, the id
|
|
133
|
+
``add_paragraph`` promotes to a list item) still names the client id. Only
|
|
134
|
+
the docx-studio pod that applied the create can resolve that id when the
|
|
135
|
+
replicas share no client-id map, so a later batch that reaches another pod
|
|
136
|
+
fails with ``block "p_…" not found``.
|
|
137
|
+
"""
|
|
138
|
+
if not resolved or not dataclasses.is_dataclass(cmd):
|
|
139
|
+
return
|
|
140
|
+
for f in dataclasses.fields(cmd):
|
|
141
|
+
if f.name in _MINTED_ID_FIELDS:
|
|
142
|
+
continue
|
|
143
|
+
value = getattr(cmd, f.name)
|
|
144
|
+
new = _with_resolved_ids(value, resolved)
|
|
145
|
+
if new is not value:
|
|
146
|
+
setattr(cmd, f.name, new)
|
|
147
|
+
|
|
148
|
+
|
|
107
149
|
_WRITES_UNAPPLIED = "_docx_writes_unapplied"
|
|
108
150
|
|
|
109
151
|
|
|
@@ -551,6 +593,10 @@ class CommandBuffer:
|
|
|
551
593
|
# ``add_heading`` / etc. when they queue a Create with a
|
|
552
594
|
# client-assigned id. Drained on every flush.
|
|
553
595
|
self._proxy_id_refs: dict[str, list[tuple[object, str]]] = {}
|
|
596
|
+
# client id → real id of every create a batch landed, so a command
|
|
597
|
+
# that still names a client id is sent with the real one
|
|
598
|
+
# (:func:`_resolve_client_ids`).
|
|
599
|
+
self._resolved_client_ids: dict[str, str] = {}
|
|
554
600
|
# The last command's ``GetNodeById`` key, result and time, while
|
|
555
601
|
# nothing has been sent or buffered after it (see :meth:`call`), for a
|
|
556
602
|
# docx-studio that gives no version.
|
|
@@ -866,6 +912,7 @@ class CommandBuffer:
|
|
|
866
912
|
user = None if self._user is None else dict(self._user)
|
|
867
913
|
version = self._version_request()
|
|
868
914
|
sent_at = _now()
|
|
915
|
+
self._resolve_drained(cmds)
|
|
869
916
|
try:
|
|
870
917
|
outcome = self._client.execute_batch(
|
|
871
918
|
self._asset_id,
|
|
@@ -944,6 +991,7 @@ class CommandBuffer:
|
|
|
944
991
|
proxy's ``_node_id`` in-place.
|
|
945
992
|
"""
|
|
946
993
|
self.ensure_usable()
|
|
994
|
+
_resolve_client_ids(cmd, self._resolved_client_ids)
|
|
947
995
|
# A write whose text holds a character XML does not allow raises
|
|
948
996
|
# python-docx's ValueError here, before anything is sent: every command
|
|
949
997
|
# comes through, a path-proxy op's and a direct ``send_command``'s.
|
|
@@ -1073,6 +1121,7 @@ class CommandBuffer:
|
|
|
1073
1121
|
self._proxy_id_refs = {}
|
|
1074
1122
|
if not pending:
|
|
1075
1123
|
return []
|
|
1124
|
+
self._resolve_drained(pending)
|
|
1076
1125
|
try:
|
|
1077
1126
|
results, _skipped_by_index = self._client.execute_batch(
|
|
1078
1127
|
self._asset_id,
|
|
@@ -1088,7 +1137,9 @@ class CommandBuffer:
|
|
|
1088
1137
|
# batch ships REAL ids for the survivors (vs. dead client
|
|
1089
1138
|
# UUIDs that would cascade into another "Block not found").
|
|
1090
1139
|
# See ``_apply_proxy_id_rewrites`` for the full rationale.
|
|
1091
|
-
_apply_proxy_id_rewrites(
|
|
1140
|
+
_apply_proxy_id_rewrites(
|
|
1141
|
+
_extract_partial_results(exc), proxy_id_refs, self._resolved_client_ids
|
|
1142
|
+
)
|
|
1092
1143
|
_ptc_emit_end_batch(pending, is_error=True)
|
|
1093
1144
|
_unapplied_writes(exc)
|
|
1094
1145
|
raise
|
|
@@ -1099,7 +1150,7 @@ class CommandBuffer:
|
|
|
1099
1150
|
finally:
|
|
1100
1151
|
_forget_reads_of_others(self)
|
|
1101
1152
|
_ptc_emit_end_batch(pending, is_error=False)
|
|
1102
|
-
_apply_proxy_id_rewrites(results, proxy_id_refs)
|
|
1153
|
+
_apply_proxy_id_rewrites(results, proxy_id_refs, self._resolved_client_ids)
|
|
1103
1154
|
return results
|
|
1104
1155
|
|
|
1105
1156
|
def close(self) -> None:
|
|
@@ -1126,6 +1177,14 @@ class CommandBuffer:
|
|
|
1126
1177
|
|
|
1127
1178
|
# ----- internals -----
|
|
1128
1179
|
|
|
1180
|
+
def _resolve_drained(self, cmds: "list[Command]") -> None:
|
|
1181
|
+
"""Point ``cmds``, about to be sent under ``_post_lock``, at the real
|
|
1182
|
+
ids every earlier batch returned: one queued while the idle timer's
|
|
1183
|
+
batch with its create was still in flight missed that id in
|
|
1184
|
+
:meth:`call`."""
|
|
1185
|
+
for queued in cmds:
|
|
1186
|
+
_resolve_client_ids(queued, self._resolved_client_ids)
|
|
1187
|
+
|
|
1129
1188
|
def _eager_flush_with(self, cmd: Command) -> Any:
|
|
1130
1189
|
"""Drain pending + run ``cmd`` in one batch; return cmd's result.
|
|
1131
1190
|
|
|
@@ -1151,6 +1210,7 @@ class CommandBuffer:
|
|
|
1151
1210
|
self._proxy_id_refs = {}
|
|
1152
1211
|
writes = self._writes
|
|
1153
1212
|
all_cmds: list[Command] = [*pending, cmd]
|
|
1213
|
+
self._resolve_drained(all_cmds)
|
|
1154
1214
|
trailing_index = len(all_cmds) - 1
|
|
1155
1215
|
# A batch that answers a read asks for the document's version (a
|
|
1156
1216
|
# docx-studio that predates it ignores the question).
|
|
@@ -1175,7 +1235,9 @@ class CommandBuffer:
|
|
|
1175
1235
|
# ``table.cell(0, 0)`` → ``tables.get``), so a failure on
|
|
1176
1236
|
# the trailing ``cmd`` would otherwise abandon every
|
|
1177
1237
|
# buffered Create's rewrite at once.
|
|
1178
|
-
_apply_proxy_id_rewrites(
|
|
1238
|
+
_apply_proxy_id_rewrites(
|
|
1239
|
+
_extract_partial_results(exc), proxy_id_refs, self._resolved_client_ids
|
|
1240
|
+
)
|
|
1179
1241
|
_ptc_emit_end_batch([cmd], is_error=True)
|
|
1180
1242
|
if pending:
|
|
1181
1243
|
self._settle_writes_of_failed_read(
|
|
@@ -1198,7 +1260,7 @@ class CommandBuffer:
|
|
|
1198
1260
|
# Proxy-id rewrites still run for the buffered prefix that DID apply —
|
|
1199
1261
|
# the trailing skip (if any) doesn't strand the supported Creates that
|
|
1200
1262
|
# flushed alongside it.
|
|
1201
|
-
_apply_proxy_id_rewrites(results, proxy_id_refs)
|
|
1263
|
+
_apply_proxy_id_rewrites(results, proxy_id_refs, self._resolved_client_ids)
|
|
1202
1264
|
# This is the AWAITED-result path: the caller is about to read the
|
|
1203
1265
|
# trailing command's result. If the server skipped that command
|
|
1204
1266
|
# (capability gap), there is no result to return — returning some
|
|
@@ -1260,11 +1322,13 @@ class CommandBuffer:
|
|
|
1260
1322
|
# ``MAX_BATCH_COMMANDS``: those a failed request's earlier ones
|
|
1261
1323
|
# applied keep their ids, as a flush's do, and only the rest are
|
|
1262
1324
|
# the failure's unapplied writes.
|
|
1263
|
-
_apply_proxy_id_rewrites(
|
|
1325
|
+
_apply_proxy_id_rewrites(
|
|
1326
|
+
_extract_partial_results(again), proxy_id_refs, self._resolved_client_ids
|
|
1327
|
+
)
|
|
1264
1328
|
_ptc_emit_end_batch(pending, is_error=True)
|
|
1265
1329
|
raise _unapplied_writes(again) from exc
|
|
1266
1330
|
_ptc_emit_end_batch(pending, is_error=False)
|
|
1267
|
-
_apply_proxy_id_rewrites(results, proxy_id_refs)
|
|
1331
|
+
_apply_proxy_id_rewrites(results, proxy_id_refs, self._resolved_client_ids)
|
|
1268
1332
|
_as_the_read_s_own_failure(exc, failed)
|
|
1269
1333
|
|
|
1270
1334
|
def _reset_timer_locked(self) -> None:
|
|
@@ -141,7 +141,10 @@ def create_empty_document(
|
|
|
141
141
|
)
|
|
142
142
|
|
|
143
143
|
url: str = resolved_base.rstrip("/") + "/docs/empty"
|
|
144
|
-
|
|
144
|
+
# The document is born as its creator's: its first write is signed as the
|
|
145
|
+
# caller, with the agent run's attributions when the script runs in one
|
|
146
|
+
# (``X-Custom-Attributions``), so the run's turn replay starts with it.
|
|
147
|
+
body: dict[str, str] = {"seedAuthor": "requester"}
|
|
145
148
|
if name is not None:
|
|
146
149
|
body["name"] = name
|
|
147
150
|
if parent_folder_id is not None:
|
|
@@ -152,6 +155,7 @@ def create_empty_document(
|
|
|
152
155
|
# Lazy import — _http is loaded lazily by Document.create(), so the
|
|
153
156
|
# package is fully initialized by the time we reach this code.
|
|
154
157
|
from docx import __version__
|
|
158
|
+
from docx._http_doc import _delegation_headers
|
|
155
159
|
|
|
156
160
|
payload: bytes = json.dumps(body).encode("utf-8")
|
|
157
161
|
req = urllib.request.Request( # noqa: S310
|
|
@@ -163,6 +167,7 @@ def create_empty_document(
|
|
|
163
167
|
"Authorization": f"Bearer {resolved_key}",
|
|
164
168
|
"Accept": "application/json",
|
|
165
169
|
"User-Agent": f"athena-python-docx/{__version__}",
|
|
170
|
+
**_delegation_headers(),
|
|
166
171
|
},
|
|
167
172
|
)
|
|
168
173
|
|
|
@@ -308,7 +313,16 @@ def upload_document(
|
|
|
308
313
|
with open(docx_path, "rb") as f:
|
|
309
314
|
file_bytes: bytes = f.read()
|
|
310
315
|
|
|
311
|
-
|
|
316
|
+
# The document is born holding the uploaded file: the create seeds no
|
|
317
|
+
# template (``seed_template``), and the import below is its first write,
|
|
318
|
+
# signed as the caller with the agent run's attributions when the script
|
|
319
|
+
# runs in one (``seed_author``, ``X-Custom-Attributions``). An agora that
|
|
320
|
+
# predates both seeds the template, which the overwrite import replaces.
|
|
321
|
+
create_body: dict[str, str | bool] = {
|
|
322
|
+
"name": title,
|
|
323
|
+
"seed_author": "requester",
|
|
324
|
+
"seed_template": False,
|
|
325
|
+
}
|
|
312
326
|
if parent_folder_id is not None:
|
|
313
327
|
create_body["parent_folder_id"] = parent_folder_id
|
|
314
328
|
if workspace_id is not None:
|
|
@@ -317,6 +331,7 @@ def upload_document(
|
|
|
317
331
|
create_url: str = resolved_agora_base.rstrip("/") + "/api/docx-studio/assets"
|
|
318
332
|
|
|
319
333
|
from docx import __version__
|
|
334
|
+
from docx._http_doc import _delegation_headers
|
|
320
335
|
|
|
321
336
|
req = urllib.request.Request( # noqa: S310
|
|
322
337
|
create_url,
|
|
@@ -327,6 +342,7 @@ def upload_document(
|
|
|
327
342
|
"Authorization": f"Bearer {resolved_key}",
|
|
328
343
|
"Accept": "application/json",
|
|
329
344
|
"User-Agent": f"athena-python-docx/{__version__}",
|
|
345
|
+
**_delegation_headers(),
|
|
330
346
|
},
|
|
331
347
|
)
|
|
332
348
|
|
|
@@ -374,6 +390,7 @@ def upload_document(
|
|
|
374
390
|
import_body = {
|
|
375
391
|
"docx_base64": base64.b64encode(file_bytes).decode("ascii"),
|
|
376
392
|
"overwrite": True,
|
|
393
|
+
"seed_author": "requester",
|
|
377
394
|
}
|
|
378
395
|
import_req = urllib.request.Request( # noqa: S310
|
|
379
396
|
import_url,
|
|
@@ -384,6 +401,7 @@ def upload_document(
|
|
|
384
401
|
"Authorization": f"Bearer {resolved_key}",
|
|
385
402
|
"Accept": "application/json",
|
|
386
403
|
"User-Agent": f"athena-python-docx/{__version__}",
|
|
404
|
+
**_delegation_headers(),
|
|
387
405
|
},
|
|
388
406
|
)
|
|
389
407
|
|
|
@@ -33,6 +33,7 @@ from __future__ import annotations
|
|
|
33
33
|
import dataclasses
|
|
34
34
|
import json
|
|
35
35
|
import os
|
|
36
|
+
import re
|
|
36
37
|
import urllib.parse
|
|
37
38
|
import warnings
|
|
38
39
|
from typing import Any, TypedDict
|
|
@@ -477,6 +478,9 @@ _PARAGRAPH_TARGETING_COMMANDS: frozenset[str] = frozenset(
|
|
|
477
478
|
)
|
|
478
479
|
|
|
479
480
|
|
|
481
|
+
# The id ``Document._mint_client_node_id`` gives a node it creates.
|
|
482
|
+
_SDK_MINTED_ID = re.compile(r"[a-z]+_[0-9a-f]{12}")
|
|
483
|
+
|
|
480
484
|
_CELL_PARAGRAPH_HINT: str = (
|
|
481
485
|
"\n\nHint: DOCX Studio 1.8.1 cannot format paragraphs nested inside "
|
|
482
486
|
"table cells via SetParagraphAlignment / SetParagraphStyle / "
|
|
@@ -534,6 +538,15 @@ _TABLE_QUERY_COMMANDS: frozenset[str] = frozenset(
|
|
|
534
538
|
)
|
|
535
539
|
|
|
536
540
|
|
|
541
|
+
_SDK_CLIENT_ID_HINT: str = (
|
|
542
|
+
"\n\nHint: this id is one the SDK minted for a block it created "
|
|
543
|
+
"(``p_xxxxxxxxxxxx``), not one docx-studio assigned. The create that "
|
|
544
|
+
"minted it failed or was discarded in an earlier batch, or it belongs "
|
|
545
|
+
"to a previous execution. This is not a table-cell problem: re-read "
|
|
546
|
+
"the block (``doc.paragraphs`` / ``doc.tables``) and use the proxy it "
|
|
547
|
+
"returns, or create the block again."
|
|
548
|
+
)
|
|
549
|
+
|
|
537
550
|
_TABLE_CLIENT_ID_HINT: str = (
|
|
538
551
|
"\n\nHint: this looks like a stale client-side table UUID "
|
|
539
552
|
"(``t_xxxxxxxxxxxx``). Either an earlier batch in this execution "
|
|
@@ -882,8 +895,12 @@ def _http_post_json(
|
|
|
882
895
|
isinstance(cmd_type, str) and cmd_type in _PARAGRAPH_TARGETING_COMMANDS
|
|
883
896
|
)
|
|
884
897
|
table_query = isinstance(cmd_type, str) and cmd_type in _TABLE_QUERY_COMMANDS
|
|
885
|
-
|
|
898
|
+
missing = _missing_block_id(err_obj.get("message"))
|
|
899
|
+
if missing in header_footer_block_ids:
|
|
886
900
|
hint = _HEADER_FOOTER_PARAGRAPH_HINT
|
|
901
|
+
elif missing is not None and _SDK_MINTED_ID.fullmatch(missing):
|
|
902
|
+
# A block this SDK created, not a cell's own paragraph.
|
|
903
|
+
hint = _SDK_CLIENT_ID_HINT
|
|
887
904
|
elif paragraph_targeting:
|
|
888
905
|
hint = _CELL_PARAGRAPH_HINT
|
|
889
906
|
elif table_query:
|
|
@@ -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:
|
|
@@ -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.1"
|
|
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:
|
|
@@ -417,6 +417,16 @@ FIXTURE_SCRIPTS: list[FixtureScript] = [
|
|
|
417
417
|
"caption.runs[4].italic = True\n"
|
|
418
418
|
),
|
|
419
419
|
),
|
|
420
|
+
FixtureScript(
|
|
421
|
+
# A run added to a footer paragraph whose one run holds "Page " and a
|
|
422
|
+
# whole PAGE field: python-docx appends a w:r after it; the SDK adds
|
|
423
|
+
# its run after the field, and the export writes the edited paragraph
|
|
424
|
+
# with the text, the field and the new run each a run of its own (the
|
|
425
|
+
# recorded run split, #46935 item A4), keeping "Page ".
|
|
426
|
+
name="fixture-footers-text-and-field-one-run-writes",
|
|
427
|
+
fixture="footers-text-and-field-one-run",
|
|
428
|
+
script=('doc.sections[0].footer.paragraphs[0].add_run(" (draft)")\n'),
|
|
429
|
+
),
|
|
420
430
|
FixtureScript(
|
|
421
431
|
# Run writes in a header whose style turns bold on and a footer whose
|
|
422
432
|
# fields are each written whole in one run, read back one `w:r` at a
|
|
@@ -711,6 +721,75 @@ FIXTURE_SCRIPTS: list[FixtureScript] = [
|
|
|
711
721
|
'last.header.paragraphs[0].add_run(" (revised)")\n'
|
|
712
722
|
),
|
|
713
723
|
),
|
|
724
|
+
FixtureScript(
|
|
725
|
+
# Runs added to the paragraphs that anchor a VML and a DrawingML text
|
|
726
|
+
# box whose text is not wrapped, and a paragraph added after them:
|
|
727
|
+
# python-docx writes the runs after the boxes' runs and keeps the
|
|
728
|
+
# boxes as they are, as does the SDK.
|
|
729
|
+
name="fixture-textboxes-no-wrap-writes",
|
|
730
|
+
fixture="textboxes-no-wrap",
|
|
731
|
+
script=(
|
|
732
|
+
'doc.paragraphs[0].add_run(" (anchor kept)").bold = True\n'
|
|
733
|
+
'doc.paragraphs[2].add_run(" (second anchor kept)")\n'
|
|
734
|
+
'doc.add_paragraph("A paragraph added after the boxes.")\n'
|
|
735
|
+
),
|
|
736
|
+
),
|
|
737
|
+
FixtureScript(
|
|
738
|
+
# Writes to the header row of a table that names a style and has no
|
|
739
|
+
# w:tblLook, which Word reads by its default look: the cells' text,
|
|
740
|
+
# a run added and one made italic, a paragraph added to a header cell
|
|
741
|
+
# and a row added. python-docx writes none of the style's bold as the
|
|
742
|
+
# runs' own and no look, nor does the SDK.
|
|
743
|
+
name="fixture-tables-default-look-writes",
|
|
744
|
+
fixture="tables-default-look",
|
|
745
|
+
script=(
|
|
746
|
+
"table = doc.tables[0]\n"
|
|
747
|
+
'table.cell(0, 1).text = "Summary"\n'
|
|
748
|
+
'table.cell(0, 0).paragraphs[0].add_run(" (code)")\n'
|
|
749
|
+
"table.cell(0, 2).paragraphs[0].runs[0].italic = True\n"
|
|
750
|
+
'table.cell(0, 2).add_paragraph("Region")\n'
|
|
751
|
+
"row = table.add_row()\n"
|
|
752
|
+
'row.cells[0].text = "Delta"\n'
|
|
753
|
+
),
|
|
754
|
+
),
|
|
755
|
+
FixtureScript(
|
|
756
|
+
# Writes to a table merged the legacy way (w:hMerge), which python-docx
|
|
757
|
+
# reads as written, each w:tc its own cell: text added after the
|
|
758
|
+
# nested table of a merge's first cell, a merge's first cell and a
|
|
759
|
+
# cell that continues it rewritten, bold on a row's runs, and a row
|
|
760
|
+
# added. The export writes each cell's w:hMerge back, as python-docx
|
|
761
|
+
# keeps it.
|
|
762
|
+
name="fixture-tables-horizontal-merges-writes",
|
|
763
|
+
fixture="tables-horizontal-merges",
|
|
764
|
+
script=(
|
|
765
|
+
"table = doc.tables[0]\n"
|
|
766
|
+
'table.cell(0, 0).paragraphs[-1].add_run("Issued")\n'
|
|
767
|
+
'table.cell(2, 0).text = "Merged text rewritten"\n'
|
|
768
|
+
'table.cell(2, 1).text = "Continued"\n'
|
|
769
|
+
"for run in table.cell(1, 3).paragraphs[0].runs:\n"
|
|
770
|
+
" run.bold = True\n"
|
|
771
|
+
"row = table.add_row()\n"
|
|
772
|
+
'row.cells[0].text = "Beta"\n'
|
|
773
|
+
),
|
|
774
|
+
),
|
|
775
|
+
FixtureScript(
|
|
776
|
+
# Writes to tables whose rows of one cell set no span in a grid of two
|
|
777
|
+
# columns: python-docx reads such a row's one cell as written, and
|
|
778
|
+
# writes it so; a row added after one, and a column added, as
|
|
779
|
+
# python-docx adds them (a w:tc in every row).
|
|
780
|
+
name="fixture-tables-implied-span-writes",
|
|
781
|
+
fixture="tables-implied-span",
|
|
782
|
+
script=(
|
|
783
|
+
"form = doc.tables[0]\n"
|
|
784
|
+
'form.rows[2].cells[0].text = "Reviewed"\n'
|
|
785
|
+
'form.rows[4].cells[0].paragraphs[0].add_run(" (internal)")\n'
|
|
786
|
+
'form.rows[5].cells[1].text = "Closed"\n'
|
|
787
|
+
"closing = doc.tables[1]\n"
|
|
788
|
+
"row = closing.add_row()\n"
|
|
789
|
+
'row.cells[0].text = "Total"\n'
|
|
790
|
+
"closing.add_column(Pt(72))\n"
|
|
791
|
+
),
|
|
792
|
+
),
|
|
714
793
|
FixtureScript(
|
|
715
794
|
# Writes to a footer part written with a header's root element and to
|
|
716
795
|
# a header part written with a footer's: python-docx edits the part's
|