athena-python-docx 0.24.0__tar.gz → 0.25.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.24.0 → athena_python_docx-0.25.0}/AGENTS.md +44 -8
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/PKG-INFO +1 -1
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/__init__.py +1 -1
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/_buffer.py +17 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/_http_doc.py +134 -8
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/_postproc.py +1 -1
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/_timeouts.py +2 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/client.py +16 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/commands.py +22 -4
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/document.py +236 -43
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/errors.py +38 -15
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/section.py +172 -122
- athena_python_docx-0.25.0/docx/styles/__init__.py +38 -0
- athena_python_docx-0.25.0/docx/styles/_table.py +303 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/styles/style.py +5 -1
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/styles/styles.py +88 -34
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/table.py +188 -60
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/text/paragraph.py +89 -45
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/text/run.py +145 -6
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/pyproject.toml +1 -1
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/fake_session.py +15 -1
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +1 -0
- athena_python_docx-0.25.0/tests/fidelity/op_snapshots/15_run_add_break_page.json +6 -0
- athena_python_docx-0.25.0/tests/fidelity/op_snapshots/29_section_headers_linked.json +7 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +1 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/35_full_report.json +1 -0
- athena_python_docx-0.25.0/tests/fidelity/op_snapshots/48_font_color_round_trip.json +5 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/49_resume_layout.json +1 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +1 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +3 -1
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/71_academic_paper.json +1 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/72_legal_contract.json +1 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +1 -0
- athena_python_docx-0.25.0/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +5 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +1 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +2 -2
- athena_python_docx-0.25.0/tests/fidelity/op_snapshots/85_header_footer_access.json +7 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +1 -1
- athena_python_docx-0.24.0/tests/fidelity/op_snapshots/47_font_size_round_trip.json → athena_python_docx-0.25.0/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +1 -1
- athena_python_docx-0.25.0/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +7 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +1 -0
- athena_python_docx-0.25.0/tests/fidelity/op_snapshots/98_style_contains_check.json +6 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +1 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +1 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +2 -1
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +13 -6
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +1 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +2 -2
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +1 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +1 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +1 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +1 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +1 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +1 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +2 -1
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +1 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +2 -1
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +1 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/rw11_header_text.json +2 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +4 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +4 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +1 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/parity/intentional_deviations.json +0 -3
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_athena_extensions_registry.py +3 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_block_not_found_error.py +29 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_buffer.py +43 -6
- athena_python_docx-0.25.0/tests/test_cell_borders_spec.py +100 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_cell_inner_format_stash.py +4 -2
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_commands.py +19 -7
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_http_transport.py +10 -10
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_iter_inner_content.py +3 -3
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_list_styles.py +8 -3
- athena_python_docx-0.25.0/tests/test_page_break.py +49 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_parity_misc.py +2 -2
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_parity_round2.py +19 -5
- athena_python_docx-0.25.0/tests/test_pdf_export_and_page_renders.py +321 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_phase_a_behavior.py +2 -1
- athena_python_docx-0.25.0/tests/test_phase_b_headers_footers.py +471 -0
- athena_python_docx-0.25.0/tests/test_run_mark_inheritance.py +414 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_paragraph_style_strict.py +2 -1
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_run_add_break.py +7 -7
- athena_python_docx-0.25.0/tests/test_style_names.py +272 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_wire_contract.py +17 -2
- athena_python_docx-0.24.0/docx/styles/__init__.py +0 -1
- athena_python_docx-0.24.0/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -5
- athena_python_docx-0.24.0/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -6
- athena_python_docx-0.24.0/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -3
- athena_python_docx-0.24.0/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -6
- athena_python_docx-0.24.0/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -5
- athena_python_docx-0.24.0/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -8
- athena_python_docx-0.24.0/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -6
- athena_python_docx-0.24.0/tests/test_phase_b_headers_footers.py +0 -281
- athena_python_docx-0.24.0/tests/test_run_mark_inheritance.py +0 -164
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/.gitignore +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/CLAUDE.md +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/DOCX_EXEC_LAB.md +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/README.md +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/_athena_extension.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/_batching.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/_citations.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/_execution.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/_http.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/_image_utils.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/_ptc.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/_references.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/_run_order.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/_table_styles.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/api.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/bookmarks.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/charts.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/comments.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/enum/__init__.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/enum/section.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/enum/shape.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/enum/style.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/enum/table.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/enum/text.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/exceptions.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/fields.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/footnotes.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/math.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/opc/__init__.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/opc/coreprops.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/oxml/__init__.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/revisions.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/sdt.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/session.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/settings.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/shape.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/shared.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/text/__init__.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/text/font.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/text/hyperlink.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/text/pagebreak.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/text/parfmt.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/text/tabstops.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/toc.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/docx/typing.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/scripts/docx_exec_lab.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/scripts/docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/scripts/dump_wire_fixtures.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/scripts/release.sh +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/scripts/round_trip_smoke.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/scripts/smoke_test_block_not_found.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/scripts/validate_find_replace_asset.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/__init__.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/conftest.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/METHODOLOGY.md +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/README.md +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/__init__.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/ab_probe_cases.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/ab_probe_runner.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/auto_gen_cases.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/binary_round_trip.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/cases.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/complex_cases.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/coverage_report.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/extract.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/extreme_cases.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/firm_templates/README.md +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/firm_templates/__init__.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/firm_templates/_runner.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/firm_templates/extractor.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/local_runner.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/mega_cases.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshot.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
- /athena_python_docx-0.24.0/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json → /athena_python_docx-0.25.0/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
- /athena_python_docx-0.24.0/tests/fidelity/op_snapshots/98_style_contains_check.json → /athena_python_docx-0.25.0/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
- /athena_python_docx-0.24.0/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json → /athena_python_docx-0.25.0/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/ours_spec.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/parity_crawl.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/parity_diff.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/real_world_cases.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/round_trip_tests.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/stock_spec.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/parity/README.md +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/parity/__init__.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/parity/baseline_gaps.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/parity/compare.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/parity/introspect.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/parity/reports/gap_report.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/parity/run_parity.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/parity/snapshots/athena_latest.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/parity/test_parity_gap.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_add_section_extract_items.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_athena_extension_creates_session_open.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_athena_extensions_contract.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_batching_perf.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_cell_add_table_not_supported.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_cell_fastpath_runs.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_cell_inner_run_format_stash.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_cell_inner_run_guard.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_cell_text_plain_fastpath.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_cell_text_replace_semantics.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_citations.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_collapsed_range_format.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_command_dataclasses.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_comments.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_document_asset_id_property.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_document_clear.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_document_create.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_document_create_from_template.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_document_factory_validation.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_docx_exec_lab.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_docx_exec_lab_server.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_execution_scope.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_find_replace_session_open.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_hyperlink_coalescing.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_image_url_data_uri.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_inline_shape_alt_text.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_insert_deferred.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_merged_cell_secondary_slot.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_merged_cells.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_oxml_shim.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_paragraph_text_len_cache.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_phase_c_tables.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_positional_cell_id.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_postproc_ref_restore.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_pr19766_review_fixes.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_ptc.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_python_docx_api_parity.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_revisions.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_run_add_picture_anchor.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_run_identity.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_add_picture.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_add_run.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_comments_get.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_document_audit.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_document_element.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_enum_section.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_font_audit.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_header_footer.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_hyperlink.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_inline_shape.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_misc.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_paragraph_style.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_parfmt.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_row_col_cell.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_run_style.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_run_style_strict.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_run_text.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_run_underline.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_section_audit.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_section_dimensions.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_section_onoff.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_settings.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_shared_audit.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_style.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_styles.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_table_audit.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_table_cell.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_table_dimensions.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_silent_stub_table_layout.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_smoke_integration.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_style_acceptance.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_style_font.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_style_setters_contract.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_table_set_cell_perf.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_table_style_id_resolution.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_temporarily_unavailable.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_validate_find_replace_asset_script.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_xml_attr_guard.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/tests/test_zod_wire_contract.py +0 -0
- {athena_python_docx-0.24.0 → athena_python_docx-0.25.0}/uv.lock +0 -0
|
@@ -268,7 +268,14 @@ Issue numbers reference `python-openxml/python-docx`.
|
|
|
268
268
|
inside_horizontal=, inside_vertical=)`** + **`_Cell.borders`**
|
|
269
269
|
property + **`Table.set_borders(...)`** — per-edge border control.
|
|
270
270
|
python-docx issue #201 (5 👍), #1238 demand a cell-borders API.
|
|
271
|
-
Routes through `SetCellBorders` / `SetTableBorders`.
|
|
271
|
+
Routes through `SetCellBorders` / `SetTableBorders`. Each edge is
|
|
272
|
+
`{"style", "size_pt", "color", "space"}` (points; `sizePt` also
|
|
273
|
+
accepted) or `"single 0.5pt #D9D9D9"`; the SDK validates it and sends
|
|
274
|
+
`{style, sizePt, color: "#RRGGBB", space}`. Any other key (`val`, `sz`,
|
|
275
|
+
`size`, `width`) raises `ValueError` naming the accepted form, and
|
|
276
|
+
docx-studio refuses it too (`InvalidBorderSpec`): such edges were once
|
|
277
|
+
stored without their size and colour, and one with no style broke every
|
|
278
|
+
later export of the document.
|
|
272
279
|
|
|
273
280
|
- **`_Cell.set_shading(*, fill=, pattern=, color=)`** +
|
|
274
281
|
**`_Cell.shading`** property — cell fill / shading. python-docx
|
|
@@ -370,7 +377,29 @@ Issue numbers reference `python-openxml/python-docx`.
|
|
|
370
377
|
include_revisions=False) -> bytes`** — render to PDF. python-docx
|
|
371
378
|
issue #113 was closed as
|
|
372
379
|
wontfix upstream but the request keeps recurring. Routes through
|
|
373
|
-
`ExportPDF` (
|
|
380
|
+
`ExportPDF` (0.25.0+ opens the session first and sends buffered edits in
|
|
381
|
+
the same request). DOCX Studio lays the document out with the engine the
|
|
382
|
+
editor paginates with and paints every page onto Skia's PDF backend: a
|
|
383
|
+
vector PDF with selectable text in embedded fonts that matches
|
|
384
|
+
`render_page` — no LibreOffice anywhere, so never install it in a sandbox.
|
|
385
|
+
`quality` is `"draft"` (pictures as JPEG), `"standard"` (default) or
|
|
386
|
+
`"high"`; `include_revisions=True` shows tracked changes as markup (the
|
|
387
|
+
default renders them accepted); `destination` writes a local file and
|
|
388
|
+
refuses asset ids and URLs with `ValueError`. A deployment that cannot
|
|
389
|
+
produce the PDF raises `DocxUnsupportedCommandError` naming the reason.
|
|
390
|
+
`GET /docs/:id/export.pdf` serves the same PDF read-only (VIEW gate).
|
|
391
|
+
|
|
392
|
+
- **`Document.render_page(page_number=1, scale=2, as_pil=False)`** +
|
|
393
|
+
**`Document.screenshot_to_path(path, page=1, scale=2) -> Path`** +
|
|
394
|
+
**`Document.page_count`** (0.25.0+) — look at the document as the editor
|
|
395
|
+
lays it out: one page (numbered from 1) as PNG bytes or a Pillow image,
|
|
396
|
+
written to disk, and the page count. Names follow
|
|
397
|
+
athena-python-pptx's `slide.render(scale=2, as_pil=True)` and
|
|
398
|
+
athena-openpyxl's `Worksheet.screenshot_to_path`. Buffered edits flush
|
|
399
|
+
first. Backed by docx-studio's read-only `GET /docs/:id/pages/:n.png` and
|
|
400
|
+
`GET /docs/:id/layout.json` (VIEW gate, the SDK's API key); a page past
|
|
401
|
+
the end raises `PageOutOfRangeError` (an `IndexError`) carrying
|
|
402
|
+
`page_count`.
|
|
374
403
|
|
|
375
404
|
- **`Document.clear()`** (0.14.0+) — empty the document body so subsequent
|
|
376
405
|
writes start from a clean slate. python-docx has no equivalent (it
|
|
@@ -754,12 +783,19 @@ architecture or a DOCX Studio constraint:
|
|
|
754
783
|
cheaply enumerate every cell. `(0,0)` and in-range negative indices
|
|
755
784
|
behave identically to upstream.
|
|
756
785
|
|
|
757
|
-
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
786
|
+
- **Unknown paragraph style names raise only when Word has no such
|
|
787
|
+
built-in.** Style names and ids follow python-docx: `.name` is the UI
|
|
788
|
+
name (`"Heading 1"`), `.style_id` the id (`"Heading1"`), read from the
|
|
789
|
+
document's style table (docx-studio `Info` with `includeStyles`, fetched
|
|
790
|
+
once per session in `docx/styles/_table.py`). A style set by name or id
|
|
791
|
+
is sent as the document's id, and an unstyled paragraph reads as the
|
|
792
|
+
default paragraph style. `paragraph.style = …` raises `KeyError` for a
|
|
793
|
+
name the document defines no style for, like python-docx — except Word's
|
|
794
|
+
built-in styles (`"Heading 4"`, `"List Bullet"`, `"Title"`…), which many
|
|
795
|
+
templates never write into `styles.xml` until used. Those are sent by
|
|
796
|
+
their Word id; Word and docx-studio apply the built-in definition. Against
|
|
797
|
+
an older docx-studio (no style table) a built-in name is sent as its id
|
|
798
|
+
and anything else as given.
|
|
763
799
|
|
|
764
800
|
- **`Document.add_table(rows, cols)` requires `rows >= 1` and
|
|
765
801
|
`cols >= 1`.** Upstream allows a 0-row/0-col grid you later populate,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: athena-python-docx
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.25.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>
|
|
@@ -341,6 +341,10 @@ class CommandBuffer:
|
|
|
341
341
|
self._asset_id: str = asset_id
|
|
342
342
|
self._pending: list[Command] = []
|
|
343
343
|
self._lock: threading.RLock = threading.RLock()
|
|
344
|
+
# Held for the whole of a flush (drain + POST), so batches reach the
|
|
345
|
+
# server one at a time and in order, and a flush returns only once
|
|
346
|
+
# every batch taken before it has been delivered.
|
|
347
|
+
self._post_lock: threading.RLock = threading.RLock()
|
|
344
348
|
self._timer: threading.Timer | None = None
|
|
345
349
|
self._auto_flush_seconds: float = auto_flush_seconds
|
|
346
350
|
self._closed: bool = False
|
|
@@ -532,6 +536,14 @@ class CommandBuffer:
|
|
|
532
536
|
proxies pick up real ids without the caller ever seeing the
|
|
533
537
|
UUID swap.
|
|
534
538
|
"""
|
|
539
|
+
# One batch in flight at a time: a flush that finds the queue empty
|
|
540
|
+
# still waits for a batch the idle timer already took and is posting,
|
|
541
|
+
# so a read that follows (a query, a page render) sees those edits and
|
|
542
|
+
# a failure of that batch is raised here.
|
|
543
|
+
with self._post_lock:
|
|
544
|
+
return self._flush_in_order()
|
|
545
|
+
|
|
546
|
+
def _flush_in_order(self) -> list[Any]:
|
|
535
547
|
self._assert_current_execution()
|
|
536
548
|
self._raise_deferred_flush_error()
|
|
537
549
|
with self._lock:
|
|
@@ -599,7 +611,12 @@ class CommandBuffer:
|
|
|
599
611
|
still get their real ids written back via the same flush-time
|
|
600
612
|
rewrite that :meth:`flush` does.
|
|
601
613
|
"""
|
|
614
|
+
with self._post_lock:
|
|
615
|
+
return self._eager_flush_in_order(cmd)
|
|
616
|
+
|
|
617
|
+
def _eager_flush_in_order(self, cmd: Command) -> Any:
|
|
602
618
|
self._assert_current_execution()
|
|
619
|
+
self._raise_deferred_flush_error()
|
|
603
620
|
with self._lock:
|
|
604
621
|
self._cancel_timer_locked()
|
|
605
622
|
pending = self._pending
|
|
@@ -31,15 +31,16 @@ from __future__ import annotations
|
|
|
31
31
|
|
|
32
32
|
import json
|
|
33
33
|
import os
|
|
34
|
+
import urllib.parse
|
|
34
35
|
import warnings
|
|
35
|
-
from typing import Any
|
|
36
|
+
from typing import Any, TypedDict
|
|
36
37
|
|
|
37
38
|
import requests
|
|
38
39
|
from requests.adapters import HTTPAdapter
|
|
39
40
|
from urllib3.util.retry import Retry
|
|
40
41
|
|
|
41
42
|
from docx._buffer import CommandBuffer
|
|
42
|
-
from docx._timeouts import timeout_for_commands
|
|
43
|
+
from docx._timeouts import DEFAULT_OP_TIMEOUT_SECONDS, timeout_for_commands
|
|
43
44
|
from docx.commands import (
|
|
44
45
|
BlocksList,
|
|
45
46
|
BookmarksGet,
|
|
@@ -177,6 +178,7 @@ from docx.errors import (
|
|
|
177
178
|
DocxUnsupportedCommandWarning,
|
|
178
179
|
NotFoundError,
|
|
179
180
|
SessionError,
|
|
181
|
+
_missing_block_id,
|
|
180
182
|
)
|
|
181
183
|
|
|
182
184
|
|
|
@@ -338,6 +340,19 @@ _TABLE_CLIENT_ID_HINT: str = (
|
|
|
338
340
|
)
|
|
339
341
|
|
|
340
342
|
|
|
343
|
+
_HEADER_FOOTER_PARAGRAPH_HINT: str = (
|
|
344
|
+
"\n\nHint: this paragraph is in a header or footer (``section.header`` / "
|
|
345
|
+
"``section.footer``), not in a table cell. docx-studio found no "
|
|
346
|
+
"paragraph with this id: the header/footer part that held it was "
|
|
347
|
+
"deleted, or the docx-studio server predates run and paragraph edits "
|
|
348
|
+
"in headers and footers. Add the paragraph again with "
|
|
349
|
+
"``section.footer.add_paragraph(text)`` (or ``section.header``) and edit "
|
|
350
|
+
"the paragraph it returns; until the server supports those edits, pass "
|
|
351
|
+
"the paragraph's full text to ``add_paragraph`` and set only its style "
|
|
352
|
+
"and alignment."
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
|
|
341
356
|
def _looks_like_not_found(parsed: dict) -> bool:
|
|
342
357
|
"""Inspect a docx-studio partial-failure dict and decide whether it
|
|
343
358
|
describes a "no such entity" miss (vs. a real transport / validation
|
|
@@ -446,7 +461,9 @@ def _build_retry_session() -> requests.Session:
|
|
|
446
461
|
"other": 0,
|
|
447
462
|
"backoff_factor": 2.0,
|
|
448
463
|
"status_forcelist": (429, 502, 503, 504),
|
|
449
|
-
|
|
464
|
+
# GET serves only the read-only render routes (page PNGs, layout),
|
|
465
|
+
# which are always safe to retry.
|
|
466
|
+
"allowed_methods": frozenset(["POST", "GET"]),
|
|
450
467
|
"raise_on_status": False,
|
|
451
468
|
"respect_retry_after_header": True,
|
|
452
469
|
}
|
|
@@ -482,6 +499,16 @@ def _parse_retry_after(value: str | None) -> float | None:
|
|
|
482
499
|
return None
|
|
483
500
|
|
|
484
501
|
|
|
502
|
+
def _delegation_headers() -> dict[str, str]:
|
|
503
|
+
"""The user a service-key caller acts for, as ``X-Custom-Attributions``.
|
|
504
|
+
|
|
505
|
+
docx-studio's ownership gate reads it (``delegatedUserId``) and agora's
|
|
506
|
+
``/access`` refuses a service-key call that delegates no one, so every
|
|
507
|
+
request carries it: commands and the read-only GETs alike."""
|
|
508
|
+
custom_attr = os.environ.get("ATHENA_DOCX_CUSTOM_ATTRIBUTIONS")
|
|
509
|
+
return {"X-Custom-Attributions": custom_attr} if custom_attr else {}
|
|
510
|
+
|
|
511
|
+
|
|
485
512
|
def _http_post_json(
|
|
486
513
|
*,
|
|
487
514
|
session: requests.Session,
|
|
@@ -489,17 +516,21 @@ def _http_post_json(
|
|
|
489
516
|
api_key: str,
|
|
490
517
|
body: dict,
|
|
491
518
|
timeout: float = 60.0,
|
|
519
|
+
header_footer_block_ids: "set[str] | frozenset[str]" = frozenset(),
|
|
492
520
|
) -> dict:
|
|
493
|
-
"""POST JSON, parse JSON, raise typed errors on non-2xx.
|
|
521
|
+
"""POST JSON, parse JSON, raise typed errors on non-2xx.
|
|
522
|
+
|
|
523
|
+
``header_footer_block_ids`` are the header/footer paragraph ids this
|
|
524
|
+
document's ``_HeaderFooter.add_paragraph`` returned: a ``block not
|
|
525
|
+
found`` on one of them gets the header/footer hint.
|
|
526
|
+
"""
|
|
494
527
|
headers = {
|
|
495
528
|
"Content-Type": "application/json",
|
|
496
529
|
"Authorization": f"Bearer {api_key}",
|
|
497
530
|
"Accept": "application/json",
|
|
498
531
|
"User-Agent": _user_agent(),
|
|
532
|
+
**_delegation_headers(),
|
|
499
533
|
}
|
|
500
|
-
custom_attr = os.environ.get("ATHENA_DOCX_CUSTOM_ATTRIBUTIONS")
|
|
501
|
-
if custom_attr:
|
|
502
|
-
headers["X-Custom-Attributions"] = custom_attr
|
|
503
534
|
try:
|
|
504
535
|
resp = session.post(
|
|
505
536
|
url,
|
|
@@ -621,7 +652,9 @@ def _http_post_json(
|
|
|
621
652
|
isinstance(cmd_type, str) and cmd_type in _PARAGRAPH_TARGETING_COMMANDS
|
|
622
653
|
)
|
|
623
654
|
table_query = isinstance(cmd_type, str) and cmd_type in _TABLE_QUERY_COMMANDS
|
|
624
|
-
if
|
|
655
|
+
if _missing_block_id(err_obj.get("message")) in header_footer_block_ids:
|
|
656
|
+
hint = _HEADER_FOOTER_PARAGRAPH_HINT
|
|
657
|
+
elif paragraph_targeting:
|
|
625
658
|
hint = _CELL_PARAGRAPH_HINT
|
|
626
659
|
elif table_query:
|
|
627
660
|
hint = _TABLE_CLIENT_ID_HINT
|
|
@@ -696,6 +729,63 @@ def _warn_skipped_commands(skipped: list[Any]) -> None:
|
|
|
696
729
|
warnings.warn(message, DocxUnsupportedCommandWarning, stacklevel=2)
|
|
697
730
|
|
|
698
731
|
|
|
732
|
+
def _http_get(
|
|
733
|
+
*,
|
|
734
|
+
session: requests.Session,
|
|
735
|
+
url: str,
|
|
736
|
+
api_key: str,
|
|
737
|
+
params: dict[str, str] | None = None,
|
|
738
|
+
timeout: float = DEFAULT_OP_TIMEOUT_SECONDS,
|
|
739
|
+
) -> requests.Response:
|
|
740
|
+
"""GET one of docx-studio's read-only routes; raise typed errors on non-2xx.
|
|
741
|
+
|
|
742
|
+
A 404 keeps the server's JSON body as the error payload, so a caller can
|
|
743
|
+
tell a page past the end (``error: "PageNotFound"``, ``pageCount``) from an
|
|
744
|
+
asset the caller cannot read.
|
|
745
|
+
"""
|
|
746
|
+
headers = {
|
|
747
|
+
"Authorization": f"Bearer {api_key}",
|
|
748
|
+
"User-Agent": _user_agent(),
|
|
749
|
+
**_delegation_headers(),
|
|
750
|
+
}
|
|
751
|
+
try:
|
|
752
|
+
resp = session.get(url, params=params, headers=headers, timeout=timeout)
|
|
753
|
+
except requests.Timeout as e:
|
|
754
|
+
raise SessionError(f"docx-studio timed out at {url}: {e}") from e
|
|
755
|
+
except requests.ConnectionError as e:
|
|
756
|
+
raise SessionError(f"Unable to reach docx-studio at {url}: {e}") from e
|
|
757
|
+
if 200 <= resp.status_code < 300:
|
|
758
|
+
return resp
|
|
759
|
+
err_body: str = resp.text
|
|
760
|
+
if resp.status_code in (401, 403):
|
|
761
|
+
raise AuthenticationError(
|
|
762
|
+
f"docx-studio rejected the request (HTTP {resp.status_code}): {err_body}",
|
|
763
|
+
)
|
|
764
|
+
if resp.status_code == 404:
|
|
765
|
+
try:
|
|
766
|
+
parsed: object = resp.json()
|
|
767
|
+
except ValueError:
|
|
768
|
+
parsed = None
|
|
769
|
+
payload: dict[str, Any] = {"status": 404, "body": err_body}
|
|
770
|
+
if isinstance(parsed, dict):
|
|
771
|
+
payload.update(parsed)
|
|
772
|
+
raise NotFoundError(f"docx-studio returned HTTP 404: {err_body}", payload=payload)
|
|
773
|
+
if resp.status_code in (502, 503, 504):
|
|
774
|
+
raise DocxStudioTemporarilyUnavailable(
|
|
775
|
+
f"docx-studio returned HTTP {resp.status_code}: {err_body}",
|
|
776
|
+
http_status=resp.status_code,
|
|
777
|
+
retry_after_seconds=_parse_retry_after(resp.headers.get("Retry-After")),
|
|
778
|
+
)
|
|
779
|
+
raise DocxError(f"docx-studio returned HTTP {resp.status_code}: {err_body}")
|
|
780
|
+
|
|
781
|
+
|
|
782
|
+
class RenderedPagePng(TypedDict):
|
|
783
|
+
"""One page from ``GET /docs/:id/pages/:n.png``."""
|
|
784
|
+
|
|
785
|
+
png: bytes
|
|
786
|
+
page_count: int
|
|
787
|
+
|
|
788
|
+
|
|
699
789
|
# ---------------------------------------------------------------------------
|
|
700
790
|
# HttpClient — the bare HTTP transport. Stateless wrt batching.
|
|
701
791
|
# ---------------------------------------------------------------------------
|
|
@@ -720,6 +810,8 @@ class HttpClient:
|
|
|
720
810
|
self._base_url: str = base_url.rstrip("/")
|
|
721
811
|
self._api_key: str = api_key
|
|
722
812
|
self._session: requests.Session = session or _build_retry_session()
|
|
813
|
+
# Header/footer paragraph ids, for the ``block not found`` hint.
|
|
814
|
+
self.header_footer_block_ids: set[str] = set()
|
|
723
815
|
|
|
724
816
|
def close(self) -> None:
|
|
725
817
|
"""Release the pooled HTTP connections held by ``requests.Session``.
|
|
@@ -801,6 +893,7 @@ class HttpClient:
|
|
|
801
893
|
api_key=self._api_key,
|
|
802
894
|
body=body,
|
|
803
895
|
timeout=timeout_for_commands(commands=commands),
|
|
896
|
+
header_footer_block_ids=self.header_footer_block_ids,
|
|
804
897
|
)
|
|
805
898
|
applied = resp.get("applied")
|
|
806
899
|
# Commands the engine doesn't implement yet are reported in
|
|
@@ -852,6 +945,38 @@ class HttpClient:
|
|
|
852
945
|
skipped_by_index[idx] = entry
|
|
853
946
|
return results, skipped_by_index
|
|
854
947
|
|
|
948
|
+
def _doc_url(self, asset_id: str, path: str) -> str:
|
|
949
|
+
return f"{self._base_url}/docs/{urllib.parse.quote(asset_id, safe='')}/{path}"
|
|
950
|
+
|
|
951
|
+
def get_layout(self, asset_id: str) -> dict[str, Any]:
|
|
952
|
+
"""``GET /docs/:id/layout.json`` — the page count and page sizes as
|
|
953
|
+
DOCX Studio's layout engine paginates the document (VIEW gate)."""
|
|
954
|
+
resp = _http_get(
|
|
955
|
+
session=self._session,
|
|
956
|
+
url=self._doc_url(asset_id, "layout.json"),
|
|
957
|
+
api_key=self._api_key,
|
|
958
|
+
)
|
|
959
|
+
try:
|
|
960
|
+
data: object = resp.json()
|
|
961
|
+
except ValueError as e:
|
|
962
|
+
raise DocxError(f"docx-studio returned a non-JSON layout: {resp.text[:200]!r}") from e
|
|
963
|
+
if not isinstance(data, dict):
|
|
964
|
+
raise DocxError(f"docx-studio returned an unexpected layout: {data!r}")
|
|
965
|
+
return data
|
|
966
|
+
|
|
967
|
+
def render_page_png(self, asset_id: str, page_number: int, *, scale: float) -> RenderedPagePng:
|
|
968
|
+
"""``GET /docs/:id/pages/:n.png?scale=`` — one page (1-indexed) as PNG,
|
|
969
|
+
from the same layout engine the editor paginates with (VIEW gate)."""
|
|
970
|
+
resp = _http_get(
|
|
971
|
+
session=self._session,
|
|
972
|
+
url=self._doc_url(asset_id, f"pages/{page_number}.png"),
|
|
973
|
+
api_key=self._api_key,
|
|
974
|
+
params={"scale": f"{scale:g}"},
|
|
975
|
+
)
|
|
976
|
+
page_count_header = resp.headers.get("X-Page-Count", "")
|
|
977
|
+
page_count = int(page_count_header) if page_count_header.isdigit() else 0
|
|
978
|
+
return RenderedPagePng(png=resp.content, page_count=page_count)
|
|
979
|
+
|
|
855
980
|
|
|
856
981
|
# ---------------------------------------------------------------------------
|
|
857
982
|
# Path proxy — translates dotted paths + camelCase param dicts into typed
|
|
@@ -1045,6 +1170,7 @@ _FIELD_RENAMES: dict[str, dict[str, str]] = {
|
|
|
1045
1170
|
# path-proxy snake-case transform leaves `in` as a Python keyword
|
|
1046
1171
|
# collision; rename to `in_` here so the dataclass accepts it.
|
|
1047
1172
|
"find_replace": {"in": "in_"},
|
|
1173
|
+
"blocks.list": {"in": "in_"},
|
|
1048
1174
|
"find_text": {"in": "in_"},
|
|
1049
1175
|
"iter_runs": {"in": "in_"},
|
|
1050
1176
|
"numbering.list": {"in": "in_"},
|
|
@@ -2055,7 +2055,7 @@ def _border_spec_to_ooxml_attrs(spec: dict[str, Any]) -> dict[str, str]:
|
|
|
2055
2055
|
out["sz"] = str(int(round(float(size_pt) * 8)))
|
|
2056
2056
|
except (TypeError, ValueError):
|
|
2057
2057
|
pass
|
|
2058
|
-
space = spec.get("space_pt")
|
|
2058
|
+
space = spec.get("space", spec.get("space_pt"))
|
|
2059
2059
|
if space is not None:
|
|
2060
2060
|
try:
|
|
2061
2061
|
out["space"] = str(int(round(float(space))))
|
|
@@ -22,6 +22,8 @@ _LONG_RUNNING_COMMAND_TYPES: frozenset[str] = frozenset(
|
|
|
22
22
|
# ``Document.clear()`` replaces the whole body story in one write;
|
|
23
23
|
# on a large document that single command can outlast the 60s budget.
|
|
24
24
|
"ClearBody",
|
|
25
|
+
# ``Document.to_pdf()`` lays out and paints every page of the document.
|
|
26
|
+
"ExportPDF",
|
|
25
27
|
}
|
|
26
28
|
)
|
|
27
29
|
|
|
@@ -178,6 +178,22 @@ class Session:
|
|
|
178
178
|
)
|
|
179
179
|
return self._doc_handle
|
|
180
180
|
|
|
181
|
+
@property
|
|
182
|
+
def http_client(self) -> Any:
|
|
183
|
+
"""The opened :class:`HttpClient`, for docx-studio's read-only routes
|
|
184
|
+
outside the command bus (page renders, layout)."""
|
|
185
|
+
self._assert_current_execution()
|
|
186
|
+
if not self._opened:
|
|
187
|
+
raise SessionError(
|
|
188
|
+
"Session not yet opened; call await session.open() first "
|
|
189
|
+
"or use the sync facade via Document().",
|
|
190
|
+
)
|
|
191
|
+
if self._closed:
|
|
192
|
+
raise DocumentClosedError(
|
|
193
|
+
f"Session for asset {self._asset_id} is closed.",
|
|
194
|
+
)
|
|
195
|
+
return self._client
|
|
196
|
+
|
|
181
197
|
async def send_command(self, cmd: Any) -> Any:
|
|
182
198
|
"""Route a pre-built :class:`docx.commands.Command` through the
|
|
183
199
|
buffer.
|
|
@@ -88,6 +88,8 @@ class CreateParagraph(Command):
|
|
|
88
88
|
# serialises as ``in`` on the wire (see top-of-module helper).
|
|
89
89
|
in_: dict[str, Any] | None = None
|
|
90
90
|
client_node_id: str | None = None
|
|
91
|
+
# The paragraph opens with a page break (``Document.add_page_break``).
|
|
92
|
+
page_break: bool | None = None
|
|
91
93
|
|
|
92
94
|
|
|
93
95
|
@dataclass
|
|
@@ -543,6 +545,9 @@ class BlocksList(Command):
|
|
|
543
545
|
# ``Paragraph.text``/``.runs``/``.style`` from one list instead of a
|
|
544
546
|
# ``get_node_by_id`` per paragraph. Serialized to ``includeRuns`` on the wire.
|
|
545
547
|
include_runs: bool | None = None
|
|
548
|
+
# A header/footer story (``headerFooterSlot``) for ``header.paragraphs``;
|
|
549
|
+
# ``in`` on the wire. See CreateParagraph.in_.
|
|
550
|
+
in_: dict[str, Any] | None = None
|
|
546
551
|
|
|
547
552
|
|
|
548
553
|
@dataclass
|
|
@@ -573,7 +578,10 @@ class ImagesList(Command):
|
|
|
573
578
|
|
|
574
579
|
@dataclass
|
|
575
580
|
class Info(Command):
|
|
576
|
-
|
|
581
|
+
# ``True`` also returns the document's style table (``styleTable``: the
|
|
582
|
+
# id, styles.xml name, type and default flag of every style); an older
|
|
583
|
+
# docx-studio ignores the field and sends no table.
|
|
584
|
+
include_styles: bool | None = None
|
|
577
585
|
|
|
578
586
|
|
|
579
587
|
@dataclass
|
|
@@ -1004,6 +1012,11 @@ class CreateField(Command):
|
|
|
1004
1012
|
target: dict[str, Any] | None = None
|
|
1005
1013
|
cached_result: str | None = None
|
|
1006
1014
|
client_entity_id: str | None = None
|
|
1015
|
+
# False for an empty run: the field takes the paragraph style's defaults,
|
|
1016
|
+
# not the formatting of the text before it.
|
|
1017
|
+
inherit_marks: bool | None = None
|
|
1018
|
+
# The formatting set on that empty run before it has text (``FormatApply.inline``).
|
|
1019
|
+
inline: dict[str, Any] | None = None
|
|
1007
1020
|
|
|
1008
1021
|
|
|
1009
1022
|
@dataclass
|
|
@@ -1616,10 +1629,12 @@ class IterRuns(Command):
|
|
|
1616
1629
|
|
|
1617
1630
|
@dataclass
|
|
1618
1631
|
class ExportPDF(Command):
|
|
1619
|
-
"""Render the document to a PDF
|
|
1620
|
-
under ``destination`` (an asset id, S3 URL, or path).
|
|
1632
|
+
"""Render the document to a PDF with DOCX Studio's layout engine.
|
|
1621
1633
|
|
|
1622
|
-
Returns ``{
|
|
1634
|
+
Returns ``{bytesBase64, mediaType, pageCount, fidelityNotes}``. The
|
|
1635
|
+
server refuses ``destination`` (``Document.to_pdf`` writes local files
|
|
1636
|
+
itself and never sends it); ``quality`` is ``"draft"`` | ``"standard"`` |
|
|
1637
|
+
``"high"``.
|
|
1623
1638
|
"""
|
|
1624
1639
|
|
|
1625
1640
|
quality: str | None = None # "draft" | "standard" | "high"
|
|
@@ -1772,6 +1787,9 @@ _RESPONSE_BEARING_TYPES: frozenset[str] = frozenset(
|
|
|
1772
1787
|
"HeaderFootersPartsCreate",
|
|
1773
1788
|
# HeaderFootersPartsList is a read-side query.
|
|
1774
1789
|
"HeaderFootersPartsList",
|
|
1790
|
+
# HeaderFootersPartsDelete returns {changed}: whether a part was
|
|
1791
|
+
# deleted. Buffered, the caller only ever saw ``{}``.
|
|
1792
|
+
"HeaderFootersPartsDelete",
|
|
1775
1793
|
# Athena-extension response-bearing creates — each returns an
|
|
1776
1794
|
# entity id the caller wraps in a proxy.
|
|
1777
1795
|
"CreateHyperlink",
|