athena-python-docx 0.6.1__tar.gz → 0.6.2__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.6.1 → athena_python_docx-0.6.2}/PKG-INFO +1 -1
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/__init__.py +1 -1
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/_buffer.py +5 -1
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/_http_doc.py +10 -3
- athena_python_docx-0.6.2/docx/_ptc.py +138 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/commands.py +40 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/document.py +187 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/pyproject.toml +1 -1
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/fake_session.py +34 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +2 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +6 -1
- athena_python_docx-0.6.2/tests/fidelity/test_e2e_against_staging.py +523 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/reports/GAP_ANALYSIS.md +3 -28
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/reports/gap_report.json +2 -1664
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/snapshots/athena_latest.json +994 -224
- athena_python_docx-0.6.2/tests/test_list_styles.py +226 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_ptc.py +55 -3
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_wire_contract.py +16 -1
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/uv.lock +1 -1
- athena_python_docx-0.6.1/docx/_ptc.py +0 -173
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/.gitignore +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/CLAUDE.md +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/README.md +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/_batching.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/_http.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/_image_utils.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/api.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/client.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/comments.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/enum/__init__.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/enum/section.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/enum/style.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/enum/table.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/enum/text.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/errors.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/exceptions.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/opc/__init__.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/opc/coreprops.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/revisions.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/section.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/settings.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/shape.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/shared.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/styles/__init__.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/styles/style.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/styles/styles.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/table.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/text/__init__.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/text/font.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/text/hyperlink.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/text/pagebreak.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/text/paragraph.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/text/parfmt.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/text/run.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/text/tabstops.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/typing.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/scripts/publish.sh +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/scripts/release.sh +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/scripts/round_trip_smoke.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/__init__.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/conftest.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/METHODOLOGY.md +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/README.md +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/__init__.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/ab_probe_cases.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/ab_probe_runner.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/auto_gen_cases.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/binary_round_trip.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/cases.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/complex_cases.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/coverage_report.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/extract.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/extreme_cases.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/local_runner.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/mega_cases.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshot.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/103_cell_tables_enumeration.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/23_nested_table.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/51_nested_tables_deep.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex01_five_levels_deep_tables.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw14_nested_cell_table.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/ours_spec.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/parity_crawl.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/parity_diff.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/real_world_cases.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/round_trip_tests.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/runner.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/stock_spec.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/README.md +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/__init__.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/baseline_gaps.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/compare.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/intentional_deviations.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/introspect.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/run_parity.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/test_parity_gap.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_batching_perf.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_buffer.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_collapsed_range_format.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_command_dataclasses.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_commands.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_comments.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_document_create.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_http_transport.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_iter_inner_content.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_merged_cells.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_paragraph_text_len_cache.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_parity_misc.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_parity_round2.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_phase_a_behavior.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_phase_b_headers_footers.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_phase_c_tables.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_pr19766_review_fixes.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_python_docx_api_parity.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_revisions.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_add_paragraph_style.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_add_picture.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_add_run.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_comments_add_comment.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_comments_get.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_document_audit.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_document_element.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_enum_section.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_font_audit.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_header_footer.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_hyperlink.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_inline_shape.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_misc.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_paragraph_strict.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_paragraph_style.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_parfmt.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_row_col_cell.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_run_add_break.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_run_bool_setters.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_run_style.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_run_style_strict.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_run_text.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_run_underline.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_section_audit.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_section_dimensions.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_section_onoff.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_settings.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_shared_audit.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_style.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_styles.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_table_audit.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_table_cell.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_table_dimensions.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_table_layout.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_smoke_integration.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_style_acceptance.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_style_font.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_style_setters_contract.py +0 -0
- {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_zod_wire_contract.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: athena-python-docx
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
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>
|
|
@@ -278,7 +278,11 @@ class CommandBuffer:
|
|
|
278
278
|
# PTC begin: one event per user-facing method call, before any
|
|
279
279
|
# batching. Failures here can't crash the user's code path.
|
|
280
280
|
try:
|
|
281
|
-
cmd._ptc_call_id = _ptc.emit_begin(
|
|
281
|
+
cmd._ptc_call_id = _ptc.emit_begin( # type: ignore[attr-defined]
|
|
282
|
+
type(cmd).__name__,
|
|
283
|
+
cmd.to_dict(),
|
|
284
|
+
asset_id=self._asset_id,
|
|
285
|
+
)
|
|
282
286
|
except Exception: # noqa: BLE001
|
|
283
287
|
pass
|
|
284
288
|
|
|
@@ -68,6 +68,8 @@ from docx.commands import (
|
|
|
68
68
|
Insert,
|
|
69
69
|
InsertLineBreak,
|
|
70
70
|
InsertTab,
|
|
71
|
+
ListsAttach,
|
|
72
|
+
ListsCreate,
|
|
71
73
|
ListsMerge,
|
|
72
74
|
ListsSplit,
|
|
73
75
|
MarkdownToFragment,
|
|
@@ -503,9 +505,14 @@ _OP_TO_COMMAND: dict[str, type[Command]] = {
|
|
|
503
505
|
"header_footers.get": HeaderFootersGet,
|
|
504
506
|
"header_footers.resolve": HeaderFootersResolve,
|
|
505
507
|
"header_footers.refs.set_linked_to_previous": HeaderFootersRefsSetLinkedToPrevious,
|
|
506
|
-
# Lists & Selection
|
|
507
|
-
#
|
|
508
|
-
#
|
|
508
|
+
# Lists & Selection. ``lists.create`` and ``lists.attach`` are
|
|
509
|
+
# consumed internally by ``Document.add_paragraph`` to make
|
|
510
|
+
# ``style="List Bullet" | "List Number"`` produce real list
|
|
511
|
+
# structure (not just a style attribute on a flat paragraph).
|
|
512
|
+
# ``lists.merge``/``lists.split`` stay typed-bus-only — no
|
|
513
|
+
# python-docx parity surface for them.
|
|
514
|
+
"lists.create": ListsCreate,
|
|
515
|
+
"lists.attach": ListsAttach,
|
|
509
516
|
"lists.merge": ListsMerge,
|
|
510
517
|
"lists.split": ListsSplit,
|
|
511
518
|
"selection.current": SelectionCurrent,
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Programmatic Tool Calling (PTC) — client side.
|
|
2
|
+
|
|
3
|
+
Activated only when ``ATHENA_PTC_URL`` is set. The URL is a presigned
|
|
4
|
+
endpoint URL with an HMAC-signed token embedded as ``?t=…``; the token
|
|
5
|
+
carries the (thread, parent_tool_call_id, run) triple and an expiry.
|
|
6
|
+
The SDK doesn't need to know that triple — it just POSTs to the URL
|
|
7
|
+
verbatim, and the server derives identity from the token.
|
|
8
|
+
|
|
9
|
+
Without ``ATHENA_PTC_URL`` set, every call here is a no-op.
|
|
10
|
+
|
|
11
|
+
Emits are fire-and-forget on a background daemon thread:
|
|
12
|
+
|
|
13
|
+
- Never raise into user code.
|
|
14
|
+
- Never block the calling thread.
|
|
15
|
+
- Re-read ``ATHENA_PTC_URL`` on every emit so updates between sandbox
|
|
16
|
+
executions take effect immediately (the SDK module itself is cached
|
|
17
|
+
across runs).
|
|
18
|
+
- Snapshot the URL at ``emit_begin`` time and carry it to ``emit_end``
|
|
19
|
+
so late end events can't be misattributed if a new sandbox run
|
|
20
|
+
swapped in a different URL between begin and end.
|
|
21
|
+
|
|
22
|
+
This module is *intentionally byte-identical* across the docx / pptx /
|
|
23
|
+
xlsx SDKs — the three SDKs have no shared base. Duplicating ~100 LOC
|
|
24
|
+
of stdlib code is cheaper than spinning up a 4th release pipeline.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import json
|
|
30
|
+
import os
|
|
31
|
+
import queue
|
|
32
|
+
import threading
|
|
33
|
+
import time
|
|
34
|
+
import urllib.error
|
|
35
|
+
import urllib.request
|
|
36
|
+
import uuid
|
|
37
|
+
from typing import Any
|
|
38
|
+
|
|
39
|
+
_MAX_QUEUE = 4096
|
|
40
|
+
_MAX_BODY = 64 * 1024
|
|
41
|
+
_HTTP_TIMEOUT = 2.0
|
|
42
|
+
|
|
43
|
+
# Singleton state. PTC has exactly one outbox per process.
|
|
44
|
+
_outbox: queue.Queue[tuple[str, dict[str, Any]]] = queue.Queue(maxsize=_MAX_QUEUE)
|
|
45
|
+
_thread_lock = threading.Lock()
|
|
46
|
+
_thread_started = False
|
|
47
|
+
# call_id -> URL snapshot at begin time; lets emit_end target the
|
|
48
|
+
# original run's endpoint even if ATHENA_PTC_URL changed since.
|
|
49
|
+
_call_url: dict[str, str] = {}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _drain() -> None:
|
|
53
|
+
while True:
|
|
54
|
+
item = _outbox.get()
|
|
55
|
+
if item is None:
|
|
56
|
+
return
|
|
57
|
+
url, body = item
|
|
58
|
+
try:
|
|
59
|
+
raw = json.dumps(body).encode("utf-8")
|
|
60
|
+
if len(raw) > _MAX_BODY:
|
|
61
|
+
key = "args" if body.get("phase") == "begin" else "result"
|
|
62
|
+
body[key] = {"__truncated__": True}
|
|
63
|
+
raw = json.dumps(body).encode("utf-8")
|
|
64
|
+
req = urllib.request.Request(
|
|
65
|
+
url,
|
|
66
|
+
data=raw,
|
|
67
|
+
method="POST",
|
|
68
|
+
headers={"Content-Type": "application/json"},
|
|
69
|
+
)
|
|
70
|
+
urllib.request.urlopen(req, timeout=_HTTP_TIMEOUT).close()
|
|
71
|
+
except (urllib.error.URLError, OSError, ValueError):
|
|
72
|
+
pass # never propagate
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _ensure_thread() -> None:
|
|
76
|
+
global _thread_started
|
|
77
|
+
if _thread_started:
|
|
78
|
+
return
|
|
79
|
+
with _thread_lock:
|
|
80
|
+
if _thread_started:
|
|
81
|
+
return
|
|
82
|
+
threading.Thread(target=_drain, name="athena-ptc", daemon=True).start()
|
|
83
|
+
_thread_started = True
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _send(url: str, body: dict[str, Any]) -> None:
|
|
87
|
+
_ensure_thread()
|
|
88
|
+
try:
|
|
89
|
+
_outbox.put_nowait((url, body))
|
|
90
|
+
except queue.Full:
|
|
91
|
+
pass # drop on backpressure; never block user code
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def emit_begin(
|
|
95
|
+
tool_name: str,
|
|
96
|
+
args: dict[str, Any],
|
|
97
|
+
*,
|
|
98
|
+
asset_id: str | None = None,
|
|
99
|
+
) -> str:
|
|
100
|
+
call_id = uuid.uuid4().hex
|
|
101
|
+
url = os.environ.get("ATHENA_PTC_URL")
|
|
102
|
+
if not url:
|
|
103
|
+
return call_id
|
|
104
|
+
_call_url[call_id] = url
|
|
105
|
+
body: dict[str, Any] = {
|
|
106
|
+
"callId": call_id,
|
|
107
|
+
"toolName": tool_name,
|
|
108
|
+
"phase": "begin",
|
|
109
|
+
"args": args,
|
|
110
|
+
"ts": time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime()),
|
|
111
|
+
}
|
|
112
|
+
if asset_id is not None:
|
|
113
|
+
body["assetId"] = asset_id
|
|
114
|
+
_send(url, body)
|
|
115
|
+
return call_id
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def emit_end(
|
|
119
|
+
*,
|
|
120
|
+
call_id: str,
|
|
121
|
+
tool_name: str,
|
|
122
|
+
result: dict[str, Any] | None,
|
|
123
|
+
is_error: bool,
|
|
124
|
+
) -> None:
|
|
125
|
+
url = _call_url.pop(call_id, None)
|
|
126
|
+
if url is None:
|
|
127
|
+
return # PTC was disabled at begin, or begin wasn't emitted
|
|
128
|
+
_send(
|
|
129
|
+
url,
|
|
130
|
+
{
|
|
131
|
+
"callId": call_id,
|
|
132
|
+
"toolName": tool_name,
|
|
133
|
+
"phase": "end",
|
|
134
|
+
"result": result,
|
|
135
|
+
"isError": is_error,
|
|
136
|
+
"ts": time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime()),
|
|
137
|
+
},
|
|
138
|
+
)
|
|
@@ -619,6 +619,40 @@ class HeaderFootersRefsSetLinkedToPrevious(Command):
|
|
|
619
619
|
# every other op.
|
|
620
620
|
|
|
621
621
|
|
|
622
|
+
@dataclass
|
|
623
|
+
class ListsCreate(Command):
|
|
624
|
+
"""Create a new list — either an empty list at an anchor, or by
|
|
625
|
+
converting an existing paragraph (range) into a list.
|
|
626
|
+
|
|
627
|
+
SuperDoc: ``doc.lists.create``.
|
|
628
|
+
|
|
629
|
+
- ``mode="empty"`` requires ``at`` (BlockAddress).
|
|
630
|
+
- ``mode="fromParagraphs"`` requires ``target`` (BlockAddressOrRange).
|
|
631
|
+
- ``kind`` is ``"bullet"`` or ``"ordered"``.
|
|
632
|
+
"""
|
|
633
|
+
|
|
634
|
+
mode: str = "fromParagraphs"
|
|
635
|
+
at: dict[str, Any] | None = None
|
|
636
|
+
target: dict[str, Any] | None = None
|
|
637
|
+
kind: str | None = None
|
|
638
|
+
level: int | None = None
|
|
639
|
+
preset: str | None = None
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
@dataclass
|
|
643
|
+
class ListsAttach(Command):
|
|
644
|
+
"""Attach an existing paragraph to an existing list as a new item.
|
|
645
|
+
|
|
646
|
+
SuperDoc: ``doc.lists.attach``. ``attach_to`` is the list item to
|
|
647
|
+
attach next to (defines list membership); ``target`` is the
|
|
648
|
+
paragraph being converted.
|
|
649
|
+
"""
|
|
650
|
+
|
|
651
|
+
attach_to: dict[str, Any] = dataclasses.field(default_factory=dict)
|
|
652
|
+
target: dict[str, Any] = dataclasses.field(default_factory=dict)
|
|
653
|
+
level: int | None = None
|
|
654
|
+
|
|
655
|
+
|
|
622
656
|
@dataclass
|
|
623
657
|
class ListsMerge(Command):
|
|
624
658
|
"""Merge a list item with an adjacent list / list item.
|
|
@@ -768,6 +802,12 @@ _RESPONSE_BEARING_TYPES: frozenset[str] = frozenset(
|
|
|
768
802
|
# describing the entities affected by accept/reject — callers
|
|
769
803
|
# need that synchronously to invalidate their Revision proxies.
|
|
770
804
|
"TrackChangesDecide",
|
|
805
|
+
# ListsCreate / ListsAttach return {item: {nodeId: <listItemId>}}.
|
|
806
|
+
# Document.add_paragraph reads listItemId to (1) update the
|
|
807
|
+
# Paragraph proxy's node_id so subsequent ops target the
|
|
808
|
+
# listItem, and (2) chain the next list item via lists.attach.
|
|
809
|
+
"ListsCreate",
|
|
810
|
+
"ListsAttach",
|
|
771
811
|
}
|
|
772
812
|
)
|
|
773
813
|
|
|
@@ -79,6 +79,49 @@ class _InlineShapeIdAdapter:
|
|
|
79
79
|
)
|
|
80
80
|
|
|
81
81
|
|
|
82
|
+
def _classify_list_style(style_id: str | None) -> tuple[str, int] | None:
|
|
83
|
+
"""Map a python-docx list-style id to a SuperDoc (kind, level) pair.
|
|
84
|
+
|
|
85
|
+
Returns ``None`` for non-list styles. Recognized styles:
|
|
86
|
+
|
|
87
|
+
- ``"List Bullet"`` → ``("bullet", 0)``
|
|
88
|
+
- ``"List Bullet 2"`` → ``("bullet", 1)`` (... up to ``5`` → level 4)
|
|
89
|
+
- ``"List Number"`` → ``("ordered", 0)``
|
|
90
|
+
- ``"List Number 2"`` → ``("ordered", 1)`` (... up to ``5`` → level 4)
|
|
91
|
+
|
|
92
|
+
Accepts both display-name (``"List Bullet 2"``) and style-id
|
|
93
|
+
(``"ListBullet2"``) forms. ``"List Paragraph"`` and ``"List Continue"``
|
|
94
|
+
are intentionally NOT classified as bullet/number lists — those styles
|
|
95
|
+
indicate "indented body text", not a numbered/bulleted list, and Word
|
|
96
|
+
renders them without a marker. Callers wanting those effects should
|
|
97
|
+
use the style as-is via the generic ``setStyle`` path.
|
|
98
|
+
"""
|
|
99
|
+
if not isinstance(style_id, str) or not style_id:
|
|
100
|
+
return None
|
|
101
|
+
# Normalize: lower-case, strip spaces.
|
|
102
|
+
norm = style_id.lower().replace(" ", "")
|
|
103
|
+
if norm.startswith("listbullet"):
|
|
104
|
+
suffix = norm[len("listbullet"):]
|
|
105
|
+
kind = "bullet"
|
|
106
|
+
elif norm.startswith("listnumber"):
|
|
107
|
+
suffix = norm[len("listnumber"):]
|
|
108
|
+
kind = "ordered"
|
|
109
|
+
else:
|
|
110
|
+
return None
|
|
111
|
+
if suffix == "":
|
|
112
|
+
level = 0
|
|
113
|
+
else:
|
|
114
|
+
try:
|
|
115
|
+
n = int(suffix)
|
|
116
|
+
except ValueError:
|
|
117
|
+
return None
|
|
118
|
+
if n < 1 or n > 9:
|
|
119
|
+
return None
|
|
120
|
+
# "List Bullet 2" → level 1 (zero-indexed depth).
|
|
121
|
+
level = n - 1
|
|
122
|
+
return (kind, level)
|
|
123
|
+
|
|
124
|
+
|
|
82
125
|
class IgnoredSaveTargetWarning(UserWarning):
|
|
83
126
|
"""Emitted when ``Document.save(path_or_stream=...)`` is called with a
|
|
84
127
|
non-None argument.
|
|
@@ -123,6 +166,13 @@ class Document:
|
|
|
123
166
|
self._pending_user_name: str | None = user_name
|
|
124
167
|
self._pending_user_email: str | None = user_email
|
|
125
168
|
self._pending_track_revisions: bool = bool(track_revisions)
|
|
169
|
+
# List-chain state. When the most-recent ``add_paragraph`` call
|
|
170
|
+
# produced a list item, subsequent ``add_paragraph(..., style="List
|
|
171
|
+
# Bullet"|"List Number")`` calls attach to it (lists.attach)
|
|
172
|
+
# instead of starting a new list (lists.create). Any non-list
|
|
173
|
+
# ``add_paragraph`` / ``add_heading`` / ``add_table`` resets this.
|
|
174
|
+
self._last_list_item_id: str | None = None
|
|
175
|
+
self._last_list_kind: str | None = None
|
|
126
176
|
|
|
127
177
|
@classmethod
|
|
128
178
|
def create(
|
|
@@ -689,8 +739,142 @@ class Document:
|
|
|
689
739
|
# or transport error otherwise leaves the user with a
|
|
690
740
|
# paragraph that quietly missed the style they asked for.
|
|
691
741
|
para.style = style_str
|
|
742
|
+
|
|
743
|
+
# python-docx parity: ``add_paragraph(text, style="List Bullet")``
|
|
744
|
+
# is the canonical way to author a bulleted/numbered list (the
|
|
745
|
+
# upstream library exposes no separate list primitive — the style
|
|
746
|
+
# is the list). On Word/OOXML the style's ``<w:numPr/>`` makes
|
|
747
|
+
# bullets appear; on SuperDoc/ProseMirror the style attribute
|
|
748
|
+
# alone does NOT, because lists are a distinct node type
|
|
749
|
+
# (``bulletList > listItem > paragraph``) rather than a flat
|
|
750
|
+
# paragraph with a style.
|
|
751
|
+
#
|
|
752
|
+
# To make the python-docx contract render correctly we follow
|
|
753
|
+
# up the paragraph create + setStyle with SuperDoc's
|
|
754
|
+
# ``doc.lists.create`` / ``doc.lists.attach`` so the paragraph
|
|
755
|
+
# is structurally converted into a list item under a real
|
|
756
|
+
# bulletList / orderedList node. Consecutive list-styled
|
|
757
|
+
# ``add_paragraph`` calls (of the same kind) chain into a
|
|
758
|
+
# single list via ``lists.attach``; the first one starts a new
|
|
759
|
+
# list via ``lists.create(mode="fromParagraphs")``.
|
|
760
|
+
list_info = _classify_list_style(style_str)
|
|
761
|
+
if list_info is not None:
|
|
762
|
+
kind, level = list_info
|
|
763
|
+
self._convert_paragraph_to_list_item(
|
|
764
|
+
paragraph_node_id=node_id,
|
|
765
|
+
kind=kind,
|
|
766
|
+
level=level,
|
|
767
|
+
)
|
|
768
|
+
else:
|
|
769
|
+
# Any non-list paragraph breaks the chain — the next list
|
|
770
|
+
# item will start a fresh list.
|
|
771
|
+
self._last_list_item_id = None
|
|
772
|
+
self._last_list_kind = None
|
|
692
773
|
return para
|
|
693
774
|
|
|
775
|
+
def _convert_paragraph_to_list_item(
|
|
776
|
+
self,
|
|
777
|
+
*,
|
|
778
|
+
paragraph_node_id: str,
|
|
779
|
+
kind: str,
|
|
780
|
+
level: int,
|
|
781
|
+
) -> None:
|
|
782
|
+
"""Promote a flat paragraph into a list item.
|
|
783
|
+
|
|
784
|
+
Chains with the previous list (same ``kind``) via
|
|
785
|
+
``lists.attach``; otherwise starts a new list via
|
|
786
|
+
``lists.create(mode="fromParagraphs")``. Updates the
|
|
787
|
+
``_last_list_item_id`` / ``_last_list_kind`` chain state so the
|
|
788
|
+
next list-styled ``add_paragraph`` call attaches to this item.
|
|
789
|
+
|
|
790
|
+
Failures don't raise — the paragraph + setStyle already
|
|
791
|
+
landed, and surfacing a structural-promotion error here would
|
|
792
|
+
roll back work the caller can't undo. We emit a console warning
|
|
793
|
+
and reset the chain so subsequent items start fresh rather than
|
|
794
|
+
attaching to an orphaned id.
|
|
795
|
+
"""
|
|
796
|
+
para_target: dict = {
|
|
797
|
+
"kind": "block",
|
|
798
|
+
"nodeType": "paragraph",
|
|
799
|
+
"nodeId": paragraph_node_id,
|
|
800
|
+
}
|
|
801
|
+
try:
|
|
802
|
+
if (
|
|
803
|
+
self._last_list_item_id is not None
|
|
804
|
+
and self._last_list_kind == kind
|
|
805
|
+
):
|
|
806
|
+
result = run_sync(
|
|
807
|
+
self._session.doc.lists.attach(
|
|
808
|
+
{
|
|
809
|
+
"attach_to": {
|
|
810
|
+
"kind": "block",
|
|
811
|
+
"nodeType": "listItem",
|
|
812
|
+
"nodeId": self._last_list_item_id,
|
|
813
|
+
},
|
|
814
|
+
"target": para_target,
|
|
815
|
+
"level": level,
|
|
816
|
+
},
|
|
817
|
+
),
|
|
818
|
+
)
|
|
819
|
+
else:
|
|
820
|
+
result = run_sync(
|
|
821
|
+
self._session.doc.lists.create(
|
|
822
|
+
{
|
|
823
|
+
"mode": "fromParagraphs",
|
|
824
|
+
"target": para_target,
|
|
825
|
+
"kind": kind,
|
|
826
|
+
"level": level,
|
|
827
|
+
},
|
|
828
|
+
),
|
|
829
|
+
)
|
|
830
|
+
except Exception as e: # noqa: BLE001
|
|
831
|
+
_log_warn(
|
|
832
|
+
f"list-item promotion failed for paragraph "
|
|
833
|
+
f"{paragraph_node_id!r} ({kind}, level {level}): {e}; "
|
|
834
|
+
f"paragraph remains flat with the style attribute set.",
|
|
835
|
+
)
|
|
836
|
+
self._last_list_item_id = None
|
|
837
|
+
self._last_list_kind = None
|
|
838
|
+
return
|
|
839
|
+
|
|
840
|
+
item_id = self._extract_list_item_id(result)
|
|
841
|
+
if item_id:
|
|
842
|
+
self._last_list_item_id = item_id
|
|
843
|
+
self._last_list_kind = kind
|
|
844
|
+
else:
|
|
845
|
+
# Couldn't read back the listItem id — break the chain so the
|
|
846
|
+
# next call doesn't attach to a stale or missing target.
|
|
847
|
+
_log_warn(
|
|
848
|
+
f"list op returned no item.nodeId for paragraph "
|
|
849
|
+
f"{paragraph_node_id!r}; list chain reset. Response: "
|
|
850
|
+
f"{result!r}",
|
|
851
|
+
)
|
|
852
|
+
self._last_list_item_id = None
|
|
853
|
+
self._last_list_kind = None
|
|
854
|
+
|
|
855
|
+
def _reset_list_chain(self) -> None:
|
|
856
|
+
"""Break the consecutive-list-items chain. Called by any block
|
|
857
|
+
insertion that isn't a list-styled paragraph (heading, table,
|
|
858
|
+
picture, page break, section break) so the next list item
|
|
859
|
+
starts a fresh list rather than attaching to a stale predecessor.
|
|
860
|
+
"""
|
|
861
|
+
self._last_list_item_id = None
|
|
862
|
+
self._last_list_kind = None
|
|
863
|
+
|
|
864
|
+
@staticmethod
|
|
865
|
+
def _extract_list_item_id(result: object) -> str | None:
|
|
866
|
+
"""Pull ``item.nodeId`` out of a ``lists.create`` / ``lists.attach``
|
|
867
|
+
SuperDoc response. Returns ``None`` if the shape is unexpected."""
|
|
868
|
+
if not isinstance(result, dict):
|
|
869
|
+
return None
|
|
870
|
+
item = result.get("item")
|
|
871
|
+
if not isinstance(item, dict):
|
|
872
|
+
return None
|
|
873
|
+
node_id = item.get("nodeId") or item.get("node_id")
|
|
874
|
+
if isinstance(node_id, str) and node_id:
|
|
875
|
+
return node_id
|
|
876
|
+
return None
|
|
877
|
+
|
|
694
878
|
def add_heading(
|
|
695
879
|
self,
|
|
696
880
|
text: str = "",
|
|
@@ -700,6 +884,7 @@ class Document:
|
|
|
700
884
|
from docx.text.paragraph import Paragraph
|
|
701
885
|
|
|
702
886
|
self._ensure_open()
|
|
887
|
+
self._reset_list_chain()
|
|
703
888
|
if not 0 <= level <= 9:
|
|
704
889
|
raise ValidationError(
|
|
705
890
|
f"level must be in 0..9; got {level}",
|
|
@@ -796,6 +981,7 @@ class Document:
|
|
|
796
981
|
from docx.table import Table
|
|
797
982
|
|
|
798
983
|
self._ensure_open()
|
|
984
|
+
self._reset_list_chain()
|
|
799
985
|
if rows < 1 or cols < 1:
|
|
800
986
|
raise ValidationError(
|
|
801
987
|
f"rows and cols must be >= 1; got rows={rows} cols={cols}",
|
|
@@ -883,6 +1069,7 @@ class Document:
|
|
|
883
1069
|
from docx.text.run import _build_inline_shape_info
|
|
884
1070
|
|
|
885
1071
|
self._ensure_open()
|
|
1072
|
+
self._reset_list_chain()
|
|
886
1073
|
|
|
887
1074
|
image_bytes: bytes
|
|
888
1075
|
fallback_path: str = ""
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "athena-python-docx"
|
|
7
|
-
version = "0.6.
|
|
7
|
+
version = "0.6.2"
|
|
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"
|
|
@@ -686,6 +686,40 @@ class FakeDocState:
|
|
|
686
686
|
if op.startswith("styles."):
|
|
687
687
|
return {"ok": True}
|
|
688
688
|
|
|
689
|
+
# ---- lists ----
|
|
690
|
+
# lists.create / lists.attach return ``{success, item: {nodeId, ...}}``.
|
|
691
|
+
# The fake doesn't model the ProseMirror list tree faithfully — it
|
|
692
|
+
# only mints a deterministic listItem id keyed off the paragraph id
|
|
693
|
+
# so consecutive ``add_paragraph(style="List Bullet")`` calls in
|
|
694
|
+
# ``Document._convert_paragraph_to_list_item`` produce the chained
|
|
695
|
+
# ``lists.create → lists.attach → lists.attach`` op sequence that
|
|
696
|
+
# the op-snapshot tests pin.
|
|
697
|
+
if op == "lists.create":
|
|
698
|
+
target = params.get("target") or {}
|
|
699
|
+
pid = str(target.get("nodeId") or "")
|
|
700
|
+
return {
|
|
701
|
+
"success": True,
|
|
702
|
+
"listId": f"list-{pid}",
|
|
703
|
+
"item": {
|
|
704
|
+
"kind": "block",
|
|
705
|
+
"nodeType": "listItem",
|
|
706
|
+
"nodeId": f"li-{pid}",
|
|
707
|
+
},
|
|
708
|
+
}
|
|
709
|
+
if op == "lists.attach":
|
|
710
|
+
target = params.get("target") or {}
|
|
711
|
+
pid = str(target.get("nodeId") or "")
|
|
712
|
+
return {
|
|
713
|
+
"success": True,
|
|
714
|
+
"item": {
|
|
715
|
+
"kind": "block",
|
|
716
|
+
"nodeType": "listItem",
|
|
717
|
+
"nodeId": f"li-{pid}",
|
|
718
|
+
},
|
|
719
|
+
}
|
|
720
|
+
if op.startswith("lists."):
|
|
721
|
+
return {"ok": True}
|
|
722
|
+
|
|
689
723
|
# ---- tables ----
|
|
690
724
|
if op == "tables.get":
|
|
691
725
|
tid = params.get("nodeId")
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
[
|
|
2
2
|
"create.paragraph",
|
|
3
3
|
"styles.paragraph.setStyle",
|
|
4
|
+
"lists.create",
|
|
4
5
|
"create.paragraph",
|
|
5
6
|
"styles.paragraph.setStyle",
|
|
7
|
+
"lists.attach",
|
|
6
8
|
"create.paragraph",
|
|
7
9
|
"styles.paragraph.setStyle",
|
|
10
|
+
"lists.attach",
|
|
8
11
|
"create.paragraph",
|
|
9
12
|
"styles.paragraph.setStyle",
|
|
13
|
+
"lists.attach",
|
|
10
14
|
"create.paragraph",
|
|
11
|
-
"styles.paragraph.setStyle"
|
|
15
|
+
"styles.paragraph.setStyle",
|
|
16
|
+
"lists.attach"
|
|
12
17
|
]
|