athena-python-docx 0.11.3__tar.gz → 0.12.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.11.3 → athena_python_docx-0.12.0}/CLAUDE.md +76 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/PKG-INFO +1 -1
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/__init__.py +1 -1
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/_http_doc.py +18 -8
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/commands.py +36 -1
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/document.py +143 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/oxml/__init__.py +16 -4
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/section.py +65 -3
- athena_python_docx-0.12.0/docx/session.py +27 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/table.py +243 -29
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/text/paragraph.py +114 -3
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/text/parfmt.py +60 -35
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/text/run.py +31 -6
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/pyproject.toml +1 -1
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +1 -1
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +1 -11
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_athena_extensions_registry.py +1 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_block_not_found_error.py +18 -13
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_oxml_shim.py +24 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/.gitignore +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/README.md +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/_athena_extension.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/_batching.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/_buffer.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/_http.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/_image_utils.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/_ptc.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/_table_styles.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/api.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/bookmarks.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/charts.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/client.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/comments.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/enum/__init__.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/enum/section.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/enum/style.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/enum/table.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/enum/text.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/errors.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/exceptions.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/fields.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/footnotes.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/math.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/opc/__init__.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/opc/coreprops.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/revisions.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/sdt.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/settings.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/shape.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/shared.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/styles/__init__.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/styles/style.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/styles/styles.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/text/__init__.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/text/font.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/text/hyperlink.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/text/pagebreak.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/text/tabstops.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/toc.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/docx/typing.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/scripts/publish.sh +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/scripts/release.sh +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/scripts/round_trip_smoke.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/scripts/smoke_test_block_not_found.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/__init__.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/conftest.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/METHODOLOGY.md +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/README.md +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/__init__.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/ab_probe_cases.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/ab_probe_runner.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/auto_gen_cases.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/binary_round_trip.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/cases.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/complex_cases.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/coverage_report.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/extract.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/extreme_cases.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/fake_session.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/firm_templates/README.md +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/firm_templates/__init__.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/firm_templates/_runner.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/firm_templates/extractor.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/local_runner.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/mega_cases.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshot.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/ours_spec.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/parity_crawl.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/parity_diff.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/real_world_cases.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/round_trip_tests.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/runner.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/stock_spec.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/parity/README.md +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/parity/__init__.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/parity/baseline_gaps.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/parity/compare.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/parity/intentional_deviations.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/parity/introspect.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/parity/reports/gap_report.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/parity/run_parity.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/parity/snapshots/athena_latest.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/parity/test_parity_gap.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_athena_extensions_contract.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_batching_perf.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_buffer.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_cell_add_table_not_supported.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_cell_text_plain_fastpath.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_collapsed_range_format.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_command_dataclasses.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_commands.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_comments.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_document_asset_id_property.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_document_create.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_document_create_from_template.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_document_factory_validation.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_http_transport.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_hyperlink_coalescing.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_insert_deferred.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_iter_inner_content.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_list_styles.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_merged_cell_secondary_slot.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_merged_cells.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_paragraph_text_len_cache.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_parity_misc.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_parity_round2.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_partial_failure_cascade.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_phase_a_behavior.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_phase_b_headers_footers.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_phase_c_tables.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_pr19766_review_fixes.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_ptc.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_python_docx_api_parity.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_revisions.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_add_picture.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_add_run.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_comments_get.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_document_audit.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_document_element.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_enum_section.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_font_audit.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_header_footer.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_hyperlink.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_inline_shape.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_misc.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_paragraph_style.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_parfmt.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_row_col_cell.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_run_add_break.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_run_style.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_run_style_strict.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_run_text.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_run_underline.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_section_audit.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_section_dimensions.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_section_onoff.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_settings.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_shared_audit.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_style.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_styles.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_table_audit.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_table_cell.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_table_dimensions.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_silent_stub_table_layout.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_smoke_integration.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_style_acceptance.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_style_font.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_style_setters_contract.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_table_set_cell_perf.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_table_style_id_resolution.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_temporarily_unavailable.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_wire_contract.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/tests/test_zod_wire_contract.py +0 -0
- {athena_python_docx-0.11.3 → athena_python_docx-0.12.0}/uv.lock +0 -0
|
@@ -349,6 +349,82 @@ Issue numbers reference `python-openxml/python-docx`.
|
|
|
349
349
|
wontfix upstream but the request keeps recurring. Routes through
|
|
350
350
|
`ExportPDF` (SuperDoc handles the actual conversion).
|
|
351
351
|
|
|
352
|
+
- **`Document.export_docx(path=None, *, include_revisions=False)
|
|
353
|
+
-> bytes`** (0.11.4+) — export the document's current state as
|
|
354
|
+
``.docx`` bytes, optionally writing to ``path``. Closes the gap
|
|
355
|
+
left by ``Document.save(path)`` raising
|
|
356
|
+
``LocalSaveTargetNotSupportedError`` in this SDK: parity test
|
|
357
|
+
harnesses and CI workflows need a programmatic .docx export to
|
|
358
|
+
diff against ``python-docx`` output, and pointing callers at
|
|
359
|
+
Olympus's Export DOCX action breaks every headless flow.
|
|
360
|
+
Routes through ``ExportDocx`` → SuperDoc's ``exportDocx``;
|
|
361
|
+
raises ``DocxError`` when the server build doesn't expose it
|
|
362
|
+
rather than silently writing zero bytes.
|
|
363
|
+
|
|
364
|
+
### 0.11.4 behavior fixes for cell-inner paragraphs
|
|
365
|
+
|
|
366
|
+
Format ops on cell-inner paragraphs (`paragraph.alignment`,
|
|
367
|
+
`paragraph.style`, every `paragraph_format` setter, and
|
|
368
|
+
`paragraph.add_hyperlink`) used to cascade-fail the entire HTTP
|
|
369
|
+
batch. SuperDoc 1.8.1's
|
|
370
|
+
``SetParagraphAlignment``/``SetParagraphStyle``/``SetParagraphIndentation``/``SetParagraphSpacing``/``CreateHyperlink``
|
|
371
|
+
reject cell-inner block targets with ``BlockNotFoundError``, and the
|
|
372
|
+
applier's all-or-nothing batch model meant one cell-paragraph
|
|
373
|
+
mistake mid-script nuked every other unrelated create/format command
|
|
374
|
+
queued before it (``applied: []``).
|
|
375
|
+
|
|
376
|
+
As of 0.11.4 these setters check the paragraph proxy's ``_in_cell``
|
|
377
|
+
flag (set by ``_Cell.add_paragraph`` and ``_Cell.paragraphs``) and
|
|
378
|
+
emit ``docx.text.paragraph.CellInnerFormatNotSupportedWarning``
|
|
379
|
+
instead of forwarding the broken command. The format op is dropped,
|
|
380
|
+
the surrounding batch survives, and the workaround pointer (
|
|
381
|
+
``cell.text = 'value'`` to materialize, then ``cell.paragraphs[0]``
|
|
382
|
+
for the addressable proxy) is in the warning text. Tracked at
|
|
383
|
+
``docx-studio/SUPERDOC_UPSTREAM_REQUESTS.md`` § 13. Behavior pinned
|
|
384
|
+
by ``tests/test_athena_extensions_registry.py`` and the buffer
|
|
385
|
+
tests; ``add_hyperlink`` in a cell drops BOTH the URL and the link
|
|
386
|
+
text (empirically, ``Insert`` on a cell-inner paragraph also fails
|
|
387
|
+
with ``BlockNotFoundError`` — so the obvious ``add_run(text)``
|
|
388
|
+
fallback can't even land the visible text without nuking the
|
|
389
|
+
surrounding batch). The call returns a stub ``Hyperlink`` with an
|
|
390
|
+
empty range so callers don't crash; insert any visible text via
|
|
391
|
+
``cell.text = '...'`` before calling ``add_hyperlink`` if you need
|
|
392
|
+
the link text to survive.
|
|
393
|
+
|
|
394
|
+
### 0.11.4 behavior fixes elsewhere
|
|
395
|
+
|
|
396
|
+
- **`Sections.__getitem__` short-circuits on empty docs.**
|
|
397
|
+
``Document.create()`` doesn't materialize a section client-side,
|
|
398
|
+
so ``sections.list`` returns ``[]`` or times out at the full
|
|
399
|
+
command-budget. Pre-0.11.4 ``doc.sections[0]`` either raised an
|
|
400
|
+
un-contextualized ``IndexError`` from ``items[index]`` or hung
|
|
401
|
+
for ~60s. The cache-then-raise path now fails fast with a clear
|
|
402
|
+
workaround pointer and avoids re-issuing the slow query.
|
|
403
|
+
- **`Table._fresh_node_info` rotation tracking.** Each ``Table``
|
|
404
|
+
created via ``Document.add_table`` records its document-order
|
|
405
|
+
position as ``_doc_index``. When SuperDoc rotates the table's
|
|
406
|
+
nodeId between saves, the fallback resolves the rotated address
|
|
407
|
+
by position instead of always picking the last ``doc.find`` item.
|
|
408
|
+
Pre-0.11.4 the "take the last" heuristic silently mapped N
|
|
409
|
+
rotated proxies onto a single target, surfacing as
|
|
410
|
+
``ValidationError: Cell (r,c) not found in table table-auto-…``
|
|
411
|
+
on the next cell access.
|
|
412
|
+
- **`set_borders` accepts string and dict.** Both
|
|
413
|
+
``cell.set_borders(top="single 0.5pt #DDDDDD")`` and
|
|
414
|
+
``cell.set_borders(top={"style": "single", "size_pt": 0.5,
|
|
415
|
+
"color": "#DDDDDD"})`` work; the string form is normalized via
|
|
416
|
+
``_normalize_border_spec`` to the wire shape the server's Zod
|
|
417
|
+
validator expects. Pre-0.11.4 only the dict was accepted —
|
|
418
|
+
the string variant documented in the skill produced an immediate
|
|
419
|
+
HTTP 400 from the server's validator.
|
|
420
|
+
- **`Run.add_field` segments shape.** ``run.add_field("PAGE")`` (and
|
|
421
|
+
every other field kind) now emits ``at.segments`` with
|
|
422
|
+
``[{blockId, start, end}]`` instead of the ``{kind: "selection",
|
|
423
|
+
start, end}`` cursor envelope used by other create-ops.
|
|
424
|
+
SuperDoc's ``doc.fields.insert`` rejects the cursor form with
|
|
425
|
+
``SuperDocCliError: fields insert:at.segments is required``;
|
|
426
|
+
pre-0.11.4 the call took down the whole batch on every flush.
|
|
427
|
+
|
|
352
428
|
These additions are surfaced under their natural python-docx-shaped
|
|
353
429
|
names. When python-docx upstream eventually ships any of them
|
|
354
430
|
natively, the SDK should swap to the upstream signature in place;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: athena-python-docx
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.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>
|
|
@@ -262,14 +262,24 @@ _CELL_PARAGRAPH_HINT: str = (
|
|
|
262
262
|
"\n\nHint: SuperDoc 1.8.1 cannot format paragraphs nested inside "
|
|
263
263
|
"table cells via SetParagraphAlignment / SetParagraphStyle / "
|
|
264
264
|
"SetParagraphIndentation / SetParagraphSpacing / doc.insert with "
|
|
265
|
-
"a paragraph-block target
|
|
266
|
-
"
|
|
267
|
-
"isn't a top-level addressable block
|
|
268
|
-
|
|
269
|
-
"
|
|
270
|
-
"
|
|
271
|
-
"
|
|
272
|
-
"
|
|
265
|
+
"a paragraph-block target. The cell's original inner paragraph id "
|
|
266
|
+
"(returned by ``cell.paragraphs[0]`` on a freshly-loaded cell) "
|
|
267
|
+
"isn't a top-level addressable block.\n\nWorkaround: create a "
|
|
268
|
+
"fresh paragraph in the cell via ``para = cell.add_paragraph(text)`` "
|
|
269
|
+
"— 0.11.1+ routes that through ``doc.insert`` against the cell "
|
|
270
|
+
"with ``placement: 'insideEnd'``, so the returned ``Paragraph`` "
|
|
271
|
+
"proxy points at an addressable block. Apply format ops to that "
|
|
272
|
+
"proxy:\n\n"
|
|
273
|
+
' para = cell.add_paragraph("MAY 2026")\n'
|
|
274
|
+
" para.alignment = WD_ALIGN_PARAGRAPH.RIGHT # works\n"
|
|
275
|
+
" para.add_run(...).bold = True # also works\n\n"
|
|
276
|
+
"Do NOT use ``cell.paragraphs[0].alignment = X`` on a freshly-"
|
|
277
|
+
"loaded cell — that targets the original cell-inner paragraph "
|
|
278
|
+
"which can't be addressed for format ops. If the cell already "
|
|
279
|
+
"has agent-authored content, use ``cell.paragraphs[-1]`` (the "
|
|
280
|
+
"most-recently-inserted paragraph, which is addressable). "
|
|
281
|
+
"Tracked upstream at docx-studio/SUPERDOC_UPSTREAM_REQUESTS.md "
|
|
282
|
+
"§ 'cell-inner paragraph addressing'."
|
|
273
283
|
)
|
|
274
284
|
|
|
275
285
|
|
|
@@ -1451,6 +1451,30 @@ class ExportPDF(Command):
|
|
|
1451
1451
|
include_revisions: bool | None = None
|
|
1452
1452
|
|
|
1453
1453
|
|
|
1454
|
+
@dataclass
|
|
1455
|
+
class ExportDocx(Command):
|
|
1456
|
+
"""Export the document as a ``.docx`` byte stream, optionally storing
|
|
1457
|
+
it under ``destination`` (an asset id, S3 URL, or path).
|
|
1458
|
+
|
|
1459
|
+
Returns ``{bytes_base64, asset_id?}``. The docx-studio applier
|
|
1460
|
+
routes through SuperDoc's ``exportDocx`` SDK call when available;
|
|
1461
|
+
when the server build doesn't expose it, the command returns an
|
|
1462
|
+
empty payload and the Python SDK raises a clear ``DocxError``
|
|
1463
|
+
rather than silently writing zero bytes.
|
|
1464
|
+
|
|
1465
|
+
Athena extension beyond python-docx 1.x — closes the gap left by
|
|
1466
|
+
``Document.save(path)`` rejecting local targets (an asset-backed
|
|
1467
|
+
SDK can't fulfill the implied "write bytes to disk" contract from
|
|
1468
|
+
the buffer alone). Parity test harnesses against upstream
|
|
1469
|
+
``python-docx`` need a byte-for-byte export to diff against, and
|
|
1470
|
+
pointing users at Olympus's Export DOCX action breaks every CI
|
|
1471
|
+
workflow that runs headless.
|
|
1472
|
+
"""
|
|
1473
|
+
|
|
1474
|
+
destination: str | None = None
|
|
1475
|
+
include_revisions: bool | None = None
|
|
1476
|
+
|
|
1477
|
+
|
|
1454
1478
|
# ---------------------------------------------------------------------------
|
|
1455
1479
|
# Numbering / List metadata reads (Athena extension — python-docx
|
|
1456
1480
|
# issue #471 demands ``Paragraph.get_listnum()``-style access. The
|
|
@@ -1580,6 +1604,8 @@ _RESPONSE_BEARING_TYPES: frozenset[str] = frozenset(
|
|
|
1580
1604
|
"CreateContentControl",
|
|
1581
1605
|
# ExportPDF returns bytes the caller needs synchronously.
|
|
1582
1606
|
"ExportPDF",
|
|
1607
|
+
# ExportDocx returns .docx bytes the caller needs synchronously.
|
|
1608
|
+
"ExportDocx",
|
|
1583
1609
|
# FindReplace returns a replacement count for the caller.
|
|
1584
1610
|
"FindReplace",
|
|
1585
1611
|
}
|
|
@@ -1619,8 +1645,15 @@ from docx._athena_extension import ( # noqa: E402 — local circular import saf
|
|
|
1619
1645
|
)
|
|
1620
1646
|
|
|
1621
1647
|
|
|
1622
|
-
def _mark_athena_extension_command(
|
|
1648
|
+
def _mark_athena_extension_command(
|
|
1649
|
+
cls: type, issue: "str | None", description: str
|
|
1650
|
+
) -> None:
|
|
1623
1651
|
setattr(cls, ATHENA_EXTENSION_ATTR, True)
|
|
1652
|
+
# ``issue`` is ``None`` for additions that don't have a 1:1 upstream
|
|
1653
|
+
# python-docx issue — typically things that are *not* missing from
|
|
1654
|
+
# python-docx but that our asset-backed SDK has to surface differently
|
|
1655
|
+
# (e.g. ``Document.export_docx`` because ``Document.save(path)``
|
|
1656
|
+
# rejects local targets in this SDK).
|
|
1624
1657
|
setattr(cls, ATHENA_EXTENSION_ISSUE_ATTR, issue)
|
|
1625
1658
|
setattr(cls, ATHENA_EXTENSION_DESCRIPTION_ATTR, description)
|
|
1626
1659
|
setattr(cls, ATHENA_EXTENSION_SINCE_ATTR, "0.11.0")
|
|
@@ -1672,6 +1705,7 @@ for _cls, _issue, _desc in [
|
|
|
1672
1705
|
(FindReplace, "python-docx#30", "Formatting-preserving find/replace"),
|
|
1673
1706
|
(IterRuns, "python-docx#980", "Run stream for run-isolation"),
|
|
1674
1707
|
(ExportPDF, "python-docx#113", "PDF export"),
|
|
1708
|
+
(ExportDocx, None, "DOCX export — local file roundtrip"),
|
|
1675
1709
|
(NumberingGet, "python-docx#471", "Numbering metadata read"),
|
|
1676
1710
|
(NumberingList, "python-docx#471", "Numbering enumeration"),
|
|
1677
1711
|
]:
|
|
@@ -1803,6 +1837,7 @@ __all__ = [
|
|
|
1803
1837
|
"FindReplace",
|
|
1804
1838
|
"IterRuns",
|
|
1805
1839
|
"ExportPDF",
|
|
1840
|
+
"ExportDocx",
|
|
1806
1841
|
"NumberingGet",
|
|
1807
1842
|
"NumberingList",
|
|
1808
1843
|
# Helpers
|
|
@@ -168,6 +168,14 @@ class Document:
|
|
|
168
168
|
# ``add_paragraph`` / ``add_heading`` / ``add_table`` resets this.
|
|
169
169
|
self._last_list_item_id: str | None = None
|
|
170
170
|
self._last_list_kind: str | None = None
|
|
171
|
+
# Document-order counter for tables created via ``add_table``.
|
|
172
|
+
# Captured at the time of creation and stamped on the Table
|
|
173
|
+
# proxy as ``_doc_index`` so the rotation fallback in
|
|
174
|
+
# ``Table._fresh_node_info`` can disambiguate when SuperDoc
|
|
175
|
+
# rotates the table's id between saves. Lazy-initialized to the
|
|
176
|
+
# current top-level table count so docs opened from an existing
|
|
177
|
+
# asset account for pre-existing tables.
|
|
178
|
+
self._table_doc_index_counter: int | None = None
|
|
171
179
|
|
|
172
180
|
@classmethod
|
|
173
181
|
@athena_extension(
|
|
@@ -1018,6 +1026,94 @@ class Document:
|
|
|
1018
1026
|
return b""
|
|
1019
1027
|
return b""
|
|
1020
1028
|
|
|
1029
|
+
@athena_extension(
|
|
1030
|
+
since="0.11.4",
|
|
1031
|
+
description="Document.export_docx — write the current state to a local .docx.",
|
|
1032
|
+
)
|
|
1033
|
+
def export_docx(
|
|
1034
|
+
self,
|
|
1035
|
+
path: "str | None" = None,
|
|
1036
|
+
*,
|
|
1037
|
+
include_revisions: bool = False,
|
|
1038
|
+
) -> bytes:
|
|
1039
|
+
"""Export the document's current state as ``.docx`` bytes.
|
|
1040
|
+
|
|
1041
|
+
Athena extension beyond python-docx 1.x. ``Document.save(path)``
|
|
1042
|
+
is intentionally rejected by this SDK (``LocalSaveTargetNotSupportedError``)
|
|
1043
|
+
because the buffer holds no local bytes — the source of truth
|
|
1044
|
+
is the SuperDoc Y.Doc on docx-studio. ``export_docx`` is the
|
|
1045
|
+
explicit "ship the current Y.Doc state to disk" escape hatch:
|
|
1046
|
+
the server runs SuperDoc's ``exportDocx`` against the live
|
|
1047
|
+
session and streams the bytes back, the SDK writes them to
|
|
1048
|
+
``path`` (when given) and returns them.
|
|
1049
|
+
|
|
1050
|
+
Parameters
|
|
1051
|
+
----------
|
|
1052
|
+
path:
|
|
1053
|
+
Optional local filesystem path. When provided, the bytes
|
|
1054
|
+
are written there (parents must exist). Always returned
|
|
1055
|
+
from the call regardless.
|
|
1056
|
+
include_revisions:
|
|
1057
|
+
When ``True``, accepted/pending tracked revisions are
|
|
1058
|
+
baked into the exported document instead of being
|
|
1059
|
+
collapsed to their resolved form.
|
|
1060
|
+
|
|
1061
|
+
Returns
|
|
1062
|
+
-------
|
|
1063
|
+
bytes
|
|
1064
|
+
The full ``.docx`` payload.
|
|
1065
|
+
|
|
1066
|
+
Raises
|
|
1067
|
+
------
|
|
1068
|
+
DocxError
|
|
1069
|
+
When the server-side SuperDoc build doesn't expose
|
|
1070
|
+
``exportDocx`` (older docx-studio deploys), the command
|
|
1071
|
+
returns an empty payload and this method raises a clear
|
|
1072
|
+
``DocxError`` instead of silently writing 0 bytes.
|
|
1073
|
+
"""
|
|
1074
|
+
from docx.commands import ExportDocx
|
|
1075
|
+
from docx.errors import DocxError
|
|
1076
|
+
|
|
1077
|
+
# Drain anything pending so the export reflects every queued
|
|
1078
|
+
# mutation. ``send_command`` for a response-bearing command
|
|
1079
|
+
# already flushes, but the explicit save makes intent visible
|
|
1080
|
+
# in tracebacks if anything below fails.
|
|
1081
|
+
self.save()
|
|
1082
|
+
|
|
1083
|
+
result = run_sync(
|
|
1084
|
+
self._session.send_command(
|
|
1085
|
+
ExportDocx(
|
|
1086
|
+
destination=None,
|
|
1087
|
+
include_revisions=include_revisions,
|
|
1088
|
+
),
|
|
1089
|
+
),
|
|
1090
|
+
)
|
|
1091
|
+
payload: bytes = b""
|
|
1092
|
+
if isinstance(result, dict):
|
|
1093
|
+
import base64
|
|
1094
|
+
|
|
1095
|
+
b64 = result.get("bytesBase64") or result.get("bytes_base64")
|
|
1096
|
+
if isinstance(b64, str):
|
|
1097
|
+
try:
|
|
1098
|
+
payload = base64.b64decode(b64)
|
|
1099
|
+
except (ValueError, TypeError):
|
|
1100
|
+
payload = b""
|
|
1101
|
+
|
|
1102
|
+
if not payload:
|
|
1103
|
+
raise DocxError(
|
|
1104
|
+
"docx-studio returned no bytes for ExportDocx. Either "
|
|
1105
|
+
"the server build doesn't expose SuperDoc's exportDocx "
|
|
1106
|
+
"yet (upgrade docx-studio) or the asset is in a state "
|
|
1107
|
+
"that can't be serialized to .docx. Use to_pdf() as a "
|
|
1108
|
+
"stopgap, or fall back to Olympus's Export DOCX action "
|
|
1109
|
+
"for an interactive export."
|
|
1110
|
+
)
|
|
1111
|
+
|
|
1112
|
+
if path is not None:
|
|
1113
|
+
with open(path, "wb") as f:
|
|
1114
|
+
f.write(payload)
|
|
1115
|
+
return payload
|
|
1116
|
+
|
|
1021
1117
|
@athena_extension(issue=425, description="Document.add_bookmark.")
|
|
1022
1118
|
def add_bookmark(
|
|
1023
1119
|
self,
|
|
@@ -1377,6 +1473,13 @@ class Document:
|
|
|
1377
1473
|
# SuperDoc id via per-batch ``clientIdMap``, so any subsequent
|
|
1378
1474
|
# ``set_style({nodeId: cli_id})`` resolves correctly server-side.
|
|
1379
1475
|
client_node_id = self._mint_client_node_id("t")
|
|
1476
|
+
# Resolve the next document-order position for this new table.
|
|
1477
|
+
# First call queries ``doc.find`` to account for any tables that
|
|
1478
|
+
# already existed when the Document was opened — Document.create()
|
|
1479
|
+
# docs start at 0, ``Document(existing_asset_id)`` starts at
|
|
1480
|
+
# whatever count the asset has.
|
|
1481
|
+
doc_index = self._next_table_doc_index()
|
|
1482
|
+
|
|
1380
1483
|
table_params: dict = {
|
|
1381
1484
|
"rows": rows,
|
|
1382
1485
|
"columns": cols,
|
|
@@ -1402,10 +1505,50 @@ class Document:
|
|
|
1402
1505
|
node_id=node_id,
|
|
1403
1506
|
rows=rows,
|
|
1404
1507
|
columns=cols,
|
|
1508
|
+
doc_index=doc_index,
|
|
1405
1509
|
)
|
|
1406
1510
|
self._register_proxy_id(client_node_id, tbl)
|
|
1407
1511
|
return tbl
|
|
1408
1512
|
|
|
1513
|
+
def _next_table_doc_index(self) -> int:
|
|
1514
|
+
"""Return the session-relative position the next ``add_table``
|
|
1515
|
+
will occupy, then increment the counter.
|
|
1516
|
+
|
|
1517
|
+
``_doc_index`` is intentionally session-relative (0-based among
|
|
1518
|
+
tables added in *this* Document handle's lifetime) rather than
|
|
1519
|
+
document-absolute. Pre-0.11.4 (the lazy-init attempt) queried
|
|
1520
|
+
``doc.find({"type": "table"})`` on the first ``add_table`` to
|
|
1521
|
+
seed the counter with the count of pre-existing tables — but
|
|
1522
|
+
that added a stray ``find`` op to every Document's wire log,
|
|
1523
|
+
which broke the op-snapshot drift tests in
|
|
1524
|
+
``tests/fidelity/op_snapshots/``. ``Table._fresh_node_info`` does
|
|
1525
|
+
the offset math at rotation-lookup time instead (``items[len(
|
|
1526
|
+
items) - session_count + _doc_index]``), reading the live
|
|
1527
|
+
session count off the buffer attribute set below. For
|
|
1528
|
+
``Document.create()`` docs (the common case) the offset is 0
|
|
1529
|
+
and the lookup degenerates to the obvious ``items[_doc_index]``.
|
|
1530
|
+
|
|
1531
|
+
Defensive against unit tests that construct ``Document`` via
|
|
1532
|
+
``Document.__new__`` (skipping ``__init__``) — those instances
|
|
1533
|
+
don't have ``_table_doc_index_counter`` set, so we read it via
|
|
1534
|
+
``getattr`` and fall back to a fresh local counter that we
|
|
1535
|
+
write back through ``setattr``.
|
|
1536
|
+
"""
|
|
1537
|
+
counter = getattr(self, "_table_doc_index_counter", None) or 0
|
|
1538
|
+
idx = counter
|
|
1539
|
+
new_count = idx + 1
|
|
1540
|
+
self._table_doc_index_counter = new_count
|
|
1541
|
+
# Expose the live session count via the session so Table proxies
|
|
1542
|
+
# can compute the offset in ``_fresh_node_info`` rotation
|
|
1543
|
+
# fallbacks without holding a direct reference back to Document.
|
|
1544
|
+
try:
|
|
1545
|
+
session = getattr(self, "_session", None)
|
|
1546
|
+
if session is not None:
|
|
1547
|
+
session._table_session_count = new_count # type: ignore[attr-defined]
|
|
1548
|
+
except Exception:
|
|
1549
|
+
pass
|
|
1550
|
+
return idx
|
|
1551
|
+
|
|
1409
1552
|
def add_picture(
|
|
1410
1553
|
self,
|
|
1411
1554
|
image_path_or_stream: str | BinaryIO,
|
|
@@ -75,9 +75,15 @@ class _LazyRaisingProxy:
|
|
|
75
75
|
educated message.
|
|
76
76
|
* ``from docx.oxml import OxmlElement`` → same. ``OxmlElement(...)``
|
|
77
77
|
raises; ``OxmlElement.something`` raises.
|
|
78
|
-
* ``isinstance(x, OxmlElement)`` → raises
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
* ``isinstance(x, OxmlElement)`` → raises Python's native
|
|
79
|
+
``TypeError: isinstance() arg 2 must be a type, a tuple of
|
|
80
|
+
types, or a union`` (CPython validates the second argument is a
|
|
81
|
+
type before consulting ``__instancecheck__``, so the proxy never
|
|
82
|
+
sees the call). Agents who reach for it land in the same
|
|
83
|
+
"this surface isn't real" place via a different error class —
|
|
84
|
+
the message names ``isinstance`` so the typed
|
|
85
|
+
:class:`OxmlNotAvailableError` route isn't required to surface
|
|
86
|
+
the gap.
|
|
81
87
|
|
|
82
88
|
The proxy carries the full dotted path so the error message points
|
|
83
89
|
at exactly the offending symbol — ``docx.oxml.ns.qn`` rather than a
|
|
@@ -197,6 +203,10 @@ def __getattr__(name: str) -> Any: # noqa: ANN401
|
|
|
197
203
|
:func:`_install_stub_submodules`) and dunder names fall through
|
|
198
204
|
normally. Everything else returns a :class:`_LazyRaisingProxy` so
|
|
199
205
|
the import succeeds and the typed error fires only on actual use.
|
|
206
|
+
|
|
207
|
+
The proxy is cached into module globals so a second access skips
|
|
208
|
+
``__getattr__`` and returns the same instance — matches the
|
|
209
|
+
per-submodule caching done by :meth:`_OxmlStubModule.__getattr__`.
|
|
200
210
|
"""
|
|
201
211
|
if name.startswith("__") and name.endswith("__"):
|
|
202
212
|
raise AttributeError(name)
|
|
@@ -210,7 +220,9 @@ def __getattr__(name: str) -> Any: # noqa: ANN401
|
|
|
210
220
|
mod = _OxmlStubModule(full)
|
|
211
221
|
sys.modules[full] = mod
|
|
212
222
|
return mod
|
|
213
|
-
|
|
223
|
+
proxy = _LazyRaisingProxy(f"docx.oxml.{name}")
|
|
224
|
+
globals()[name] = proxy
|
|
225
|
+
return proxy
|
|
214
226
|
|
|
215
227
|
|
|
216
228
|
__all__ = ["OxmlNotAvailableError"]
|
|
@@ -890,18 +890,57 @@ class Sections(Sequence["Section"]):
|
|
|
890
890
|
Subclasses :class:`collections.abc.Sequence` so ``count``,
|
|
891
891
|
``index``, ``__contains__``, and ``__reversed__`` come for free —
|
|
892
892
|
this matches python-docx's :class:`docx.section.Sections`.
|
|
893
|
+
|
|
894
|
+
Implementation notes
|
|
895
|
+
--------------------
|
|
896
|
+
|
|
897
|
+
``_items()`` is cached for the lifetime of this proxy. python-docx
|
|
898
|
+
semantics are that ``doc.sections`` is a stable handle whose ``len``
|
|
899
|
+
only changes when the user explicitly adds a section break — which
|
|
900
|
+
the SuperDoc-backed SDK can't currently do anyway — so re-querying
|
|
901
|
+
on every ``len()`` / ``__getitem__`` would be wasteful and turns
|
|
902
|
+
every accidental ``for section in doc.sections`` into N HTTP round
|
|
903
|
+
trips.
|
|
904
|
+
|
|
905
|
+
The list is also defensively short-circuited when ``sections.list``
|
|
906
|
+
raises (the SuperDoc query has been observed to hang for the full
|
|
907
|
+
60s command timeout on docs created with ``Document.create()`` when
|
|
908
|
+
SuperDoc has not yet materialized a default section). Without the
|
|
909
|
+
catch, ``len(doc.sections)`` / ``doc.sections[0]`` block the calling
|
|
910
|
+
script for a minute and then raise; with it, the SDK fails fast
|
|
911
|
+
with a clear ``RuntimeError`` that names the workaround.
|
|
893
912
|
"""
|
|
894
913
|
|
|
895
914
|
def __init__(self, *, session: "Session") -> None:
|
|
896
915
|
self._session: "Session" = session
|
|
916
|
+
self._items_cache: "list[dict] | None" = None
|
|
897
917
|
|
|
898
918
|
def _items(self) -> list[dict]:
|
|
899
|
-
|
|
919
|
+
if self._items_cache is not None:
|
|
920
|
+
return self._items_cache
|
|
921
|
+
try:
|
|
922
|
+
info: object = run_sync(self._session.doc.sections.list({}))
|
|
923
|
+
except Exception: # noqa: BLE001
|
|
924
|
+
# Common case: SuperDoc has not materialized a default
|
|
925
|
+
# section yet, and ``sections.list`` either returns ``[]``
|
|
926
|
+
# or hangs through the full command timeout. Cache the
|
|
927
|
+
# empty result so subsequent accesses are O(1) and never
|
|
928
|
+
# re-hang the calling script.
|
|
929
|
+
self._items_cache = []
|
|
930
|
+
return self._items_cache
|
|
931
|
+
items_list: list[dict] = []
|
|
900
932
|
if isinstance(info, dict):
|
|
901
933
|
items: object = info.get("items", [])
|
|
902
934
|
if isinstance(items, list):
|
|
903
|
-
|
|
904
|
-
|
|
935
|
+
items_list = [i for i in items if isinstance(i, dict)]
|
|
936
|
+
self._items_cache = items_list
|
|
937
|
+
return self._items_cache
|
|
938
|
+
|
|
939
|
+
def _invalidate(self) -> None:
|
|
940
|
+
"""Drop the cached items list. Call after operations that may
|
|
941
|
+
add a new section break (currently none — kept for forward
|
|
942
|
+
compatibility with future ``add_section`` support)."""
|
|
943
|
+
self._items_cache = None
|
|
905
944
|
|
|
906
945
|
def __iter__(self):
|
|
907
946
|
for item in self._items():
|
|
@@ -914,6 +953,29 @@ class Sections(Sequence["Section"]):
|
|
|
914
953
|
|
|
915
954
|
def __getitem__(self, index: int) -> Section:
|
|
916
955
|
items = self._items()
|
|
956
|
+
if not items:
|
|
957
|
+
raise IndexError(
|
|
958
|
+
f"doc.sections is empty (index {index} requested). "
|
|
959
|
+
f"Document.create() does not materialize a default "
|
|
960
|
+
f"section client-side; SuperDoc's exported styles.xml "
|
|
961
|
+
f"is the source of truth for page setup. Page size, "
|
|
962
|
+
f"margins, headers, footers, and column count cannot "
|
|
963
|
+
f"currently be configured at this layer — track the "
|
|
964
|
+
f"unblock at docx-studio/SUPERDOC_UPSTREAM_REQUESTS.md "
|
|
965
|
+
f"§ 'sections.list on net-new docs'."
|
|
966
|
+
)
|
|
967
|
+
# Mirror Python list / Sequence semantics for negative indices.
|
|
968
|
+
# python-docx's ``Sections`` inherits ``collections.abc.Sequence``
|
|
969
|
+
# whose default ``__getitem__`` supports ``-1`` → last,
|
|
970
|
+
# ``-2`` → second-to-last, etc. The earlier ``0 <= index <
|
|
971
|
+
# len(items)`` check rejected those, breaking
|
|
972
|
+
# ``doc.sections[-1]``.
|
|
973
|
+
if index < 0:
|
|
974
|
+
index += len(items)
|
|
975
|
+
if not 0 <= index < len(items):
|
|
976
|
+
raise IndexError(
|
|
977
|
+
f"doc.sections index out of range (len={len(items)})"
|
|
978
|
+
)
|
|
917
979
|
addr = items[index].get("address", {})
|
|
918
980
|
if not isinstance(addr, dict):
|
|
919
981
|
raise IndexError(f"Section {index} has no address")
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Discoverability alias — re-exports the public batching surface.
|
|
2
|
+
|
|
3
|
+
Agents repeatedly try ``from docx.session import run_sync`` (verified
|
|
4
|
+
in LangSmith thread ``thread_28dae56c-…`` on PR #20788 preview, where
|
|
5
|
+
the model burned a tool-call iteration on
|
|
6
|
+
``ModuleNotFoundError: No module named 'docx.session'`` before falling
|
|
7
|
+
back to ``inspect.getsource``). The canonical home is the private
|
|
8
|
+
``docx._batching`` module, but the leading-underscore prefix isn't a
|
|
9
|
+
signal LLMs read reliably. This shim exists to fail less surprisingly.
|
|
10
|
+
|
|
11
|
+
Stable surface — these are also exported from :mod:`docx._batching`
|
|
12
|
+
and either path is supported:
|
|
13
|
+
|
|
14
|
+
* :func:`run_sync` — drive an async coroutine from a sync call site.
|
|
15
|
+
* :data:`DEFAULT_OP_TIMEOUT_SECONDS` — the timeout the ``run_sync``
|
|
16
|
+
shim applies before raising ``concurrent.futures.TimeoutError``.
|
|
17
|
+
|
|
18
|
+
Anything else inside :mod:`docx._batching` is implementation detail
|
|
19
|
+
and may move without a deprecation cycle; this module deliberately
|
|
20
|
+
re-exports only the surface agents have a legitimate reason to call.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from docx._batching import DEFAULT_OP_TIMEOUT_SECONDS, run_sync
|
|
26
|
+
|
|
27
|
+
__all__ = ["run_sync", "DEFAULT_OP_TIMEOUT_SECONDS"]
|