athena-python-docx 0.18.3__tar.gz → 0.19.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.18.3 → athena_python_docx-0.19.0}/CLAUDE.md +16 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/PKG-INFO +2 -1
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/__init__.py +1 -1
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_buffer.py +40 -6
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_http_doc.py +88 -17
- athena_python_docx-0.19.0/docx/_references.py +82 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/commands.py +48 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/document.py +17 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/errors.py +34 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/text/paragraph.py +92 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/pyproject.toml +7 -1
- athena_python_docx-0.19.0/scripts/dump_wire_fixtures.py +59 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/scripts/release.sh +9 -12
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_athena_extensions_registry.py +3 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_batching_perf.py +4 -8
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_buffer.py +119 -4
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_http_transport.py +159 -6
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_ptc.py +2 -2
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_revisions.py +18 -47
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/uv.lock +11 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/.gitignore +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/DOCX_EXEC_LAB.md +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/README.md +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_athena_extension.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_batching.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_execution.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_http.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_image_utils.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_postproc.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_ptc.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_table_styles.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_timeouts.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/api.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/bookmarks.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/charts.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/client.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/comments.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/enum/__init__.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/enum/section.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/enum/style.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/enum/table.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/enum/text.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/exceptions.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/fields.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/footnotes.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/math.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/opc/__init__.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/opc/coreprops.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/oxml/__init__.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/revisions.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/sdt.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/section.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/session.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/settings.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/shape.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/shared.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/styles/__init__.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/styles/style.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/styles/styles.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/table.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/text/__init__.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/text/font.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/text/hyperlink.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/text/pagebreak.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/text/parfmt.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/text/run.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/text/tabstops.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/toc.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/typing.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/scripts/docx_exec_lab.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/scripts/docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/scripts/publish.sh +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/scripts/round_trip_smoke.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/scripts/smoke_test_block_not_found.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/scripts/validate_find_replace_asset.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/__init__.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/conftest.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/METHODOLOGY.md +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/README.md +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/__init__.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/ab_probe_cases.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/ab_probe_runner.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/auto_gen_cases.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/binary_round_trip.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/cases.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/complex_cases.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/coverage_report.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/extract.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/extreme_cases.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/fake_session.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/firm_templates/README.md +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/firm_templates/__init__.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/firm_templates/_runner.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/firm_templates/extractor.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/local_runner.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/mega_cases.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshot.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/ours_spec.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/parity_crawl.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/parity_diff.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/real_world_cases.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/round_trip_tests.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/runner.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/stock_spec.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/README.md +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/__init__.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/baseline_gaps.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/compare.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/intentional_deviations.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/introspect.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/reports/gap_report.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/run_parity.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/snapshots/athena_latest.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/test_parity_gap.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_add_section_extract_items.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_athena_extensions_contract.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_block_not_found_error.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_cell_add_table_not_supported.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_cell_inner_format_stash.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_cell_inner_run_format_stash.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_cell_inner_run_guard.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_cell_text_plain_fastpath.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_cell_text_replace_semantics.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_collapsed_range_format.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_command_dataclasses.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_commands.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_comments.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_document_asset_id_property.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_document_clear.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_document_create.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_document_create_from_template.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_document_factory_validation.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_docx_exec_lab.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_execution_scope.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_find_replace_session_open.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_hyperlink_coalescing.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_image_url_data_uri.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_insert_deferred.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_iter_inner_content.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_list_styles.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_merged_cell_secondary_slot.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_merged_cells.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_oxml_shim.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_paragraph_text_len_cache.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_parity_misc.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_parity_round2.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_phase_a_behavior.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_phase_b_headers_footers.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_phase_c_tables.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_positional_cell_id.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_postproc_ref_restore.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_pr19766_review_fixes.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_python_docx_api_parity.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_add_picture.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_add_run.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_comments_get.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_document_audit.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_document_element.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_enum_section.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_font_audit.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_header_footer.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_hyperlink.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_inline_shape.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_misc.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_paragraph_style.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_parfmt.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_row_col_cell.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_run_add_break.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_run_style.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_run_style_strict.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_run_text.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_run_underline.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_section_audit.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_section_dimensions.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_section_onoff.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_settings.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_shared_audit.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_style.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_styles.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_table_audit.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_table_cell.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_table_dimensions.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_table_layout.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_smoke_integration.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_style_acceptance.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_style_font.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_style_setters_contract.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_table_set_cell_perf.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_table_style_id_resolution.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_temporarily_unavailable.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_validate_find_replace_asset_script.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_wire_contract.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_xml_attr_guard.py +0 -0
- {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_zod_wire_contract.py +0 -0
|
@@ -222,6 +222,22 @@ Issue numbers reference `python-openxml/python-docx`.
|
|
|
222
222
|
the same `Hyperlink` class the read path already exposes. Also
|
|
223
223
|
available as `Run.add_hyperlink` for convenience.
|
|
224
224
|
|
|
225
|
+
- **`Paragraph.add_citation(*, source, text=None, anchor=None,
|
|
226
|
+
display_value=None, label=None) -> str`** + **`Document.remove_citation
|
|
227
|
+
(*, citation_id, soft=True)`** — source-citation markers (Athena
|
|
228
|
+
studio-linking; no python-docx analog, `issue=None`). Attaches a
|
|
229
|
+
Word-style superscript citation marker pointing at an Athena asset
|
|
230
|
+
(`source`, an `AssetReference`), optionally narrowed by `anchor` (a
|
|
231
|
+
sheet range, PDF page, …). Renders in Olympus as a clickable superscript
|
|
232
|
+
with a hover preview + Citations-panel entry — the same unified
|
|
233
|
+
citation system as sheets / PPTX / native docs; clicking opens the
|
|
234
|
+
source in a new tab. Routes through `CreateCitation` / `RemoveCitation`;
|
|
235
|
+
the studio server derives the openable `citation_string` from
|
|
236
|
+
`source_ref` + `source_anchor` via `@athenaintel/references` (single
|
|
237
|
+
canonical serializer). `docx/_references.py` mirrors the pptx
|
|
238
|
+
`AssetReference` + anchor re-exports off the shared `athena-references`
|
|
239
|
+
package.
|
|
240
|
+
|
|
225
241
|
- **`Run.add_field(kind, *, args=None, cached_result=None) -> Field`**
|
|
226
242
|
+ **`Document.fields: Fields`** — generic Word field insertion.
|
|
227
243
|
Supports `"PAGE"`, `"NUMPAGES"`, `"DATE"`, `"TIME"`, `"AUTHOR"`,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: athena-python-docx
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.19.0
|
|
4
4
|
Summary: Drop-in replacement for python-docx that connects to Athena's Superdoc/Keryx collaborative document stack
|
|
5
5
|
Project-URL: Homepage, https://athenaintelligence.ai
|
|
6
6
|
Author-email: Athena Intelligence <engineering@athenaintelligence.ai>
|
|
@@ -11,6 +11,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
13
|
Requires-Python: >=3.11
|
|
14
|
+
Requires-Dist: athena-references>=0.1.0
|
|
14
15
|
Requires-Dist: requests>=2.28
|
|
15
16
|
Requires-Dist: urllib3>=1.26.6
|
|
16
17
|
Provides-Extra: dev
|
|
@@ -41,7 +41,7 @@ from docx._execution import (
|
|
|
41
41
|
)
|
|
42
42
|
from docx._timeouts import timeout_for_commands
|
|
43
43
|
from docx.commands import Command, is_response_bearing, must_flush_immediately
|
|
44
|
-
from docx.errors import DocxError, FlushAllError
|
|
44
|
+
from docx.errors import DocxError, DocxUnsupportedCommandError, FlushAllError
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
def _apply_proxy_id_rewrites(
|
|
@@ -467,8 +467,17 @@ class CommandBuffer:
|
|
|
467
467
|
def flush(self) -> list[Any]:
|
|
468
468
|
"""Flush pending commands as one HTTP batch.
|
|
469
469
|
|
|
470
|
-
Returns the
|
|
471
|
-
|
|
470
|
+
Returns the per-command result list, positionally aligned with the
|
|
471
|
+
flushed commands (skipped commands occupy their slot as ``None``).
|
|
472
|
+
Empty list if nothing was pending. Cancels any active idle timer.
|
|
473
|
+
|
|
474
|
+
This is the FIRE-AND-FORGET path: callers here don't await an
|
|
475
|
+
individual command's result, so an unsupported command that the
|
|
476
|
+
server skipped is simply warned (inside ``execute_batch``) and its
|
|
477
|
+
slot left ``None`` — the supported commands still applied. (A caller
|
|
478
|
+
that DOES need a single command's result goes through
|
|
479
|
+
:meth:`_eager_flush_with`, which raises on a skipped trailing command
|
|
480
|
+
rather than returning the wrong slot.)
|
|
472
481
|
|
|
473
482
|
After the batch returns, walks per-cmd results for
|
|
474
483
|
``real_node_id`` / ``real_entity_id`` echoes and updates any
|
|
@@ -490,7 +499,7 @@ class CommandBuffer:
|
|
|
490
499
|
if not pending:
|
|
491
500
|
return []
|
|
492
501
|
try:
|
|
493
|
-
results = self._client.execute_batch(
|
|
502
|
+
results, _skipped_by_index = self._client.execute_batch(
|
|
494
503
|
self._asset_id,
|
|
495
504
|
pending,
|
|
496
505
|
change_mode=change_mode,
|
|
@@ -554,8 +563,11 @@ class CommandBuffer:
|
|
|
554
563
|
proxy_id_refs = self._proxy_id_refs
|
|
555
564
|
self._proxy_id_refs = {}
|
|
556
565
|
all_cmds: list[Command] = [*pending, cmd]
|
|
566
|
+
trailing_index = len(all_cmds) - 1
|
|
557
567
|
try:
|
|
558
|
-
results: list[Any]
|
|
568
|
+
results: list[Any]
|
|
569
|
+
skipped_by_index: dict[int, dict[str, Any]]
|
|
570
|
+
results, skipped_by_index = self._client.execute_batch(
|
|
559
571
|
self._asset_id,
|
|
560
572
|
all_cmds,
|
|
561
573
|
change_mode=change_mode,
|
|
@@ -576,10 +588,32 @@ class CommandBuffer:
|
|
|
576
588
|
_ptc_emit_end_batch(all_cmds, is_error=True)
|
|
577
589
|
raise
|
|
578
590
|
_ptc_emit_end_batch(all_cmds, is_error=False)
|
|
591
|
+
# Proxy-id rewrites still run for the buffered prefix that DID apply —
|
|
592
|
+
# the trailing skip (if any) doesn't strand the supported Creates that
|
|
593
|
+
# flushed alongside it.
|
|
579
594
|
_apply_proxy_id_rewrites(results, proxy_id_refs)
|
|
595
|
+
# This is the AWAITED-result path: the caller is about to read the
|
|
596
|
+
# trailing command's result. If the server skipped that command
|
|
597
|
+
# (capability gap), there is no result to return — returning some
|
|
598
|
+
# earlier applied mutation's slot would be silent WRONG DATA. Fail
|
|
599
|
+
# loud instead. (Fire-and-forget mutations never reach here; they go
|
|
600
|
+
# through :meth:`flush`, which keeps the skip+warn behavior.)
|
|
601
|
+
if trailing_index in skipped_by_index:
|
|
602
|
+
skip = skipped_by_index[trailing_index]
|
|
603
|
+
reason = skip.get("reason") if isinstance(skip, dict) else None
|
|
604
|
+
raise DocxUnsupportedCommandError(
|
|
605
|
+
f"docx-studio skipped command {type(cmd).__name__!r} but the "
|
|
606
|
+
f"SDK was awaiting its result" + (f": {reason}" if reason else ".")
|
|
607
|
+
)
|
|
580
608
|
if not results:
|
|
581
609
|
return {}
|
|
582
|
-
|
|
610
|
+
# Trailing command applied: its slot holds the result dict (or ``{}``
|
|
611
|
+
# for a non-dict result). A ``None`` here would only arise if the
|
|
612
|
+
# server omitted the trailing command's index from ``applied[]``
|
|
613
|
+
# without listing it in ``skipped[]`` — fall back to ``{}`` to match
|
|
614
|
+
# the legacy "no result" behavior rather than handing back ``None``.
|
|
615
|
+
trailing = results[-1]
|
|
616
|
+
return trailing if trailing is not None else {}
|
|
583
617
|
|
|
584
618
|
def _reset_timer_locked(self) -> None:
|
|
585
619
|
# Caller must hold self._lock.
|
|
@@ -31,6 +31,7 @@ from __future__ import annotations
|
|
|
31
31
|
|
|
32
32
|
import json
|
|
33
33
|
import os
|
|
34
|
+
import warnings
|
|
34
35
|
from typing import Any
|
|
35
36
|
|
|
36
37
|
import requests
|
|
@@ -172,6 +173,7 @@ from docx.errors import (
|
|
|
172
173
|
BlockNotFoundError,
|
|
173
174
|
DocxError,
|
|
174
175
|
DocxStudioTemporarilyUnavailable,
|
|
176
|
+
DocxUnsupportedCommandWarning,
|
|
175
177
|
NotFoundError,
|
|
176
178
|
SessionError,
|
|
177
179
|
)
|
|
@@ -662,6 +664,29 @@ def _http_post_json(
|
|
|
662
664
|
)
|
|
663
665
|
|
|
664
666
|
|
|
667
|
+
def _warn_skipped_commands(skipped: list[Any]) -> None:
|
|
668
|
+
"""Emit one :class:`DocxUnsupportedCommandWarning` per server-reported
|
|
669
|
+
skipped command.
|
|
670
|
+
|
|
671
|
+
Each entry is the wire-shape ``{index, type, reason}`` dict the applier
|
|
672
|
+
records when a command raises ``UnsupportedDocxEditorCommandError``. The
|
|
673
|
+
batch still succeeded for every supported command; warning (rather than
|
|
674
|
+
raising) is the graceful-degradation contract so a script that ends with
|
|
675
|
+
an unsupported op doesn't abort into an empty document.
|
|
676
|
+
|
|
677
|
+
Tolerant of malformed entries: anything that isn't a dict is summarized
|
|
678
|
+
generically rather than crashing the (otherwise successful) batch.
|
|
679
|
+
"""
|
|
680
|
+
for entry in skipped:
|
|
681
|
+
if isinstance(entry, dict):
|
|
682
|
+
cmd_type = entry.get("type", "<unknown>")
|
|
683
|
+
reason = entry.get("reason") or "not supported by the docx-editor engine yet"
|
|
684
|
+
message = f"Skipped unsupported command {cmd_type!r}: {reason}"
|
|
685
|
+
else:
|
|
686
|
+
message = f"Skipped unsupported command: {entry!r}"
|
|
687
|
+
warnings.warn(message, DocxUnsupportedCommandWarning, stacklevel=2)
|
|
688
|
+
|
|
689
|
+
|
|
665
690
|
# ---------------------------------------------------------------------------
|
|
666
691
|
# HttpClient — the bare HTTP transport. Stateless wrt batching.
|
|
667
692
|
# ---------------------------------------------------------------------------
|
|
@@ -706,8 +731,22 @@ class HttpClient:
|
|
|
706
731
|
*,
|
|
707
732
|
change_mode: str | None = None,
|
|
708
733
|
user: dict[str, str] | None = None,
|
|
709
|
-
) -> list[Any]:
|
|
710
|
-
"""POST a batch of typed commands
|
|
734
|
+
) -> tuple[list[Any], dict[int, dict[str, Any]]]:
|
|
735
|
+
"""POST a batch of typed commands.
|
|
736
|
+
|
|
737
|
+
Returns ``(results, skipped_by_index)``:
|
|
738
|
+
|
|
739
|
+
- ``results`` is **positionally aligned** with ``commands``: one slot
|
|
740
|
+
per input command, in order. An applied command's slot holds its
|
|
741
|
+
``result`` dict (or ``{}`` for a non-dict result); a SKIPPED
|
|
742
|
+
command's slot holds ``None``. Positional alignment is what lets a
|
|
743
|
+
caller awaiting a single command's result (``_eager_flush_with``)
|
|
744
|
+
map ``results[-1]`` back to *its* command and detect that the
|
|
745
|
+
command was skipped (``None``) rather than silently returning some
|
|
746
|
+
earlier applied mutation's result.
|
|
747
|
+
- ``skipped_by_index`` maps the input index of each skipped command to
|
|
748
|
+
its ``{index, type, reason}`` entry, so callers awaiting a result a
|
|
749
|
+
skipped command can't provide can raise a precise error.
|
|
711
750
|
|
|
712
751
|
Envelope fields:
|
|
713
752
|
change_mode: ``"direct"`` | ``"tracked"`` | ``None``. When
|
|
@@ -720,14 +759,22 @@ class HttpClient:
|
|
|
720
759
|
opens the per-asset session for the first time; ignored
|
|
721
760
|
on subsequent batches against the same pooled session.
|
|
722
761
|
|
|
723
|
-
On success,
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
762
|
+
On success, ``len(applied) + len(skipped) == len(commands)``. The
|
|
763
|
+
server's ``skipped[]`` array carries commands the docx-editor engine
|
|
764
|
+
doesn't implement yet; each is surfaced as a
|
|
765
|
+
:class:`DocxUnsupportedCommandWarning` (graceful degradation — one
|
|
766
|
+
unsupported FIRE-AND-FORGET mutation no longer aborts the whole batch
|
|
767
|
+
into an empty document). Fire-and-forget mutations don't read their
|
|
768
|
+
result, so a warning + skip is correct for them. A skipped command
|
|
769
|
+
that a caller IS awaiting (a query / response-bearing op routed
|
|
770
|
+
through ``_eager_flush_with``) is detected positionally and raised by
|
|
771
|
+
that caller. On 207 partial-failure, the server returns 207 with
|
|
772
|
+
`applied` (the successful prefix) plus an `error` for the failing
|
|
773
|
+
command — :func:`_http_post_json` translates that to a
|
|
774
|
+
:class:`DocxError`.
|
|
728
775
|
"""
|
|
729
776
|
if not commands:
|
|
730
|
-
return []
|
|
777
|
+
return [], {}
|
|
731
778
|
|
|
732
779
|
url: str = f"{self._base_url}/docs/{asset_id}/commands"
|
|
733
780
|
body: dict[str, Any] = {
|
|
@@ -747,6 +794,12 @@ class HttpClient:
|
|
|
747
794
|
timeout=timeout_for_commands(commands=commands),
|
|
748
795
|
)
|
|
749
796
|
applied = resp.get("applied")
|
|
797
|
+
# Commands the engine doesn't implement yet are reported in
|
|
798
|
+
# ``skipped[]`` (non-fatal). Warn per skip and count them toward the
|
|
799
|
+
# batch total — they're a success, not a failure.
|
|
800
|
+
skipped_raw = resp.get("skipped")
|
|
801
|
+
skipped = skipped_raw if isinstance(skipped_raw, list) else []
|
|
802
|
+
_warn_skipped_commands(skipped)
|
|
750
803
|
# An empty applied list paired with an `error` field is the
|
|
751
804
|
# 'all-commands-failed' shape — surface it rather than returning
|
|
752
805
|
# an empty result list to a caller that's about to index [-1].
|
|
@@ -758,19 +811,37 @@ class HttpClient:
|
|
|
758
811
|
f"docx-studio response missing applied[]: {resp!r}",
|
|
759
812
|
)
|
|
760
813
|
|
|
761
|
-
|
|
814
|
+
# Applied + skipped together must account for every command. A short
|
|
815
|
+
# ``applied`` is only legitimate when the remainder were skipped.
|
|
816
|
+
if len(applied) + len(skipped) != len(commands):
|
|
762
817
|
raise DocxError(
|
|
763
|
-
f"docx-studio applied {len(applied)}
|
|
818
|
+
f"docx-studio applied {len(applied)} (+{len(skipped)} skipped) "
|
|
819
|
+
f"of {len(commands)} commands",
|
|
764
820
|
)
|
|
765
821
|
|
|
766
|
-
|
|
822
|
+
# Build a positionally-aligned result list (one slot per input
|
|
823
|
+
# command). Applied slots carry the result dict; skipped slots stay
|
|
824
|
+
# ``None``. Each entry's ``index`` field places it at the right slot —
|
|
825
|
+
# we don't assume ``applied[]`` is a contiguous prefix once skips can
|
|
826
|
+
# appear anywhere in the batch.
|
|
827
|
+
results: list[Any] = [None] * len(commands)
|
|
767
828
|
for entry in applied:
|
|
768
|
-
if isinstance(entry, dict):
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
829
|
+
if not isinstance(entry, dict):
|
|
830
|
+
continue
|
|
831
|
+
idx = entry.get("index")
|
|
832
|
+
if not isinstance(idx, int) or not (0 <= idx < len(results)):
|
|
833
|
+
continue
|
|
834
|
+
result = entry.get("result")
|
|
835
|
+
results[idx] = result if isinstance(result, dict) else {}
|
|
836
|
+
|
|
837
|
+
skipped_by_index: dict[int, dict[str, Any]] = {}
|
|
838
|
+
for entry in skipped:
|
|
839
|
+
if not isinstance(entry, dict):
|
|
840
|
+
continue
|
|
841
|
+
idx = entry.get("index")
|
|
842
|
+
if isinstance(idx, int) and 0 <= idx < len(commands):
|
|
843
|
+
skipped_by_index[idx] = entry
|
|
844
|
+
return results, skipped_by_index
|
|
774
845
|
|
|
775
846
|
|
|
776
847
|
# ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Thin Python mirror of the @athenaintel/references TypeScript package.
|
|
2
|
+
|
|
3
|
+
Used by the citation SDK method (``Paragraph.add_citation`` — an Athena
|
|
4
|
+
extension beyond the python-docx surface). Mirrors only what the SDK needs to
|
|
5
|
+
construct ``AssetReference`` + ``Anchor`` payloads — full reference
|
|
6
|
+
serialization (URI / URL / Spaces format) is server-side.
|
|
7
|
+
|
|
8
|
+
The anchor + version-policy dataclasses are re-exported from the shared
|
|
9
|
+
``athena-references`` package (the single Python source of truth, kept in
|
|
10
|
+
lockstep with ``packages/references`` and ``agora/agora/utils/asset_references``).
|
|
11
|
+
``AssetReference`` is a thin back-compat wrapper that additionally accepts a
|
|
12
|
+
plain ``dict`` ``meta`` and serializes it verbatim.
|
|
13
|
+
|
|
14
|
+
Note (Athena extension):
|
|
15
|
+
This module is NOT part of python-docx — it exists to construct
|
|
16
|
+
Athena-asset references for the SDK's citation method.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
__athena_extension_module__: bool = True
|
|
22
|
+
__athena_extension_description__: str = (
|
|
23
|
+
"Cross-studio asset reference types (Athena studio-linking, no upstream)."
|
|
24
|
+
)
|
|
25
|
+
__athena_extension_since__: str = "0.11.0"
|
|
26
|
+
|
|
27
|
+
from dataclasses import dataclass
|
|
28
|
+
from typing import Any, Optional
|
|
29
|
+
|
|
30
|
+
from athena_references import (
|
|
31
|
+
Anchor,
|
|
32
|
+
PageAnchor,
|
|
33
|
+
PageRangeAnchor,
|
|
34
|
+
PmNodeAnchor,
|
|
35
|
+
PmNodeRangeAnchor,
|
|
36
|
+
ShapeAnchor,
|
|
37
|
+
SheetCellAnchor,
|
|
38
|
+
SheetRangeAnchor,
|
|
39
|
+
SheetTableAnchor,
|
|
40
|
+
SlideAnchor,
|
|
41
|
+
VersionPolicy,
|
|
42
|
+
VersionPolicyLatest,
|
|
43
|
+
VersionPolicyPinned,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class AssetReference:
|
|
49
|
+
"""Athena asset reference. ``id`` is the asset UUID.
|
|
50
|
+
|
|
51
|
+
Re-homes the canonical anchor types onto the shared ``athena-references``
|
|
52
|
+
package while preserving the historical SDK contract that ``meta`` may be a
|
|
53
|
+
plain ``dict`` (serialized verbatim) in addition to ``None``.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
id: str
|
|
57
|
+
meta: Optional[dict[str, Any]] = None
|
|
58
|
+
|
|
59
|
+
def to_dict(self) -> dict[str, Any]:
|
|
60
|
+
"""Serialize to the canonical wire shape."""
|
|
61
|
+
out: dict[str, Any] = {"referenced": "asset", "id": self.id}
|
|
62
|
+
if self.meta is not None:
|
|
63
|
+
out["meta"] = dict(self.meta)
|
|
64
|
+
return out
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
__all__ = [
|
|
68
|
+
"AssetReference",
|
|
69
|
+
"Anchor",
|
|
70
|
+
"PageAnchor",
|
|
71
|
+
"PageRangeAnchor",
|
|
72
|
+
"PmNodeAnchor",
|
|
73
|
+
"PmNodeRangeAnchor",
|
|
74
|
+
"ShapeAnchor",
|
|
75
|
+
"SheetCellAnchor",
|
|
76
|
+
"SheetRangeAnchor",
|
|
77
|
+
"SheetTableAnchor",
|
|
78
|
+
"SlideAnchor",
|
|
79
|
+
"VersionPolicyLatest",
|
|
80
|
+
"VersionPolicyPinned",
|
|
81
|
+
"VersionPolicy",
|
|
82
|
+
]
|
|
@@ -926,6 +926,47 @@ class RemoveHyperlink(Command):
|
|
|
926
926
|
target: dict[str, Any] = dataclasses.field(default_factory=dict)
|
|
927
927
|
|
|
928
928
|
|
|
929
|
+
# ---------------------------------------------------------------------------
|
|
930
|
+
# Citations (Athena extension — source-citation markers. No python-docx
|
|
931
|
+
# analog; mirrors the PPTX studio's AddSlideCitation / RemoveCitation. The
|
|
932
|
+
# studio server derives the openable ``citation_string`` (Spaces URL) from
|
|
933
|
+
# ``source_ref`` + ``source_anchor`` via @athenaintel/references.)
|
|
934
|
+
# ---------------------------------------------------------------------------
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
@dataclass
|
|
938
|
+
class CreateCitation(Command):
|
|
939
|
+
"""Insert a source-citation marker.
|
|
940
|
+
|
|
941
|
+
Either ``text`` inserts a new cited span at ``at`` (a Word-style
|
|
942
|
+
superscript marker is appended after it), or ``target`` cites an existing
|
|
943
|
+
range. ``source_ref`` / ``source_anchor`` describe the cited source
|
|
944
|
+
(AssetReference / Anchor JSON, transited as dicts like ``SetImageAnchor``).
|
|
945
|
+
``label`` is the marker glyph (defaults to a running index server-side).
|
|
946
|
+
``client_entity_id`` is the client-minted citation id the applier honors
|
|
947
|
+
as the citation's stable id (the ``docx-editor-citations`` Y.Map key).
|
|
948
|
+
"""
|
|
949
|
+
|
|
950
|
+
source_ref: dict[str, Any] = dataclasses.field(default_factory=dict)
|
|
951
|
+
source_anchor: dict[str, Any] | None = None
|
|
952
|
+
display_value: str | None = None
|
|
953
|
+
label: str | None = None
|
|
954
|
+
text: str | None = None
|
|
955
|
+
target: dict[str, Any] | None = None
|
|
956
|
+
at: dict[str, Any] | None = None
|
|
957
|
+
client_entity_id: str | None = None
|
|
958
|
+
|
|
959
|
+
|
|
960
|
+
@dataclass
|
|
961
|
+
class RemoveCitation(Command):
|
|
962
|
+
"""Remove a citation by id — strips the marker mark and, by default,
|
|
963
|
+
deactivates the metadata record (``active: false``) so the removal is
|
|
964
|
+
undoable. ``soft=False`` splices the record out entirely."""
|
|
965
|
+
|
|
966
|
+
citation_id: str = ""
|
|
967
|
+
soft: bool = True
|
|
968
|
+
|
|
969
|
+
|
|
929
970
|
# ---------------------------------------------------------------------------
|
|
930
971
|
# Fields (Athena extension — python-docx has no field-add API. PAGE,
|
|
931
972
|
# NUMPAGES, DATE, TIME, FILENAME, AUTHOR, NUMWORDS, REF, SEQ, TOC. The
|
|
@@ -1714,6 +1755,9 @@ _RESPONSE_BEARING_TYPES: frozenset[str] = frozenset(
|
|
|
1714
1755
|
# Athena-extension response-bearing creates — each returns an
|
|
1715
1756
|
# entity id the caller wraps in a proxy.
|
|
1716
1757
|
"CreateHyperlink",
|
|
1758
|
+
# CreateCitation eager-flushes so the marker's ``at`` offset stays
|
|
1759
|
+
# correct relative to surrounding text inserts (mirrors CreateHyperlink).
|
|
1760
|
+
"CreateCitation",
|
|
1717
1761
|
"CreateField",
|
|
1718
1762
|
"CreateBookmark",
|
|
1719
1763
|
"CreateFootnote",
|
|
@@ -1783,6 +1827,8 @@ def _mark_athena_extension_command(cls: type, issue: "str | None", description:
|
|
|
1783
1827
|
for _cls, _issue, _desc in [
|
|
1784
1828
|
(CreateHyperlink, "python-docx#74", "Hyperlink add"),
|
|
1785
1829
|
(RemoveHyperlink, "python-docx#74", "Hyperlink remove"),
|
|
1830
|
+
(CreateCitation, None, "Source citation marker create"),
|
|
1831
|
+
(RemoveCitation, None, "Source citation remove"),
|
|
1786
1832
|
(CreateField, "python-docx#498", "Field insert"),
|
|
1787
1833
|
(FieldsRefresh, "python-docx#498", "Field refresh"),
|
|
1788
1834
|
(FieldsList, "python-docx#498", "Field enumeration"),
|
|
@@ -1914,6 +1960,8 @@ __all__ = [
|
|
|
1914
1960
|
# semantics))
|
|
1915
1961
|
"CreateHyperlink",
|
|
1916
1962
|
"RemoveHyperlink",
|
|
1963
|
+
"CreateCitation",
|
|
1964
|
+
"RemoveCitation",
|
|
1917
1965
|
"CreateField",
|
|
1918
1966
|
"FieldsRefresh",
|
|
1919
1967
|
"FieldsList",
|
|
@@ -936,6 +936,23 @@ class Document:
|
|
|
936
936
|
include_hyperlinks=include_hyperlinks,
|
|
937
937
|
)
|
|
938
938
|
|
|
939
|
+
@athena_extension(
|
|
940
|
+
description="Document.remove_citation — remove a source citation by id.",
|
|
941
|
+
)
|
|
942
|
+
def remove_citation(self, *, citation_id: str, soft: bool = True) -> None:
|
|
943
|
+
"""Remove a source citation by id (Athena extension).
|
|
944
|
+
|
|
945
|
+
Strips the citation marker and, by default (``soft=True``),
|
|
946
|
+
deactivates the metadata record so the removal is undoable;
|
|
947
|
+
``soft=False`` splices the record out entirely. No-op if the id is
|
|
948
|
+
unknown. No upstream python-docx analog.
|
|
949
|
+
"""
|
|
950
|
+
from docx.commands import RemoveCitation
|
|
951
|
+
|
|
952
|
+
run_sync(
|
|
953
|
+
self._session.send_command(RemoveCitation(citation_id=citation_id, soft=soft))
|
|
954
|
+
)
|
|
955
|
+
|
|
939
956
|
@athena_extension(issue=179, description="Document.add_chart.")
|
|
940
957
|
def add_chart(
|
|
941
958
|
self,
|
|
@@ -68,6 +68,40 @@ class DocxError(Exception):
|
|
|
68
68
|
return self
|
|
69
69
|
|
|
70
70
|
|
|
71
|
+
class DocxUnsupportedCommandWarning(UserWarning):
|
|
72
|
+
"""Emitted (not raised) when docx-studio reports that one or more commands
|
|
73
|
+
in a batch were *skipped* because the docx-editor engine doesn't implement
|
|
74
|
+
them yet (the server's ``skipped[]`` response array).
|
|
75
|
+
|
|
76
|
+
The batch still succeeds: every supported command applied and persisted.
|
|
77
|
+
Surfacing each skip as a warning — instead of letting one unsupported op
|
|
78
|
+
abort the whole batch into an empty document — is the graceful-degradation
|
|
79
|
+
contract. ``warnings.warn`` lets agent code observe the skip (a
|
|
80
|
+
``warnings.catch_warnings(record=True)`` block, or the executor's warning
|
|
81
|
+
capture) without the script blowing up. Subclasses :class:`UserWarning` so
|
|
82
|
+
it surfaces by default and matches the SDK's other pending/unsupported
|
|
83
|
+
warning classes.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class DocxUnsupportedCommandError(DocxError):
|
|
88
|
+
"""Raised when a caller AWAITS the result of a command the docx-editor
|
|
89
|
+
engine doesn't implement (the server skipped it, reporting it in
|
|
90
|
+
``skipped[]`` rather than ``applied[]``).
|
|
91
|
+
|
|
92
|
+
The graceful-degradation contract skips unsupported FIRE-AND-FORGET
|
|
93
|
+
mutations and merely warns (:class:`DocxUnsupportedCommandWarning`) — a
|
|
94
|
+
fire-and-forget mutation never reads its result, so dropping it keeps the
|
|
95
|
+
rest of the batch alive instead of nuking the document. But a query or
|
|
96
|
+
response-bearing command (``ExportPDF``, ``SectionsGet``, ``Find``,
|
|
97
|
+
``CommentsGet``, …) is flushed eagerly precisely because the caller is
|
|
98
|
+
about to read its result. Returning some earlier applied mutation's result
|
|
99
|
+
in its place would be silent WRONG DATA, so the awaited-result path raises
|
|
100
|
+
this instead. Subclasses :class:`DocxError` so existing ``except
|
|
101
|
+
DocxError`` handlers still catch it.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
|
|
71
105
|
class SessionError(DocxError):
|
|
72
106
|
"""Raised when the Superdoc SDK session cannot be established or used."""
|
|
73
107
|
|
|
@@ -9,6 +9,7 @@ from docx._athena_extension import athena_extension
|
|
|
9
9
|
from docx._batching import run_sync
|
|
10
10
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
12
|
+
from docx._references import Anchor, AssetReference
|
|
12
13
|
from docx.bookmarks import Bookmark
|
|
13
14
|
from docx.client import Session
|
|
14
15
|
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
|
@@ -1448,6 +1449,97 @@ class Paragraph:
|
|
|
1448
1449
|
range_=(start, end_offset),
|
|
1449
1450
|
)
|
|
1450
1451
|
|
|
1452
|
+
@athena_extension(
|
|
1453
|
+
description="Paragraph.add_citation — append a source-citation marker.",
|
|
1454
|
+
)
|
|
1455
|
+
def add_citation(
|
|
1456
|
+
self,
|
|
1457
|
+
*,
|
|
1458
|
+
source: "AssetReference",
|
|
1459
|
+
text: str | None = None,
|
|
1460
|
+
anchor: "Anchor | None" = None,
|
|
1461
|
+
display_value: str | None = None,
|
|
1462
|
+
label: str | None = None,
|
|
1463
|
+
) -> str:
|
|
1464
|
+
"""Attach a source citation to this paragraph (Athena extension).
|
|
1465
|
+
|
|
1466
|
+
Appends a Word-style superscript citation marker. When ``text`` is
|
|
1467
|
+
given it is inserted first (the cited span) and the marker follows;
|
|
1468
|
+
otherwise the marker attaches at the paragraph end, citing the
|
|
1469
|
+
preceding text. ``source`` is the cited Athena asset; ``anchor``
|
|
1470
|
+
optionally narrows it to a sub-location (a sheet range, PDF page, …).
|
|
1471
|
+
``display_value`` is the label shown on the citations-panel card;
|
|
1472
|
+
``label`` overrides the marker glyph (defaults to a bullet).
|
|
1473
|
+
|
|
1474
|
+
Renders in Olympus as a clickable superscript with a hover preview and
|
|
1475
|
+
a Citations-panel entry; clicking opens the source in a new tab. No
|
|
1476
|
+
upstream python-docx analog.
|
|
1477
|
+
|
|
1478
|
+
Returns the citation id.
|
|
1479
|
+
"""
|
|
1480
|
+
import uuid
|
|
1481
|
+
|
|
1482
|
+
from docx._references import AssetReference
|
|
1483
|
+
from docx.commands import CreateCitation
|
|
1484
|
+
|
|
1485
|
+
if not isinstance(source, AssetReference):
|
|
1486
|
+
raise TypeError(
|
|
1487
|
+
f"Paragraph.add_citation source requires an AssetReference; "
|
|
1488
|
+
f"got {type(source).__name__} {source!r}",
|
|
1489
|
+
)
|
|
1490
|
+
if text is not None and not isinstance(text, str):
|
|
1491
|
+
raise TypeError(
|
|
1492
|
+
f"Paragraph.add_citation text requires str or None; got "
|
|
1493
|
+
f"{type(text).__name__} {text!r}",
|
|
1494
|
+
)
|
|
1495
|
+
source_anchor: dict | None = None
|
|
1496
|
+
if anchor is not None:
|
|
1497
|
+
if not hasattr(anchor, "to_dict"):
|
|
1498
|
+
raise TypeError(
|
|
1499
|
+
"Paragraph.add_citation anchor must be an Anchor dataclass "
|
|
1500
|
+
"(SheetRangeAnchor, PageAnchor, …); got "
|
|
1501
|
+
f"{type(anchor).__name__}",
|
|
1502
|
+
)
|
|
1503
|
+
source_anchor = anchor.to_dict()
|
|
1504
|
+
|
|
1505
|
+
# Cell-inner paragraphs hit the same addressing gap as add_hyperlink
|
|
1506
|
+
# (BlockNotFoundError on flush). Citations in table cells are a
|
|
1507
|
+
# follow-up; warn and skip rather than fail the whole batch.
|
|
1508
|
+
if self._in_cell:
|
|
1509
|
+
warnings.warn(
|
|
1510
|
+
"Paragraph.add_citation on a cell-inner paragraph is not yet "
|
|
1511
|
+
"supported (same addressing gap as add_hyperlink); the "
|
|
1512
|
+
"citation was skipped.",
|
|
1513
|
+
CellInnerFormatNotSupportedWarning,
|
|
1514
|
+
stacklevel=2,
|
|
1515
|
+
)
|
|
1516
|
+
return ""
|
|
1517
|
+
|
|
1518
|
+
citation_id = f"citation_{uuid.uuid4().hex[:12]}"
|
|
1519
|
+
start: int = self._resolve_text_len()
|
|
1520
|
+
cursor: dict = {
|
|
1521
|
+
"kind": "text",
|
|
1522
|
+
"blockId": self._node_id,
|
|
1523
|
+
"offset": start,
|
|
1524
|
+
}
|
|
1525
|
+
cmd = CreateCitation(
|
|
1526
|
+
source_ref=source.to_dict(),
|
|
1527
|
+
source_anchor=source_anchor,
|
|
1528
|
+
display_value=display_value,
|
|
1529
|
+
label=label,
|
|
1530
|
+
text=text,
|
|
1531
|
+
at={"kind": "selection", "start": cursor, "end": cursor},
|
|
1532
|
+
client_entity_id=citation_id,
|
|
1533
|
+
)
|
|
1534
|
+
run_sync(self._session.send_command(cmd))
|
|
1535
|
+
# The applier inserts ``text`` (if any) then a marker glyph (``label``
|
|
1536
|
+
# or a 1-char bullet); keep the cached text length in sync so a
|
|
1537
|
+
# follow-up op targets the right offset before the snapshot re-fetch.
|
|
1538
|
+
marker_len = len(label) if label else 1
|
|
1539
|
+
self._text_len = start + (len(text) if text else 0) + marker_len
|
|
1540
|
+
self._invalidate_node_info()
|
|
1541
|
+
return citation_id
|
|
1542
|
+
|
|
1451
1543
|
@athena_extension(
|
|
1452
1544
|
issue=425,
|
|
1453
1545
|
description="Paragraph.add_bookmark — zero-width anchor at paragraph end.",
|
|
@@ -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.19.0"
|
|
8
8
|
description = "Drop-in replacement for python-docx that connects to Athena's Superdoc/Keryx collaborative document stack"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -26,6 +26,12 @@ dependencies = [
|
|
|
26
26
|
# (urllib3 1.26.0); requests 2.28 only pins urllib3>=1.21.1, so we
|
|
27
27
|
# tighten here to avoid a runtime TypeError on import.
|
|
28
28
|
"urllib3>=1.26.6",
|
|
29
|
+
# Canonical Python source of truth for AssetReference + the Anchor union,
|
|
30
|
+
# re-exported by docx/_references.py for Paragraph.add_citation. Deploy
|
|
31
|
+
# follow-up: the document-exec sandbox vendors this wheel via
|
|
32
|
+
# `pip download --no-deps athena-references` (no PyPI access at build time),
|
|
33
|
+
# mirroring presentation-exec.
|
|
34
|
+
"athena-references>=0.1.0",
|
|
29
35
|
]
|
|
30
36
|
|
|
31
37
|
[project.optional-dependencies]
|