athena-python-docx 0.42.0__tar.gz → 0.43.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.42.0 → athena_python_docx-0.43.0}/PKG-INFO +1 -1
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/__init__.py +1 -1
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_buffer.py +71 -7
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_http.py +52 -34
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_http_doc.py +18 -1
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/document.py +9 -2
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/pyproject.toml +1 -1
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/live/fixture_scripts.py +38 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_block_not_found_error.py +24 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_buffer.py +86 -0
- athena_python_docx-0.43.0/tests/test_create_workspace.py +82 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_document_create.py +74 -15
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_document_create_from_template.py +93 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/uv.lock +1 -1
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/.gitignore +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/AGENTS.md +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/CLAUDE.md +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/DOCX_EXEC_LAB.md +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/README.md +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_athena_extension.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_batching.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_capabilities.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_citations.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_execution.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_image_utils.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_postproc.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_proxy.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_ptc.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_references.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_run_order.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_table_styles.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_timeouts.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_tracked.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_xml_text.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/api.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/blkcntnr.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/bookmarks.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/charts.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/client.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/commands.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/comments.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/drawing/__init__.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/enum/__init__.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/enum/base.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/enum/dml.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/enum/section.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/enum/shape.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/enum/style.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/enum/table.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/enum/text.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/errors.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/exceptions.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/fields.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/footnotes.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/image/__init__.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/image/exceptions.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/image/image.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/math.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/opc/__init__.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/opc/coreprops.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/oxml/__init__.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/revisions.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/sdt.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/section.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/session.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/settings.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/shape.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/shared.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/styles/__init__.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/styles/_table.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/styles/latent.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/styles/style.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/styles/styles.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/table.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/text/__init__.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/text/font.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/text/hyperlink.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/text/pagebreak.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/text/paragraph.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/text/parfmt.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/text/run.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/text/tabstops.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/toc.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/typing.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/scripts/docx_exec_lab.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/scripts/docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/scripts/dump_wire_fixtures.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/scripts/release.sh +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/scripts/round_trip_smoke.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/scripts/smoke_test_block_not_found.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/scripts/validate_find_replace_asset.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/__init__.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/conftest.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/METHODOLOGY.md +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/README.md +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/__init__.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/__init__.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/agent_documents.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/default_style_run_formatting.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/header_footer_pictures.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/less_used_writes.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/merged_cell_identity.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/paragraph_text_setter.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/picture_runs.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/proxy_equality.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/return_types.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/xml_incompatible_text.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/zero_and_negative_indents.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe_cases.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe_runner.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/auto_gen_cases.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/binary_round_trip.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/cases.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/complex_cases.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/coverage_report.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/extract.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/extreme_cases.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/fake_session.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/firm_templates/README.md +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/firm_templates/__init__.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/firm_templates/_runner.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/firm_templates/extractor.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/local_runner.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/mega_cases.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshot.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw16_header_footer_tables.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw17_header_letterhead_filled.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw18_header_table_properties.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw19_header_table_rows_columns.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw20_header_table_merges.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ours_spec.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/parity_crawl.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/parity_diff.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/real_world_cases.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/round_trip_tests.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/stock_spec.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fixtures/field_reads.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fixtures/note_reference_reads.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fixtures/ruby_reads.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fixtures/symbol_reads.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/live/__init__.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/live/baseline.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/live/reader.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/live/runner.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/live/sdk_driver.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/live/stock_driver.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/live/test_live_runner.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/README.md +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/__init__.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/baseline_gaps.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/compare.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/intentional_deviations.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/introspect.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/reports/gap_report.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/run_parity.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/snapshots/athena_latest.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/test_parity_gap.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_add_section.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_add_section_extract_items.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_alignment_values.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_athena_extension_creates_session_open.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_athena_extensions_contract.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_athena_extensions_registry.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_batch_chunking.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_batching_perf.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_battery_cases.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_add_table_not_supported.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_borders_spec.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_fastpath_runs.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_inner_format_stash.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_inner_run_format_stash.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_inner_run_guard.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_merge.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_nested_tables.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_paragraphs_listed.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_text_plain_fastpath.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_text_replace_semantics.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_citations.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_collapsed_range_format.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_color_format_theme.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_command_dataclasses.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_commands.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_comment_paragraphs.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_comments.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_comments_first_read.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_content_control_value.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_core_properties.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_document_asset_id_property.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_document_clear.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_document_factory_validation.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_document_settings_session_open.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_docx_exec_lab.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_drawing_image.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_empty_hyperlinks.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_enum_parity.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_execution_scope.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_export_docx_clean.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_export_rescue_anchors.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_field_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_field_refresh.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_field_runs.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_find_replace_session_open.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_flushed_write_failures.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_http_transport.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_hyperlink_coalescing.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_image_url_data_uri.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_inline_page_break.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_inline_shape_alt_text.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_inline_shape_size.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_inline_shape_type.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_inline_shapes_floating.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_insert_deferred.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_iter_inner_content.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_latent_style_writes.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_line_clear_break.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_list_styles.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_listed_paragraph_reads.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_literal_no_break_hyphens.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_literal_soft_hyphens.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_merged_cell_identity.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_merged_cell_secondary_slot.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_merged_cells.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_missing_modules.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_nested_table_refused.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_note_reference_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_oxml_shim.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_page_break.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_paragraph_delete.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_paragraph_indent_reads.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_paragraph_runs_hyperlinks.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_paragraph_text_empty_run.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_paragraph_text_len_cache.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_parfmt_clears.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_parity_misc.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_parity_round2.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_pdf_export_and_page_renders.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_phase_a_behavior.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_phase_b_headers_footers.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_phase_c_tables.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_positional_cell_id.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_postproc_ref_restore.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_pr19766_review_fixes.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_proxy_equality.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_ptc.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_python_docx_api_parity.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_read_run_ranges.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_rendered_page_breaks.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_repeated_node_read.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_restart_numbering.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_revisions.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_round_trip_smoke_script.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_ruby_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_add_picture_anchor.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_break_inner_content.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_edges.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_formatting_wire.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_identity.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_later_runs.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_mark_inheritance.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_page_number_blocks.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_table_style_objects.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_text_python_docx.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_text_refreshes_paragraph.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_unmodeled_toggles.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_sdk_reads_served.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_section_read_lengths.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_sections_empty_list.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_add_picture.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_add_run.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_comments_get.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_document_audit.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_document_element.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_enum_section.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_font_audit.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_header_footer.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_hyperlink.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_inline_shape.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_misc.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_paragraph_style.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_parfmt.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_row_col_cell.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_run_add_break.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_run_style.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_run_style_strict.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_run_text.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_run_underline.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_section_audit.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_section_dimensions.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_section_onoff.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_settings.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_shared_audit.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_style.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_styles.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_table_audit.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_table_cell.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_table_dimensions.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_table_layout.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_smoke_integration.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_acceptance.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_delete.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_font.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_formatting_writes.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_metadata_writes.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_names.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_resolution.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_setters_contract.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_tab_stops.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_tab_stops_read.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_symbol_reads_python_docx_parity.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_tab_stop_writes.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_table_add_row_grid_row.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_table_anchors.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_table_clears.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_table_direction.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_table_length_reads.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_table_listing_reads.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_table_read_reuse.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_table_set_cell_perf.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_table_style_id_resolution.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_temporarily_unavailable.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_text_control_characters.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_tracked_revisions_offsets.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_unmapped_alignment_reads.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_uv_lock_version.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_validate_find_replace_asset_script.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_versioned_read_reuse.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_wire_contract.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_xml_attr_guard.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_xml_incompatible_text.py +0 -0
- {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_zod_wire_contract.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: athena-python-docx
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.43.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>
|
|
@@ -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:
|
|
@@ -38,6 +38,20 @@ _API_KEY_ENV = "ATHENA_DOCX_API_KEY" # noqa: S105
|
|
|
38
38
|
_AGORA_BASE_URL_ENV = "ATHENA_AGORA_BASE_URL"
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
def _resolve_api_key(api_key: str | None) -> str | None:
|
|
42
|
+
"""The key a create or upload sends: ``api_key``, else the studio key.
|
|
43
|
+
|
|
44
|
+
``ATHENA_DOCX_API_KEY`` comes first, as on every other call this SDK
|
|
45
|
+
makes. Both create paths land outside Athena's public API (docx-studio's
|
|
46
|
+
``/docs/empty``, agora's ``/api/docx-studio/assets``), and inside an Athena
|
|
47
|
+
session VM or computer ``ATHENA_API_KEY`` is a credential only that public
|
|
48
|
+
API (``/api/v0``) accepts. ``ATHENA_API_KEY`` stays the fallback for an
|
|
49
|
+
environment that sets no studio key (local use with one personal key).
|
|
50
|
+
The new document is owned by the user the key belongs to.
|
|
51
|
+
"""
|
|
52
|
+
return api_key or os.environ.get(_API_KEY_ENV) or os.environ.get("ATHENA_API_KEY")
|
|
53
|
+
|
|
54
|
+
|
|
41
55
|
def _derive_agora_base_url(docx_base_url: str) -> str | None:
|
|
42
56
|
"""Best-effort derive an Agora base URL from a docx-studio base URL.
|
|
43
57
|
|
|
@@ -99,11 +113,11 @@ def create_empty_document(
|
|
|
99
113
|
Falls back to ``$ATHENA_DOCX_BASE_URL``.
|
|
100
114
|
api_key: Athena API key (PropelAuth user API key) or PropelAuth
|
|
101
115
|
access token. Sent as ``Authorization: Bearer <api_key>``.
|
|
102
|
-
Falls back to ``$ATHENA_DOCX_API_KEY``.
|
|
116
|
+
Falls back to ``$ATHENA_DOCX_API_KEY``, then ``$ATHENA_API_KEY``.
|
|
103
117
|
name: Optional display title.
|
|
104
118
|
parent_folder_id: Optional parent folder; defaults to workspace root.
|
|
105
|
-
workspace_id: Optional workspace UUID; defaults to
|
|
106
|
-
current workspace.
|
|
119
|
+
workspace_id: Optional workspace UUID; defaults to the sandbox run's
|
|
120
|
+
ATHENA_WORKSPACE_ID, then the caller's current workspace.
|
|
107
121
|
timeout: Request timeout in seconds.
|
|
108
122
|
|
|
109
123
|
Returns:
|
|
@@ -115,17 +129,7 @@ def create_empty_document(
|
|
|
115
129
|
DocxError: any other 4xx/5xx from the server.
|
|
116
130
|
"""
|
|
117
131
|
resolved_base: str | None = base_url or os.environ.get(_BASE_URL_ENV)
|
|
118
|
-
|
|
119
|
-
# is the user, not a service identity. ``ATHENA_API_KEY`` is the canonical
|
|
120
|
-
# per-user Athena key (a local API key in BYO/SSO mode that agora resolves to
|
|
121
|
-
# the real user); the docx-studio studio key (``ATHENA_DOCX_API_KEY``) is a
|
|
122
|
-
# deployment service credential used only for studio access-checks on
|
|
123
|
-
# EXISTING assets, and agora's create gate rejects it (no backing user). So
|
|
124
|
-
# prefer the user key here; only fall back to the studio key for callers that
|
|
125
|
-
# set just that.
|
|
126
|
-
resolved_key: str | None = (
|
|
127
|
-
api_key or os.environ.get("ATHENA_API_KEY") or os.environ.get(_API_KEY_ENV)
|
|
128
|
-
)
|
|
132
|
+
resolved_key: str | None = _resolve_api_key(api_key)
|
|
129
133
|
|
|
130
134
|
if not resolved_base:
|
|
131
135
|
raise SessionError(
|
|
@@ -141,17 +145,22 @@ def create_empty_document(
|
|
|
141
145
|
)
|
|
142
146
|
|
|
143
147
|
url: str = resolved_base.rstrip("/") + "/docs/empty"
|
|
144
|
-
|
|
148
|
+
# The document is born as its creator's: its first write is signed as the
|
|
149
|
+
# caller, with the agent run's attributions when the script runs in one
|
|
150
|
+
# (``X-Custom-Attributions``), so the run's turn replay starts with it.
|
|
151
|
+
body: dict[str, str] = {"seedAuthor": "requester"}
|
|
145
152
|
if name is not None:
|
|
146
153
|
body["name"] = name
|
|
147
154
|
if parent_folder_id is not None:
|
|
148
155
|
body["parentFolderId"] = parent_folder_id
|
|
149
|
-
|
|
150
|
-
|
|
156
|
+
resolved_workspace = workspace_id or os.environ.get("ATHENA_WORKSPACE_ID")
|
|
157
|
+
if resolved_workspace:
|
|
158
|
+
body["workspaceId"] = resolved_workspace
|
|
151
159
|
|
|
152
160
|
# Lazy import — _http is loaded lazily by Document.create(), so the
|
|
153
161
|
# package is fully initialized by the time we reach this code.
|
|
154
162
|
from docx import __version__
|
|
163
|
+
from docx._http_doc import _delegation_headers
|
|
155
164
|
|
|
156
165
|
payload: bytes = json.dumps(body).encode("utf-8")
|
|
157
166
|
req = urllib.request.Request( # noqa: S310
|
|
@@ -163,6 +172,7 @@ def create_empty_document(
|
|
|
163
172
|
"Authorization": f"Bearer {resolved_key}",
|
|
164
173
|
"Accept": "application/json",
|
|
165
174
|
"User-Agent": f"athena-python-docx/{__version__}",
|
|
175
|
+
**_delegation_headers(),
|
|
166
176
|
},
|
|
167
177
|
)
|
|
168
178
|
|
|
@@ -227,6 +237,7 @@ def upload_document(
|
|
|
227
237
|
name: str | None = None,
|
|
228
238
|
parent_folder_id: str | None = None,
|
|
229
239
|
workspace_id: str | None = None,
|
|
240
|
+
source_asset_id: str | None = None,
|
|
230
241
|
timeout: float = 120.0,
|
|
231
242
|
) -> UploadAssetResult:
|
|
232
243
|
"""Upload a local ``.docx`` to agora and create a new Word document asset.
|
|
@@ -242,11 +253,12 @@ def upload_document(
|
|
|
242
253
|
agora_base_url: Explicit Agora base URL. Falls back to
|
|
243
254
|
``$ATHENA_AGORA_BASE_URL`` and finally to a host substitution
|
|
244
255
|
of ``docx-studio`` → ``agora`` against ``docx_base_url``.
|
|
245
|
-
api_key: Athena API key. Falls back to ``$ATHENA_DOCX_API_KEY
|
|
256
|
+
api_key: Athena API key. Falls back to ``$ATHENA_DOCX_API_KEY``, then
|
|
257
|
+
``$ATHENA_API_KEY``.
|
|
246
258
|
name: Optional asset title (defaults to the file's stem).
|
|
247
259
|
parent_folder_id: Optional parent folder for the new asset.
|
|
248
|
-
workspace_id: Optional workspace UUID; defaults to the
|
|
249
|
-
current workspace
|
|
260
|
+
workspace_id: Optional workspace UUID; defaults to the sandbox run's
|
|
261
|
+
ATHENA_WORKSPACE_ID, then the caller's current workspace in Agora.
|
|
250
262
|
timeout: Request timeout in seconds (uploads can be slow for big
|
|
251
263
|
decks/templates).
|
|
252
264
|
|
|
@@ -262,17 +274,7 @@ def upload_document(
|
|
|
262
274
|
import os
|
|
263
275
|
|
|
264
276
|
resolved_docx_base: str | None = docx_base_url or os.environ.get(_BASE_URL_ENV)
|
|
265
|
-
|
|
266
|
-
# is the user, not a service identity. ``ATHENA_API_KEY`` is the canonical
|
|
267
|
-
# per-user Athena key (a local API key in BYO/SSO mode that agora resolves to
|
|
268
|
-
# the real user); the docx-studio studio key (``ATHENA_DOCX_API_KEY``) is a
|
|
269
|
-
# deployment service credential used only for studio access-checks on
|
|
270
|
-
# EXISTING assets, and agora's create gate rejects it (no backing user). So
|
|
271
|
-
# prefer the user key here; only fall back to the studio key for callers that
|
|
272
|
-
# set just that.
|
|
273
|
-
resolved_key: str | None = (
|
|
274
|
-
api_key or os.environ.get("ATHENA_API_KEY") or os.environ.get(_API_KEY_ENV)
|
|
275
|
-
)
|
|
277
|
+
resolved_key: str | None = _resolve_api_key(api_key)
|
|
276
278
|
resolved_agora_base: str | None = (
|
|
277
279
|
agora_base_url
|
|
278
280
|
or os.environ.get(_AGORA_BASE_URL_ENV)
|
|
@@ -308,15 +310,28 @@ def upload_document(
|
|
|
308
310
|
with open(docx_path, "rb") as f:
|
|
309
311
|
file_bytes: bytes = f.read()
|
|
310
312
|
|
|
311
|
-
|
|
313
|
+
# The document is born holding the uploaded file: the create seeds no
|
|
314
|
+
# template (``seed_template``), and the import below is its first write,
|
|
315
|
+
# signed as the caller with the agent run's attributions when the script
|
|
316
|
+
# runs in one (``seed_author``, ``X-Custom-Attributions``). An agora that
|
|
317
|
+
# predates both seeds the template, which the overwrite import replaces.
|
|
318
|
+
create_body: dict[str, str | bool] = {
|
|
319
|
+
"name": title,
|
|
320
|
+
"seed_author": "requester",
|
|
321
|
+
"seed_template": False,
|
|
322
|
+
}
|
|
323
|
+
if source_asset_id is not None:
|
|
324
|
+
create_body["source_asset_id"] = source_asset_id
|
|
312
325
|
if parent_folder_id is not None:
|
|
313
326
|
create_body["parent_folder_id"] = parent_folder_id
|
|
314
|
-
|
|
315
|
-
|
|
327
|
+
resolved_workspace = workspace_id or os.environ.get("ATHENA_WORKSPACE_ID")
|
|
328
|
+
if resolved_workspace:
|
|
329
|
+
create_body["workspace_id"] = resolved_workspace
|
|
316
330
|
|
|
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:
|
|
@@ -259,6 +259,7 @@ class Document:
|
|
|
259
259
|
track_revisions: bool = False,
|
|
260
260
|
docx: str | None = None,
|
|
261
261
|
agora_base_url: str | None = None,
|
|
262
|
+
source_asset_id: str | None = None,
|
|
262
263
|
) -> "Document":
|
|
263
264
|
"""Create a new Word document asset and return an opened Document.
|
|
264
265
|
|
|
@@ -279,9 +280,14 @@ class Document:
|
|
|
279
280
|
base_url: docx-studio API base URL. Falls back to
|
|
280
281
|
``$ATHENA_DOCX_BASE_URL``.
|
|
281
282
|
api_key: Athena API key (PropelAuth user API key) or
|
|
282
|
-
access token. Falls back to ``$ATHENA_DOCX_API_KEY
|
|
283
|
+
access token. Falls back to ``$ATHENA_DOCX_API_KEY``, then
|
|
284
|
+
``$ATHENA_API_KEY``, as every other call does; the upload path
|
|
285
|
+
(``docx=``) sends the same key to agora.
|
|
283
286
|
parent_folder_id: Optional parent folder; defaults to
|
|
284
|
-
|
|
287
|
+
the source document's folder when source_asset_id is set,
|
|
288
|
+
otherwise workspace root.
|
|
289
|
+
source_asset_id: Original asset when creating a copy from an export.
|
|
290
|
+
Agora checks source access and destination edit permission.
|
|
285
291
|
workspace_id: Optional workspace UUID; defaults to the
|
|
286
292
|
caller's current workspace as resolved by Agora.
|
|
287
293
|
docx: Optional path to a local ``.docx`` file. When provided,
|
|
@@ -325,6 +331,7 @@ class Document:
|
|
|
325
331
|
name=name,
|
|
326
332
|
parent_folder_id=parent_folder_id,
|
|
327
333
|
workspace_id=workspace_id,
|
|
334
|
+
source_asset_id=source_asset_id,
|
|
328
335
|
)
|
|
329
336
|
asset_id = upload_result["asset_id"]
|
|
330
337
|
else:
|
|
@@ -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.43.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"
|
|
@@ -752,6 +752,44 @@ FIXTURE_SCRIPTS: list[FixtureScript] = [
|
|
|
752
752
|
'row.cells[0].text = "Delta"\n'
|
|
753
753
|
),
|
|
754
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
|
+
),
|
|
755
793
|
FixtureScript(
|
|
756
794
|
# Writes to a footer part written with a header's root element and to
|
|
757
795
|
# a header part written with a footer's: python-docx edits the part's
|
|
@@ -123,6 +123,30 @@ def test_insert_block_not_found_carries_cell_hint() -> None:
|
|
|
123
123
|
)
|
|
124
124
|
|
|
125
125
|
|
|
126
|
+
def test_sdk_minted_id_block_not_found_names_the_stale_client_id() -> None:
|
|
127
|
+
"""A miss on an id the SDK minted (``p_<12 hex>``) is a create that never
|
|
128
|
+
landed, not a cell paragraph: the hint says so instead of the cell one."""
|
|
129
|
+
|
|
130
|
+
def _capture(url: str, **kwargs: Any) -> _FakeResponse: # noqa: ARG001
|
|
131
|
+
return _FakeResponse(
|
|
132
|
+
status_code=207,
|
|
133
|
+
body=_partial_failure_body(
|
|
134
|
+
cmd_type="Insert",
|
|
135
|
+
message='Block "p_7b3a2a6afbf3" not found.',
|
|
136
|
+
),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
client = HttpClient(base_url="https://example.com", api_key="bg_key")
|
|
140
|
+
|
|
141
|
+
with patch.object(client._session, "post", side_effect=_capture):
|
|
142
|
+
with pytest.raises(BlockNotFoundError) as exc_info:
|
|
143
|
+
_execute_setalignment(client)
|
|
144
|
+
|
|
145
|
+
msg = str(exc_info.value)
|
|
146
|
+
assert "the SDK minted" in msg
|
|
147
|
+
assert "cell.add_paragraph" not in msg
|
|
148
|
+
|
|
149
|
+
|
|
126
150
|
def test_non_paragraph_command_block_not_found_skips_hint() -> None:
|
|
127
151
|
"""A ``Block ... not found`` miss on a non-paragraph-targeting
|
|
128
152
|
command (a stale image id from ``SetImageSize``, a deleted list
|
|
@@ -670,3 +670,89 @@ def test_discard_all_skips_stale_buffers() -> None:
|
|
|
670
670
|
buf.close()
|
|
671
671
|
finally:
|
|
672
672
|
begin_execution(None)
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
class _EchoingClient(_FakeClient):
|
|
676
|
+
"""Answers each create with its real id, as docx-studio echoes it."""
|
|
677
|
+
|
|
678
|
+
def execute_batch(
|
|
679
|
+
self, asset_id: str, commands: list[Command], **kw: Any
|
|
680
|
+
) -> tuple[list[Any], dict[int, dict[str, Any]]]:
|
|
681
|
+
super().execute_batch(asset_id, commands, **kw)
|
|
682
|
+
results: list[Any] = []
|
|
683
|
+
for cmd in commands:
|
|
684
|
+
cli = getattr(cmd, "client_node_id", None)
|
|
685
|
+
results.append({"client_node_id": cli, "real_node_id": f"REAL-{cli}"} if cli else {})
|
|
686
|
+
return results, {}
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
def test_a_client_id_copied_before_its_create_landed_is_sent_as_the_real_id() -> None:
|
|
690
|
+
"""``add_paragraph(style="List Bullet")`` and ``p.add_run().add_break()``
|
|
691
|
+
keep the paragraph's client id; when a read flushed the create first, the
|
|
692
|
+
next batch may reach a docx-studio pod that never minted it."""
|
|
693
|
+
fake = _EchoingClient()
|
|
694
|
+
buf = CommandBuffer(fake, "asset_1", auto_flush_seconds=0.0)
|
|
695
|
+
buf.call(CreateParagraph(text="one", at={"kind": "documentEnd"}, client_node_id="p_abc"))
|
|
696
|
+
buf.flush()
|
|
697
|
+
buf.call(
|
|
698
|
+
Replace(
|
|
699
|
+
target={
|
|
700
|
+
"kind": "selection",
|
|
701
|
+
"start": {"kind": "text", "blockId": "p_abc", "offset": 0},
|
|
702
|
+
},
|
|
703
|
+
text="y",
|
|
704
|
+
)
|
|
705
|
+
)
|
|
706
|
+
buf.call(
|
|
707
|
+
CreateParagraph(
|
|
708
|
+
text="two", at={"kind": "after", "target": {"nodeId": "p_abc"}}, client_node_id="p_def"
|
|
709
|
+
)
|
|
710
|
+
)
|
|
711
|
+
buf.flush()
|
|
712
|
+
replace, create = fake.batches[1]
|
|
713
|
+
assert replace.target == {
|
|
714
|
+
"kind": "selection",
|
|
715
|
+
"start": {"kind": "text", "blockId": "REAL-p_abc", "offset": 0},
|
|
716
|
+
}
|
|
717
|
+
assert create.at == {"kind": "after", "target": {"nodeId": "REAL-p_abc"}}
|
|
718
|
+
assert create.client_node_id == "p_def"
|
|
719
|
+
buf.close()
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
class _HeldEchoingClient(_EchoingClient):
|
|
723
|
+
"""An echoing client whose first batch waits for ``release``."""
|
|
724
|
+
|
|
725
|
+
def __init__(self) -> None:
|
|
726
|
+
super().__init__()
|
|
727
|
+
self.sent = threading.Event()
|
|
728
|
+
self.release = threading.Event()
|
|
729
|
+
|
|
730
|
+
def execute_batch(
|
|
731
|
+
self, asset_id: str, commands: list[Command], **kw: Any
|
|
732
|
+
) -> tuple[list[Any], dict[int, dict[str, Any]]]:
|
|
733
|
+
if not self.batches:
|
|
734
|
+
self.sent.set()
|
|
735
|
+
assert self.release.wait(5)
|
|
736
|
+
return super().execute_batch(asset_id, commands, **kw)
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
def test_a_command_queued_while_its_create_is_in_flight_is_sent_as_the_real_id() -> None:
|
|
740
|
+
"""The idle timer's batch carries the create; an edit queued before that
|
|
741
|
+
batch answers still goes out naming the paragraph by its real id."""
|
|
742
|
+
fake = _HeldEchoingClient()
|
|
743
|
+
buf = CommandBuffer(fake, "asset_1", auto_flush_seconds=0.0)
|
|
744
|
+
buf.call(CreateParagraph(text="one", at={"kind": "documentEnd"}, client_node_id="p_abc"))
|
|
745
|
+
in_flight = threading.Thread(target=buf.flush)
|
|
746
|
+
in_flight.start()
|
|
747
|
+
assert fake.sent.wait(5)
|
|
748
|
+
buf.call(
|
|
749
|
+
CreateParagraph(
|
|
750
|
+
text="two", at={"kind": "after", "target": {"nodeId": "p_abc"}}, client_node_id="p_def"
|
|
751
|
+
)
|
|
752
|
+
)
|
|
753
|
+
fake.release.set()
|
|
754
|
+
in_flight.join(5)
|
|
755
|
+
buf.flush()
|
|
756
|
+
(create,) = fake.batches[1]
|
|
757
|
+
assert create.at == {"kind": "after", "target": {"nodeId": "REAL-p_abc"}}
|
|
758
|
+
buf.close()
|