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.
Files changed (303) hide show
  1. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/PKG-INFO +1 -1
  2. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/__init__.py +1 -1
  3. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/_buffer.py +5 -1
  4. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/_http_doc.py +10 -3
  5. athena_python_docx-0.6.2/docx/_ptc.py +138 -0
  6. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/commands.py +40 -0
  7. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/document.py +187 -0
  8. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/pyproject.toml +1 -1
  9. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/fake_session.py +34 -0
  10. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +2 -0
  11. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +6 -1
  12. athena_python_docx-0.6.2/tests/fidelity/test_e2e_against_staging.py +523 -0
  13. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/reports/GAP_ANALYSIS.md +3 -28
  14. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/reports/gap_report.json +2 -1664
  15. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/snapshots/athena_latest.json +994 -224
  16. athena_python_docx-0.6.2/tests/test_list_styles.py +226 -0
  17. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_ptc.py +55 -3
  18. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_wire_contract.py +16 -1
  19. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/uv.lock +1 -1
  20. athena_python_docx-0.6.1/docx/_ptc.py +0 -173
  21. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/.gitignore +0 -0
  22. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/CLAUDE.md +0 -0
  23. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/README.md +0 -0
  24. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/_batching.py +0 -0
  25. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/_http.py +0 -0
  26. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/_image_utils.py +0 -0
  27. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/api.py +0 -0
  28. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/client.py +0 -0
  29. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/comments.py +0 -0
  30. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/enum/__init__.py +0 -0
  31. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/enum/section.py +0 -0
  32. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/enum/style.py +0 -0
  33. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/enum/table.py +0 -0
  34. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/enum/text.py +0 -0
  35. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/errors.py +0 -0
  36. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/exceptions.py +0 -0
  37. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/opc/__init__.py +0 -0
  38. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/opc/coreprops.py +0 -0
  39. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/revisions.py +0 -0
  40. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/section.py +0 -0
  41. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/settings.py +0 -0
  42. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/shape.py +0 -0
  43. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/shared.py +0 -0
  44. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/styles/__init__.py +0 -0
  45. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/styles/style.py +0 -0
  46. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/styles/styles.py +0 -0
  47. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/table.py +0 -0
  48. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/text/__init__.py +0 -0
  49. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/text/font.py +0 -0
  50. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/text/hyperlink.py +0 -0
  51. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/text/pagebreak.py +0 -0
  52. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/text/paragraph.py +0 -0
  53. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/text/parfmt.py +0 -0
  54. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/text/run.py +0 -0
  55. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/text/tabstops.py +0 -0
  56. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/docx/typing.py +0 -0
  57. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/scripts/publish.sh +0 -0
  58. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/scripts/release.sh +0 -0
  59. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/scripts/round_trip_smoke.py +0 -0
  60. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/__init__.py +0 -0
  61. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/conftest.py +0 -0
  62. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/METHODOLOGY.md +0 -0
  63. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/README.md +0 -0
  64. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/__init__.py +0 -0
  65. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/ab_probe_cases.py +0 -0
  66. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/ab_probe_runner.py +0 -0
  67. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/auto_gen_cases.py +0 -0
  68. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/binary_round_trip.py +0 -0
  69. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/cases.py +0 -0
  70. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/complex_cases.py +0 -0
  71. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/coverage_report.py +0 -0
  72. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/extract.py +0 -0
  73. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/extreme_cases.py +0 -0
  74. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/local_runner.py +0 -0
  75. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/mega_cases.py +0 -0
  76. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshot.py +0 -0
  77. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
  78. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
  79. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
  80. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
  81. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
  82. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
  83. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
  84. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
  85. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
  86. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
  87. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
  88. {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
  89. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/103_cell_tables_enumeration.json +0 -0
  90. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
  91. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
  92. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
  93. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
  94. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
  95. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
  96. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
  97. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
  98. {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
  99. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
  100. {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
  101. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
  102. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
  103. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
  104. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
  105. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
  106. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
  107. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/23_nested_table.json +0 -0
  108. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
  109. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
  110. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
  111. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
  112. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
  113. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
  114. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
  115. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
  116. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
  117. {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
  118. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
  119. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
  120. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
  121. {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
  122. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
  123. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
  124. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
  125. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
  126. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
  127. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
  128. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
  129. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
  130. {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
  131. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
  132. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
  133. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
  134. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
  135. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/51_nested_tables_deep.json +0 -0
  136. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
  137. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
  138. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
  139. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
  140. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
  141. {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
  142. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
  143. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
  144. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
  145. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
  146. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
  147. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
  148. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
  149. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
  150. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
  151. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
  152. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
  153. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
  154. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
  155. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
  156. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
  157. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
  158. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
  159. {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
  160. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
  161. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
  162. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
  163. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
  164. {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
  165. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
  166. {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
  167. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
  168. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
  169. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
  170. {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
  171. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
  172. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
  173. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
  174. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
  175. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
  176. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
  177. {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
  178. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
  179. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
  180. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
  181. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
  182. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
  183. {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
  184. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex01_five_levels_deep_tables.json +0 -0
  185. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
  186. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
  187. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
  188. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
  189. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
  190. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
  191. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
  192. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
  193. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
  194. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
  195. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
  196. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
  197. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
  198. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
  199. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
  200. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
  201. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
  202. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
  203. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
  204. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
  205. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
  206. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
  207. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
  208. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
  209. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
  210. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
  211. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
  212. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
  213. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
  214. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
  215. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
  216. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
  217. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
  218. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
  219. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
  220. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
  221. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
  222. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
  223. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
  224. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
  225. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw14_nested_cell_table.json +0 -0
  226. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
  227. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/ours_spec.json +0 -0
  228. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/parity_crawl.py +0 -0
  229. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/parity_diff.json +0 -0
  230. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/real_world_cases.py +0 -0
  231. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/round_trip_tests.py +0 -0
  232. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/runner.py +0 -0
  233. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/fidelity/stock_spec.json +0 -0
  234. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/README.md +0 -0
  235. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/__init__.py +0 -0
  236. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/baseline_gaps.json +0 -0
  237. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/compare.py +0 -0
  238. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/intentional_deviations.json +0 -0
  239. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/introspect.py +0 -0
  240. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/run_parity.py +0 -0
  241. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
  242. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/parity/test_parity_gap.py +0 -0
  243. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_batching_perf.py +0 -0
  244. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_buffer.py +0 -0
  245. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_collapsed_range_format.py +0 -0
  246. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_command_dataclasses.py +0 -0
  247. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_commands.py +0 -0
  248. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_comments.py +0 -0
  249. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_document_create.py +0 -0
  250. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_http_transport.py +0 -0
  251. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_iter_inner_content.py +0 -0
  252. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_merged_cells.py +0 -0
  253. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_paragraph_text_len_cache.py +0 -0
  254. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_parity_misc.py +0 -0
  255. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_parity_round2.py +0 -0
  256. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_phase_a_behavior.py +0 -0
  257. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_phase_b_headers_footers.py +0 -0
  258. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_phase_c_tables.py +0 -0
  259. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_pr19766_review_fixes.py +0 -0
  260. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_python_docx_api_parity.py +0 -0
  261. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_revisions.py +0 -0
  262. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_add_paragraph_style.py +0 -0
  263. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_add_picture.py +0 -0
  264. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_add_run.py +0 -0
  265. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
  266. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_comments_add_comment.py +0 -0
  267. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_comments_get.py +0 -0
  268. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_document_audit.py +0 -0
  269. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_document_element.py +0 -0
  270. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_enum_section.py +0 -0
  271. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_font_audit.py +0 -0
  272. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_header_footer.py +0 -0
  273. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_hyperlink.py +0 -0
  274. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_inline_shape.py +0 -0
  275. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
  276. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_misc.py +0 -0
  277. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_paragraph_strict.py +0 -0
  278. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_paragraph_style.py +0 -0
  279. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
  280. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_parfmt.py +0 -0
  281. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_row_col_cell.py +0 -0
  282. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_run_add_break.py +0 -0
  283. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_run_bool_setters.py +0 -0
  284. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_run_style.py +0 -0
  285. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_run_style_strict.py +0 -0
  286. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_run_text.py +0 -0
  287. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_run_underline.py +0 -0
  288. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_section_audit.py +0 -0
  289. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_section_dimensions.py +0 -0
  290. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_section_onoff.py +0 -0
  291. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_settings.py +0 -0
  292. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_shared_audit.py +0 -0
  293. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_style.py +0 -0
  294. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_styles.py +0 -0
  295. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_table_audit.py +0 -0
  296. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_table_cell.py +0 -0
  297. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_table_dimensions.py +0 -0
  298. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_silent_stub_table_layout.py +0 -0
  299. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_smoke_integration.py +0 -0
  300. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_style_acceptance.py +0 -0
  301. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_style_font.py +0 -0
  302. {athena_python_docx-0.6.1 → athena_python_docx-0.6.2}/tests/test_style_setters_contract.py +0 -0
  303. {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.1
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>
@@ -6,7 +6,7 @@ See CLAUDE.md for the API parity contract.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- __version__ = "0.6.1"
9
+ __version__ = "0.6.2"
10
10
 
11
11
  from docx.api import Document
12
12
  from docx._buffer import flush_all
@@ -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(type(cmd).__name__, cmd.to_dict()) # type: ignore[attr-defined]
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 (SuperDoc 1.8.1 additions; not exposed on the
507
- # python-docx parity surface registered here for typed-bus
508
- # completeness when advanced callers reach for them).
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.1"
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")
@@ -12,8 +12,10 @@
12
12
  "styles.paragraph.setStyle",
13
13
  "create.paragraph",
14
14
  "styles.paragraph.setStyle",
15
+ "lists.create",
15
16
  "create.paragraph",
16
17
  "styles.paragraph.setStyle",
18
+ "lists.create",
17
19
  "create.table",
18
20
  "tables.get",
19
21
  "tables.get",
@@ -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
  ]