athena-python-docx 0.7.0__tar.gz → 0.8.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.7.0 → athena_python_docx-0.8.0}/CLAUDE.md +27 -2
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/PKG-INFO +1 -1
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/__init__.py +1 -1
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/_http_doc.py +58 -5
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/commands.py +9 -2
- athena_python_docx-0.8.0/docx/errors.py +88 -0
- athena_python_docx-0.8.0/docx/oxml/__init__.py +148 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/table.py +90 -19
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/pyproject.toml +1 -1
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -6
- athena_python_docx-0.8.0/tests/fidelity/op_snapshots/17_table_basic.json +4 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -6
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +2 -1
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/23_nested_table.json +0 -1
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -1
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/35_full_report.json +12 -10
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -100
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -1
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -4
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/51_nested_tables_deep.json +0 -1
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -13
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +2 -1
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +3 -1
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -1
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +800 -401
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -19
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -1
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -3
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -30
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +18 -10
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -10
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -100
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex01_five_levels_deep_tables.json +0 -1
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -4
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -50
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -2
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +90 -66
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +240 -81
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -1
- athena_python_docx-0.8.0/tests/fidelity/op_snapshots/ex14_styled_report_table.json +211 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +120 -90
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +18 -15
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +18 -19
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +28 -23
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +32 -21
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +40 -31
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +8 -7
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +64 -32
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -12
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -13
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -8
- athena_python_docx-0.7.0/tests/fidelity/op_snapshots/ex14_styled_report_table.json → athena_python_docx-0.8.0/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +58 -47
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw14_nested_cell_table.json +0 -2
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/parity/baseline_gaps.json +1 -1
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/parity/compare.py +16 -1
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/parity/reports/GAP_ANALYSIS.md +3 -4
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/parity/reports/gap_report.json +3 -12
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/parity/snapshots/athena_latest.json +124 -59
- athena_python_docx-0.8.0/tests/test_block_not_found_error.py +246 -0
- athena_python_docx-0.8.0/tests/test_cell_text_plain_fastpath.py +60 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_command_dataclasses.py +10 -1
- athena_python_docx-0.8.0/tests/test_insert_deferred.py +138 -0
- athena_python_docx-0.8.0/tests/test_oxml_shim.py +123 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/uv.lock +1 -1
- athena_python_docx-0.7.0/docx/errors.py +0 -45
- athena_python_docx-0.7.0/tests/fidelity/op_snapshots/17_table_basic.json +0 -5
- athena_python_docx-0.7.0/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -181
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/.gitignore +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/README.md +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/_batching.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/_buffer.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/_http.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/_image_utils.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/_ptc.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/api.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/client.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/comments.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/document.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/enum/__init__.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/enum/section.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/enum/style.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/enum/table.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/enum/text.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/exceptions.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/opc/__init__.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/opc/coreprops.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/revisions.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/section.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/settings.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/shape.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/shared.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/styles/__init__.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/styles/style.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/styles/styles.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/text/__init__.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/text/font.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/text/hyperlink.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/text/pagebreak.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/text/paragraph.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/text/parfmt.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/text/run.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/text/tabstops.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/docx/typing.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/scripts/publish.sh +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/scripts/release.sh +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/scripts/round_trip_smoke.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/__init__.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/conftest.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/METHODOLOGY.md +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/README.md +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/__init__.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/ab_probe_cases.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/ab_probe_runner.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/auto_gen_cases.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/binary_round_trip.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/cases.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/complex_cases.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/coverage_report.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/extract.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/extreme_cases.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/fake_session.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/local_runner.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/mega_cases.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshot.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/103_cell_tables_enumeration.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/ours_spec.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/parity_crawl.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/parity_diff.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/real_world_cases.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/round_trip_tests.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/runner.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/stock_spec.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/parity/README.md +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/parity/__init__.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/parity/intentional_deviations.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/parity/introspect.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/parity/run_parity.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/parity/test_parity_gap.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_batching_perf.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_buffer.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_collapsed_range_format.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_commands.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_comments.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_document_create.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_http_transport.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_hyperlink_coalescing.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_iter_inner_content.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_list_styles.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_merged_cells.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_paragraph_text_len_cache.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_parity_misc.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_parity_round2.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_phase_a_behavior.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_phase_b_headers_footers.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_phase_c_tables.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_pr19766_review_fixes.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_ptc.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_python_docx_api_parity.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_revisions.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_add_picture.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_add_run.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_comments_get.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_document_audit.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_document_element.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_enum_section.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_font_audit.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_header_footer.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_hyperlink.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_inline_shape.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_misc.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_paragraph_style.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_parfmt.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_row_col_cell.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_run_add_break.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_run_style.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_run_style_strict.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_run_text.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_run_underline.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_section_audit.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_section_dimensions.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_section_onoff.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_settings.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_shared_audit.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_style.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_styles.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_table_audit.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_table_cell.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_table_dimensions.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_silent_stub_table_layout.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_smoke_integration.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_style_acceptance.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_style_font.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_style_setters_contract.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_wire_contract.py +0 -0
- {athena_python_docx-0.7.0 → athena_python_docx-0.8.0}/tests/test_zod_wire_contract.py +0 -0
|
@@ -39,6 +39,18 @@ These standard python-docx members don't apply to a Superdoc-backed SDK:
|
|
|
39
39
|
`Comment.tables` — SuperDoc models a comment as a single text body,
|
|
40
40
|
not a `BlockItemContainer`. Use `Comment.text` instead. The
|
|
41
41
|
paragraph/table-bearing surface raises `CommentsNotImplementedError`.
|
|
42
|
+
- **`docx.oxml.*`** — the entire raw-OOXML module tree (``ns.qn``,
|
|
43
|
+
``OxmlElement``, ``CT_*`` classes, etc.) is unavailable because the
|
|
44
|
+
SDK is HTTP-only against a SuperDoc Y.Doc; there is no local lxml
|
|
45
|
+
tree to manipulate. As of 0.8.0, ``docx.oxml`` is a real stub
|
|
46
|
+
package that raises a typed
|
|
47
|
+
:class:`docx.oxml.OxmlNotAvailableError` (subclass of
|
|
48
|
+
``ImportError``) on any attribute access, with an inline pointer at
|
|
49
|
+
the high-level API (``Document.add_paragraph``, ``Run.bold``,
|
|
50
|
+
``_Cell.text``, …) or the typed command surface in
|
|
51
|
+
``docx.commands``. Pre-0.8.0 this used to fail with the stdlib's
|
|
52
|
+
generic ``ModuleNotFoundError: No module named 'docx.oxml'`` and
|
|
53
|
+
agents had to retry to discover the gap.
|
|
42
54
|
|
|
43
55
|
### Phase-3b upstream-blocked surface
|
|
44
56
|
|
|
@@ -149,13 +161,26 @@ This is a **thin HTTP client** that mimics the sync python-docx API.
|
|
|
149
161
|
the context-manager exit drain explicitly; `docx.flush_all()` is
|
|
150
162
|
the Daytona-prelude hook that flushes every live buffer in the
|
|
151
163
|
process.
|
|
152
|
-
-
|
|
153
|
-
|
|
164
|
+
- **Insert deferral (0.8.0+):** `Insert` was removed from the
|
|
165
|
+
response-bearing set because every in-tree caller
|
|
166
|
+
(`Paragraph._insert_run_text_segments`, `Run.add_text`, `_Cell.text`)
|
|
167
|
+
ignores the response — the Run proxy wraps a known client-side range
|
|
168
|
+
and doesn't need the server's echoed node id. Net result: a styled
|
|
169
|
+
run (one `add_run` + 6 format setters) ships as 1 HTTP request
|
|
170
|
+
instead of 2. The remaining response-bearing ops without a client id
|
|
171
|
+
(`ListsCreate`, `ListsAttach`, `CommentsCreate`, `CommentsPatch`,
|
|
154
172
|
`TrackChangesDecide`) keep their eager-flush semantics so callers
|
|
155
173
|
reading back ids still see real data. A `Document.add_paragraph`
|
|
156
174
|
with `style="List Bullet"` flushes the queued CreateParagraph in
|
|
157
175
|
the same batch as the follow-up `ListsCreate` — still one HTTP
|
|
158
176
|
request per logical operation.
|
|
177
|
+
- **Plain-text cell fast path (0.8.0+):** `_Cell.text = value` skips
|
|
178
|
+
the eager `doc.markdownToFragment` query when ``value`` contains no
|
|
179
|
+
markdown control characters (the dominant case for tabular data —
|
|
180
|
+
numbers, labels, currency strings). The fragment is built locally
|
|
181
|
+
and the follow-up `doc.insert` is buffered, so plain cell
|
|
182
|
+
assignments are 0 HTTP requests until the next flush. See
|
|
183
|
+
``docx.table._is_plain_text`` for the trigger predicate.
|
|
159
184
|
- The path-proxy in `_http_doc.py` is an internal translation layer:
|
|
160
185
|
call sites that look like `await self._session.doc.create.paragraph(p)`
|
|
161
186
|
resolve to `CommandBuffer.call(CreateParagraph(**p))`. Rewriting call
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: athena-python-docx
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.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>
|
|
@@ -112,6 +112,7 @@ from docx.commands import (
|
|
|
112
112
|
)
|
|
113
113
|
from docx.errors import (
|
|
114
114
|
AuthenticationError,
|
|
115
|
+
BlockNotFoundError,
|
|
115
116
|
DocxError,
|
|
116
117
|
NotFoundError,
|
|
117
118
|
SessionError,
|
|
@@ -134,6 +135,39 @@ _NOT_FOUND_ERROR_NAMES: frozenset[str] = frozenset(
|
|
|
134
135
|
)
|
|
135
136
|
|
|
136
137
|
|
|
138
|
+
def _looks_like_block_not_found(err_obj: dict) -> bool:
|
|
139
|
+
"""Detect SuperDoc's ``Block "<type>:<id>" was not found`` shape.
|
|
140
|
+
|
|
141
|
+
Triggers the typed :class:`BlockNotFoundError` so agent code can
|
|
142
|
+
distinguish a missing-block miss (cell-inner-paragraph addressing
|
|
143
|
+
bug, stale id from a different session) from a generic transport
|
|
144
|
+
or validation error.
|
|
145
|
+
"""
|
|
146
|
+
msg = err_obj.get("message")
|
|
147
|
+
if not isinstance(msg, str):
|
|
148
|
+
return False
|
|
149
|
+
lower = msg.lower()
|
|
150
|
+
if "not found" not in lower:
|
|
151
|
+
return False
|
|
152
|
+
# Be conservative: require either ``block "`` (the SuperDoc CLI's
|
|
153
|
+
# quoted-id format) or ``block <id>`` followed by ``not found``.
|
|
154
|
+
return 'block "' in lower or ('block ' in lower and ' was not found' in lower)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
_CELL_PARAGRAPH_HINT: str = (
|
|
158
|
+
"\n\nHint: SuperDoc 1.8.1 cannot format paragraphs nested inside "
|
|
159
|
+
"table cells via SetParagraphAlignment / SetParagraphStyle / "
|
|
160
|
+
"SetParagraphIndentation / SetParagraphSpacing or doc.insert with "
|
|
161
|
+
"a paragraph-block target. The cell's inner paragraph id is "
|
|
162
|
+
"returned by cell.getNodeById but isn't a top-level addressable "
|
|
163
|
+
"block. Materialize the cell's paragraph first via "
|
|
164
|
+
'``cell.text = "value"``, then re-read ``cell.paragraphs[0]`` and '
|
|
165
|
+
"apply format ops to that post-materialization Paragraph proxy. "
|
|
166
|
+
"Tracked upstream at docx-studio/SUPERDOC_UPSTREAM_REQUESTS.md "
|
|
167
|
+
"§ 'cell-inner paragraph addressing'."
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
137
171
|
def _looks_like_not_found(parsed: dict) -> bool:
|
|
138
172
|
"""Inspect a docx-studio partial-failure dict and decide whether it
|
|
139
173
|
describes a "no such entity" miss (vs. a real transport / validation
|
|
@@ -285,11 +319,30 @@ def _http_post_json(
|
|
|
285
319
|
# sites (``Comments.get``) can coerce it to ``None`` without
|
|
286
320
|
# swallowing real transport / validation failures.
|
|
287
321
|
err_obj = parsed.get("error") if isinstance(parsed, dict) else None
|
|
288
|
-
if isinstance(err_obj, dict)
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
322
|
+
if isinstance(err_obj, dict):
|
|
323
|
+
base_msg = f"docx-studio batch reported a partial failure: {parsed!r}"
|
|
324
|
+
# ``BlockNotFoundError`` is the narrower miss — caller is most
|
|
325
|
+
# likely targeting a cell-inner paragraph or stale-session
|
|
326
|
+
# block id. Surface the typed exception plus an agent-readable
|
|
327
|
+
# workaround so the next attempt doesn't repeat the same
|
|
328
|
+
# mistake. The cell-paragraph hint only applies when the
|
|
329
|
+
# missing id has the ``paragraph:`` prefix (SuperDoc's quoted-
|
|
330
|
+
# id error format) — stale list-item / table-row / image ids
|
|
331
|
+
# would benefit from a different hint or none at all, and a
|
|
332
|
+
# red-herring "use cell.text" pointer would just waste the
|
|
333
|
+
# next retry.
|
|
334
|
+
if _looks_like_block_not_found(err_obj):
|
|
335
|
+
msg_str = err_obj.get("message")
|
|
336
|
+
paragraph_block = (
|
|
337
|
+
isinstance(msg_str, str)
|
|
338
|
+
and "paragraph:" in msg_str.lower()
|
|
339
|
+
)
|
|
340
|
+
raise BlockNotFoundError(
|
|
341
|
+
base_msg + (_CELL_PARAGRAPH_HINT if paragraph_block else ""),
|
|
342
|
+
payload=err_obj,
|
|
343
|
+
)
|
|
344
|
+
if _looks_like_not_found(err_obj):
|
|
345
|
+
raise NotFoundError(base_msg, payload=err_obj)
|
|
293
346
|
raise DocxError(
|
|
294
347
|
f"docx-studio batch reported a partial failure: {parsed!r}",
|
|
295
348
|
)
|
|
@@ -814,8 +814,15 @@ _RESPONSE_BEARING_TYPES: frozenset[str] = frozenset(
|
|
|
814
814
|
# CreateImage returns {image: {nodeId}}; SetImageSize and the
|
|
815
815
|
# InlineShape proxy both need that nodeId, so it must flush.
|
|
816
816
|
"CreateImage",
|
|
817
|
-
# Insert
|
|
818
|
-
|
|
817
|
+
# Insert is INTENTIONALLY not here as of 0.8.0. All in-tree
|
|
818
|
+
# callers (``Paragraph._insert_run_text_segments``, ``Run.add_text``,
|
|
819
|
+
# ``_Cell.text``) wrap a known client-side range/cell target and
|
|
820
|
+
# never read Insert's response; deferring it lets a styled
|
|
821
|
+
# ``add_run`` flow ship its text-insert in the same HTTP batch
|
|
822
|
+
# as the format setters that follow it. If a future caller needs
|
|
823
|
+
# the Insert response, route it through ``CommandBuffer._eager_flush_with``
|
|
824
|
+
# at the call site rather than re-adding Insert here, so the
|
|
825
|
+
# batched path isn't penalized for the one outlier.
|
|
819
826
|
# CreateSectionBreak intentionally NOT here — it returns no node id;
|
|
820
827
|
# the caller resolves the new section via sections.list afterwards.
|
|
821
828
|
# CommentsCreate returns the new comment's entityId — caller wraps
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Exception types for athena-python-docx."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DocxError(Exception):
|
|
7
|
+
"""Base class for all athena-python-docx errors."""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SessionError(DocxError):
|
|
11
|
+
"""Raised when the Superdoc SDK session cannot be established or used."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AuthenticationError(SessionError):
|
|
15
|
+
"""Raised when Keryx rejects the collab token."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class UnsupportedProviderError(SessionError):
|
|
19
|
+
"""Raised when the asset is on ysweet instead of yhub.
|
|
20
|
+
|
|
21
|
+
Phase 1 only supports yhub; ysweet-routed assets must be migrated first.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class DocumentClosedError(DocxError):
|
|
26
|
+
"""Raised when operating on a closed Document."""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ValidationError(DocxError):
|
|
30
|
+
"""Raised when SDK-level validation fails (bad args, out-of-range indices)."""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class NotFoundError(DocxError):
|
|
34
|
+
"""Raised when a server-side lookup determines that the addressed
|
|
35
|
+
entity does not exist (comment id, node id, …).
|
|
36
|
+
|
|
37
|
+
Distinct from :class:`DocxError` so callers can speculatively read
|
|
38
|
+
(``Comments.get``, future ``find_by_id`` patterns) and coerce a
|
|
39
|
+
typed miss to ``None`` without swallowing real transport/auth
|
|
40
|
+
failures.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(self, message: str, payload: dict | None = None) -> None:
|
|
44
|
+
super().__init__(message)
|
|
45
|
+
self.payload: dict = payload or {}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class BlockNotFoundError(NotFoundError):
|
|
49
|
+
"""Raised when SuperDoc reports ``Block "<type>:<id>" was not found``.
|
|
50
|
+
|
|
51
|
+
Most commonly fires when paragraph-format operations target a
|
|
52
|
+
paragraph node nested inside a table cell. SuperDoc 1.8.1's
|
|
53
|
+
``format.paragraph.setAlignment`` / ``setStyle`` / ``setIndentation``
|
|
54
|
+
/ ``setSpacing`` and ``doc.insert``-into-block-target ops don't
|
|
55
|
+
traverse into table cells: the inner paragraph's id is returned by
|
|
56
|
+
``cell.getNodeById`` but isn't a valid top-level addressable block.
|
|
57
|
+
|
|
58
|
+
The agent-facing workaround is:
|
|
59
|
+
|
|
60
|
+
cell.text = "value" # materializes the cell's paragraph as
|
|
61
|
+
# a real block once content lands.
|
|
62
|
+
p = cell.paragraphs[0] # the *post-materialization* paragraph
|
|
63
|
+
p.alignment = ... # now resolves.
|
|
64
|
+
|
|
65
|
+
See ``docx-studio/SUPERDOC_UPSTREAM_REQUESTS.md`` § "cell-inner
|
|
66
|
+
paragraph addressing" for the upstream ask.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def block_id(self) -> str | None:
|
|
71
|
+
"""Best-effort extraction of the missing block id from the SuperDoc
|
|
72
|
+
error message. Returns ``None`` when the prefix can't be parsed.
|
|
73
|
+
"""
|
|
74
|
+
msg = self.payload.get("message")
|
|
75
|
+
if not isinstance(msg, str):
|
|
76
|
+
return None
|
|
77
|
+
# SuperDoc formats the message as ``Block "<type>:<uuid>" was not
|
|
78
|
+
# found.`` (or ``Block "<uuid>" not found.`` for some shapes) —
|
|
79
|
+
# extract whatever sits between the quotes, then strip an optional
|
|
80
|
+
# ``<type>:`` prefix so callers can compare against bare ids.
|
|
81
|
+
start = msg.find('"')
|
|
82
|
+
end = msg.find('"', start + 1) if start >= 0 else -1
|
|
83
|
+
if start < 0 or end <= start:
|
|
84
|
+
return None
|
|
85
|
+
quoted = msg[start + 1 : end]
|
|
86
|
+
if ":" in quoted:
|
|
87
|
+
return quoted.split(":", 1)[1]
|
|
88
|
+
return quoted
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""Stub package — ``docx.oxml`` is intentionally unavailable in
|
|
2
|
+
athena-python-docx.
|
|
3
|
+
|
|
4
|
+
python-docx upstream exposes raw OOXML element classes (``OxmlElement``,
|
|
5
|
+
``CT_*``, ``qn``, ``nsmap``, …) for advanced users who manipulate
|
|
6
|
+
Word's XML directly. athena-python-docx is backed by a SuperDoc Y.Doc
|
|
7
|
+
+ Keryx pipeline, not a local OOXML tree, so there is no XML element
|
|
8
|
+
to manipulate.
|
|
9
|
+
|
|
10
|
+
Before 0.8.0 attempts to ``from docx.oxml.ns import qn`` failed with
|
|
11
|
+
the stdlib's generic ``ModuleNotFoundError: No module named
|
|
12
|
+
'docx.oxml'`` — agent code had to discover the gap by retry. As of
|
|
13
|
+
0.8.0 this package is a typed stub: every import path resolves, but
|
|
14
|
+
every attribute access raises :class:`OxmlNotAvailableError` with a
|
|
15
|
+
single actionable message pointing at the high-level python-docx
|
|
16
|
+
surface (``Document.add_paragraph``, ``Run.bold``, ``_Cell.text``, …)
|
|
17
|
+
or the typed command bus (``docx.commands``).
|
|
18
|
+
|
|
19
|
+
The gap is documented in ``docx-studio/python-sdk/CLAUDE.md`` § "Intentionally
|
|
20
|
+
omitted" and locked into the parity matrix via
|
|
21
|
+
``tests/parity/intentional_deviations.json`` (``docx.oxml**``).
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import sys
|
|
27
|
+
import types
|
|
28
|
+
from typing import Any
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class OxmlNotAvailableError(ImportError):
|
|
32
|
+
"""Raised on any access to ``docx.oxml.*`` symbols.
|
|
33
|
+
|
|
34
|
+
Inherits :class:`ImportError` so callers using
|
|
35
|
+
``try: from docx.oxml import X / except ImportError`` still match,
|
|
36
|
+
while ``except OxmlNotAvailableError`` lets newer code distinguish
|
|
37
|
+
a "no oxml surface" miss from a real import-resolution failure.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
_MESSAGE: str = (
|
|
42
|
+
"docx.oxml is not available in athena-python-docx — the SuperDoc "
|
|
43
|
+
"backend doesn't expose raw OOXML elements. Use the high-level "
|
|
44
|
+
"python-docx API (Document.add_paragraph, Run.bold, _Cell.text, …) "
|
|
45
|
+
"or the typed command surface in docx.commands for any mutation "
|
|
46
|
+
"the high-level API doesn't cover. "
|
|
47
|
+
"See docx-studio/python-sdk/CLAUDE.md § 'Intentionally omitted' for "
|
|
48
|
+
"the parity rationale."
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class _OxmlStubModule(types.ModuleType):
|
|
53
|
+
"""ModuleType subclass that raises :class:`OxmlNotAvailableError`
|
|
54
|
+
on every attribute access.
|
|
55
|
+
|
|
56
|
+
Installed into :data:`sys.modules` at package-init time so
|
|
57
|
+
qualified imports like ``from docx.oxml.ns import qn`` find a real
|
|
58
|
+
module object (avoiding ``ModuleNotFoundError``) and raise the
|
|
59
|
+
typed exception when the symbol is dereferenced.
|
|
60
|
+
|
|
61
|
+
No ``__slots__`` — ``types.ModuleType`` itself carries a
|
|
62
|
+
``__dict__``, so a slots declaration on a subclass is a no-op and
|
|
63
|
+
doesn't prevent ``docx.oxml.ns.qn = something`` from silently
|
|
64
|
+
bypassing :meth:`__getattr__` on the next read. If a future caller
|
|
65
|
+
needs to block that bypass we'd need a ``__setattr__`` guard that
|
|
66
|
+
rejects user-attribute writes while still letting the import
|
|
67
|
+
machinery install dunders (``__loader__``, ``__spec__``, …) on the
|
|
68
|
+
stub at package-init time.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
def __getattr__(self, name: str) -> Any: # noqa: ANN401
|
|
72
|
+
if name.startswith("__") and name.endswith("__"):
|
|
73
|
+
# Dunder access (e.g. ``__path__`` during submodule import)
|
|
74
|
+
# must fall through to AttributeError so Python's import
|
|
75
|
+
# machinery can probe without triggering our typed error.
|
|
76
|
+
raise AttributeError(name)
|
|
77
|
+
path = self.__name__
|
|
78
|
+
raise OxmlNotAvailableError(f"{path}.{name} — {_MESSAGE}")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# Common upstream submodule paths. Listed explicitly rather than wild-
|
|
82
|
+
# carded so adding a new one is a visible diff. Every entry maps to a
|
|
83
|
+
# single sentinel module; no per-submodule state.
|
|
84
|
+
_SUBMODULES: tuple[str, ...] = (
|
|
85
|
+
"ns",
|
|
86
|
+
"parser",
|
|
87
|
+
"element",
|
|
88
|
+
"xmlchemy",
|
|
89
|
+
"exceptions",
|
|
90
|
+
"ooxml",
|
|
91
|
+
"coreprops",
|
|
92
|
+
"document",
|
|
93
|
+
"text",
|
|
94
|
+
"table",
|
|
95
|
+
"comments",
|
|
96
|
+
"header_footer",
|
|
97
|
+
"footnote",
|
|
98
|
+
"endnote",
|
|
99
|
+
"shared",
|
|
100
|
+
"shape",
|
|
101
|
+
"section",
|
|
102
|
+
"settings",
|
|
103
|
+
"styles",
|
|
104
|
+
"numbering",
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _install_stub_submodules() -> None:
|
|
109
|
+
"""Pre-populate :data:`sys.modules` with stub entries for every
|
|
110
|
+
well-known ``docx.oxml.*`` submodule.
|
|
111
|
+
|
|
112
|
+
Idempotent — re-running is a no-op for entries already installed
|
|
113
|
+
(covers the rare case of a test that reloads the package).
|
|
114
|
+
"""
|
|
115
|
+
for short in _SUBMODULES:
|
|
116
|
+
full = f"docx.oxml.{short}"
|
|
117
|
+
if full in sys.modules:
|
|
118
|
+
continue
|
|
119
|
+
sys.modules[full] = _OxmlStubModule(full)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
_install_stub_submodules()
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def __getattr__(name: str) -> Any: # noqa: ANN401
|
|
126
|
+
"""PEP 562 attribute hook for ``from docx.oxml import X``.
|
|
127
|
+
|
|
128
|
+
Submodule names (handled via :data:`sys.modules` injection in
|
|
129
|
+
:func:`_install_stub_submodules`) and dunder names fall through to
|
|
130
|
+
the default ``AttributeError`` so Python's import machinery
|
|
131
|
+
behaves normally. Everything else raises the typed error.
|
|
132
|
+
"""
|
|
133
|
+
if name.startswith("__") and name.endswith("__"):
|
|
134
|
+
raise AttributeError(name)
|
|
135
|
+
if name in _SUBMODULES:
|
|
136
|
+
# Should already be present in sys.modules; defensive fallback
|
|
137
|
+
# so we never leak ModuleNotFoundError if the caller reloaded
|
|
138
|
+
# the package between calls.
|
|
139
|
+
full = f"docx.oxml.{name}"
|
|
140
|
+
mod = sys.modules.get(full)
|
|
141
|
+
if mod is None:
|
|
142
|
+
mod = _OxmlStubModule(full)
|
|
143
|
+
sys.modules[full] = mod
|
|
144
|
+
return mod
|
|
145
|
+
raise OxmlNotAvailableError(f"docx.oxml.{name} — {_MESSAGE}")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
__all__ = ["OxmlNotAvailableError"]
|
|
@@ -54,6 +54,33 @@ def _log_warn(msg: str) -> None:
|
|
|
54
54
|
print(f"[docx-sdk] WARN: {msg}", file=sys.stderr)
|
|
55
55
|
|
|
56
56
|
|
|
57
|
+
# Characters that, when present, force ``_Cell.text``'s setter to round-
|
|
58
|
+
# trip through the server-side ``doc.markdownToFragment`` op so any
|
|
59
|
+
# inline markdown gets rendered into structural runs. Plain-text values
|
|
60
|
+
# (the common case for tabular data — numbers, labels, names) skip the
|
|
61
|
+
# query entirely and build the fragment locally, saving one HTTP
|
|
62
|
+
# round-trip per cell assignment. The set is conservative on purpose:
|
|
63
|
+
# ``-``, ``.``, ``,``, parentheses, currency symbols, and percent are
|
|
64
|
+
# considered plain so that ``cell.text = "$1,234.56 (-3.4%)"`` doesn't
|
|
65
|
+
# eat a round-trip.
|
|
66
|
+
_MARKDOWN_SPECIAL_CHARS: frozenset[str] = frozenset(
|
|
67
|
+
"*_~`#>[]<|\\"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _is_plain_text(value: str) -> bool:
|
|
72
|
+
"""Return True iff ``value`` contains no markdown control characters.
|
|
73
|
+
|
|
74
|
+
Used by ``_Cell.text`` to decide whether to construct the SuperDoc
|
|
75
|
+
paragraph fragment locally (plain text) or to defer to the
|
|
76
|
+
server-side ``doc.markdownToFragment`` op (anything that might
|
|
77
|
+
contain bold/italic/code/header/list/link/blockquote/html syntax).
|
|
78
|
+
"""
|
|
79
|
+
if not value:
|
|
80
|
+
return True
|
|
81
|
+
return not any(ch in _MARKDOWN_SPECIAL_CHARS for ch in value)
|
|
82
|
+
|
|
83
|
+
|
|
57
84
|
def _find_first_paragraph_id(obj: object) -> str:
|
|
58
85
|
"""Walk a getNodeById result to find the first paragraph nodeId."""
|
|
59
86
|
if isinstance(obj, dict):
|
|
@@ -1010,10 +1037,18 @@ class _Cell:
|
|
|
1010
1037
|
}
|
|
1011
1038
|
# Superdoc only accepts block-typed fragments at the top level
|
|
1012
1039
|
# (paragraph/heading/table/image/list/sectionBreak/sdt/tableOfContents).
|
|
1013
|
-
# We
|
|
1014
|
-
#
|
|
1015
|
-
#
|
|
1016
|
-
#
|
|
1040
|
+
# We need to materialize ``value`` into a
|
|
1041
|
+
# ``{kind:"paragraph", paragraph:{inlines:[...]}}`` fragment.
|
|
1042
|
+
#
|
|
1043
|
+
# As of 0.8.0 we build the fragment locally when ``value`` has no
|
|
1044
|
+
# markdown-special characters — that's the dominant cell-content
|
|
1045
|
+
# case in practice (numeric cells, plain labels) and skipping
|
|
1046
|
+
# the ``doc.markdownToFragment`` query removes one eager HTTP
|
|
1047
|
+
# round-trip per ``cell.text = …``. Combined with
|
|
1048
|
+
# ``Insert`` no longer being response-bearing (0.8.0), a 30-cell
|
|
1049
|
+
# table assignment now ships in a single ``doc.insert`` batch
|
|
1050
|
+
# instead of 60 sequential round-trips. See
|
|
1051
|
+
# ``docx-studio/PERFORMANCE_BATCHING_ANALYSIS.md``.
|
|
1017
1052
|
#
|
|
1018
1053
|
# Confirmed against real staging Superdoc. This is the ONLY shape
|
|
1019
1054
|
# that actually lands text inside a tableCell:
|
|
@@ -1022,17 +1057,8 @@ class _Cell:
|
|
|
1022
1057
|
# - doc.replace + tableCell target → replaces the cell itself,
|
|
1023
1058
|
# destroying the table structure
|
|
1024
1059
|
try:
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
)
|
|
1028
|
-
fragment: object = (
|
|
1029
|
-
frag_result.get("fragment")
|
|
1030
|
-
if isinstance(frag_result, dict)
|
|
1031
|
-
else None
|
|
1032
|
-
)
|
|
1033
|
-
# Fall back to a hand-built fragment with Superdoc's native
|
|
1034
|
-
# shape if markdownToFragment is unavailable.
|
|
1035
|
-
if not isinstance(fragment, dict):
|
|
1060
|
+
fragment: dict
|
|
1061
|
+
if _is_plain_text(value):
|
|
1036
1062
|
fragment = {
|
|
1037
1063
|
"kind": "paragraph",
|
|
1038
1064
|
"paragraph": {
|
|
@@ -1043,6 +1069,29 @@ class _Cell:
|
|
|
1043
1069
|
),
|
|
1044
1070
|
},
|
|
1045
1071
|
}
|
|
1072
|
+
else:
|
|
1073
|
+
frag_result: object = run_sync(
|
|
1074
|
+
session.doc.markdown_to_fragment({"markdown": value}),
|
|
1075
|
+
)
|
|
1076
|
+
resolved: object = (
|
|
1077
|
+
frag_result.get("fragment")
|
|
1078
|
+
if isinstance(frag_result, dict)
|
|
1079
|
+
else None
|
|
1080
|
+
)
|
|
1081
|
+
if isinstance(resolved, dict):
|
|
1082
|
+
fragment = resolved
|
|
1083
|
+
else:
|
|
1084
|
+
# Server didn't return a fragment — fall back to the
|
|
1085
|
+
# hand-built shape so we still land text rather than
|
|
1086
|
+
# raising.
|
|
1087
|
+
fragment = {
|
|
1088
|
+
"kind": "paragraph",
|
|
1089
|
+
"paragraph": {
|
|
1090
|
+
"inlines": [
|
|
1091
|
+
{"kind": "run", "run": {"text": value}},
|
|
1092
|
+
],
|
|
1093
|
+
},
|
|
1094
|
+
}
|
|
1046
1095
|
run_sync(
|
|
1047
1096
|
session.doc.insert(
|
|
1048
1097
|
{
|
|
@@ -1063,14 +1112,36 @@ class _Cell:
|
|
|
1063
1112
|
def paragraphs(self) -> list["Paragraph"]:
|
|
1064
1113
|
from docx.text.paragraph import Paragraph
|
|
1065
1114
|
|
|
1066
|
-
|
|
1067
|
-
|
|
1115
|
+
# Mark each Paragraph proxy as cell-inner so downstream format
|
|
1116
|
+
# ops that hit a SuperDoc ``Block not found`` miss can correlate
|
|
1117
|
+
# back to the well-known cell-paragraph addressing gap (see
|
|
1118
|
+
# ``BlockNotFoundError`` and ``SUPERDOC_UPSTREAM_REQUESTS.md``).
|
|
1119
|
+
# The flag is informational — no setter changes behavior based
|
|
1120
|
+
# on it yet — but it makes the connection inspectable from a
|
|
1121
|
+
# debugger and gives ``_apply_inline`` and friends something
|
|
1122
|
+
# stable to consult when we eventually add a materialization
|
|
1123
|
+
# fallback at the SDK layer.
|
|
1124
|
+
pids = self._inner_paragraph_ids()
|
|
1125
|
+
# Resolve the cell address ONCE and re-use it for every
|
|
1126
|
+
# paragraph; the original draft called ``self._cell_address()``
|
|
1127
|
+
# inside the loop, which hits ``_cell_id`` → ``_cell_info`` →
|
|
1128
|
+
# ``tables.get_cells`` per iteration. A 10-paragraph cell would
|
|
1129
|
+
# have eaten 10 redundant ``tables.get_cells`` round-trips just
|
|
1130
|
+
# to stamp identical metadata — defeats the whole point of the
|
|
1131
|
+
# 0.8.0 round-trip-reduction work. ``addr`` is ``None`` for an
|
|
1132
|
+
# empty cell so we don't issue a no-op address query in that case.
|
|
1133
|
+
addr = self._cell_address() if pids else None
|
|
1134
|
+
out: list[Paragraph] = []
|
|
1135
|
+
for pid in pids:
|
|
1136
|
+
p = Paragraph(
|
|
1068
1137
|
session=self._table._session,
|
|
1069
1138
|
node_id=pid,
|
|
1070
1139
|
node_type="paragraph",
|
|
1071
1140
|
)
|
|
1072
|
-
|
|
1073
|
-
|
|
1141
|
+
p._cell_inner = True # type: ignore[attr-defined]
|
|
1142
|
+
p._cell_address = addr # type: ignore[attr-defined]
|
|
1143
|
+
out.append(p)
|
|
1144
|
+
return out
|
|
1074
1145
|
|
|
1075
1146
|
@property
|
|
1076
1147
|
def tables(self) -> list[Table]:
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "athena-python-docx"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.8.0"
|
|
8
8
|
description = "Drop-in replacement for python-docx that connects to Athena's Superdoc/Keryx collaborative document stack"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -5,26 +5,20 @@
|
|
|
5
5
|
"tables.get",
|
|
6
6
|
"tables.get",
|
|
7
7
|
"tables.getCells",
|
|
8
|
-
"markdownToFragment",
|
|
9
8
|
"insert",
|
|
10
9
|
"tables.get",
|
|
11
10
|
"tables.getCells",
|
|
12
|
-
"markdownToFragment",
|
|
13
11
|
"insert",
|
|
14
12
|
"tables.get",
|
|
15
13
|
"tables.getCells",
|
|
16
|
-
"markdownToFragment",
|
|
17
14
|
"insert",
|
|
18
15
|
"tables.get",
|
|
19
16
|
"tables.getCells",
|
|
20
|
-
"markdownToFragment",
|
|
21
17
|
"insert",
|
|
22
18
|
"tables.get",
|
|
23
19
|
"tables.getCells",
|
|
24
|
-
"markdownToFragment",
|
|
25
20
|
"insert",
|
|
26
21
|
"tables.get",
|
|
27
22
|
"tables.getCells",
|
|
28
|
-
"markdownToFragment",
|
|
29
23
|
"insert"
|
|
30
24
|
]
|
|
@@ -3,31 +3,25 @@
|
|
|
3
3
|
"tables.get",
|
|
4
4
|
"tables.get",
|
|
5
5
|
"tables.getCells",
|
|
6
|
-
"markdownToFragment",
|
|
7
6
|
"insert",
|
|
8
7
|
"tables.get",
|
|
9
8
|
"tables.get",
|
|
10
9
|
"tables.getCells",
|
|
11
|
-
"markdownToFragment",
|
|
12
10
|
"insert",
|
|
13
11
|
"tables.get",
|
|
14
12
|
"tables.get",
|
|
15
13
|
"tables.getCells",
|
|
16
|
-
"markdownToFragment",
|
|
17
14
|
"insert",
|
|
18
15
|
"tables.get",
|
|
19
16
|
"tables.get",
|
|
20
17
|
"tables.getCells",
|
|
21
|
-
"markdownToFragment",
|
|
22
18
|
"insert",
|
|
23
19
|
"tables.get",
|
|
24
20
|
"tables.get",
|
|
25
21
|
"tables.getCells",
|
|
26
|
-
"markdownToFragment",
|
|
27
22
|
"insert",
|
|
28
23
|
"tables.get",
|
|
29
24
|
"tables.get",
|
|
30
25
|
"tables.getCells",
|
|
31
|
-
"markdownToFragment",
|
|
32
26
|
"insert"
|
|
33
27
|
]
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
"tables.get",
|
|
4
4
|
"tables.get",
|
|
5
5
|
"tables.getCells",
|
|
6
|
-
"markdownToFragment",
|
|
7
6
|
"insert",
|
|
8
7
|
"tables.get",
|
|
9
8
|
"tables.getCells",
|
|
@@ -12,5 +11,7 @@
|
|
|
12
11
|
"tables.get",
|
|
13
12
|
"tables.getCells",
|
|
14
13
|
"getNodeById",
|
|
14
|
+
"tables.get",
|
|
15
|
+
"tables.getCells",
|
|
15
16
|
"getNodeById"
|
|
16
17
|
]
|