athena-python-docx 0.21.8__tar.gz → 0.23.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (373) hide show
  1. athena_python_docx-0.21.8/CLAUDE.md → athena_python_docx-0.23.0/AGENTS.md +50 -3
  2. athena_python_docx-0.23.0/CLAUDE.md +5 -0
  3. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/DOCX_EXEC_LAB.md +1 -1
  4. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/PKG-INFO +2 -2
  5. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/README.md +1 -1
  6. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/__init__.py +1 -1
  7. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/_buffer.py +2 -2
  8. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/_citations.py +122 -1
  9. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/_http.py +1 -1
  10. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/_http_doc.py +2 -0
  11. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/_postproc.py +21 -0
  12. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/_ptc.py +74 -8
  13. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/_timeouts.py +3 -0
  14. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/client.py +5 -1
  15. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/commands.py +23 -2
  16. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/document.py +39 -70
  17. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/table.py +78 -1
  18. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/text/paragraph.py +20 -10
  19. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/text/run.py +3 -0
  20. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/pyproject.toml +1 -1
  21. athena_python_docx-0.23.0/scripts/release.sh +14 -0
  22. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/README.md +1 -1
  23. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/__init__.py +1 -1
  24. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/ab_probe_cases.py +1 -1
  25. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/binary_round_trip.py +1 -1
  26. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/cases.py +1 -1
  27. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/fake_session.py +1 -1
  28. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/local_runner.py +1 -1
  29. athena_python_docx-0.23.0/tests/test_athena_extension_creates_session_open.py +64 -0
  30. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_athena_extensions_registry.py +2 -0
  31. athena_python_docx-0.23.0/tests/test_citations.py +620 -0
  32. athena_python_docx-0.23.0/tests/test_document_clear.py +277 -0
  33. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_execution_scope.py +1 -1
  34. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_ptc.py +105 -1
  35. athena_python_docx-0.23.0/tests/test_run_mark_inheritance.py +88 -0
  36. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_wire_contract.py +1 -0
  37. athena_python_docx-0.21.8/scripts/publish.sh +0 -37
  38. athena_python_docx-0.21.8/scripts/release.sh +0 -84
  39. athena_python_docx-0.21.8/tests/test_citations.py +0 -317
  40. athena_python_docx-0.21.8/tests/test_document_clear.py +0 -132
  41. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/.gitignore +0 -0
  42. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/_athena_extension.py +0 -0
  43. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/_batching.py +0 -0
  44. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/_execution.py +0 -0
  45. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/_image_utils.py +0 -0
  46. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/_references.py +0 -0
  47. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/_table_styles.py +0 -0
  48. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/api.py +0 -0
  49. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/bookmarks.py +0 -0
  50. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/charts.py +0 -0
  51. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/comments.py +0 -0
  52. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/enum/__init__.py +0 -0
  53. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/enum/section.py +0 -0
  54. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/enum/shape.py +0 -0
  55. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/enum/style.py +0 -0
  56. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/enum/table.py +0 -0
  57. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/enum/text.py +0 -0
  58. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/errors.py +0 -0
  59. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/exceptions.py +0 -0
  60. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/fields.py +0 -0
  61. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/footnotes.py +0 -0
  62. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/math.py +0 -0
  63. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/opc/__init__.py +0 -0
  64. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/opc/coreprops.py +0 -0
  65. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/oxml/__init__.py +0 -0
  66. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/revisions.py +0 -0
  67. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/sdt.py +0 -0
  68. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/section.py +0 -0
  69. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/session.py +0 -0
  70. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/settings.py +0 -0
  71. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/shape.py +0 -0
  72. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/shared.py +0 -0
  73. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/styles/__init__.py +0 -0
  74. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/styles/style.py +0 -0
  75. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/styles/styles.py +0 -0
  76. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/text/__init__.py +0 -0
  77. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/text/font.py +0 -0
  78. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/text/hyperlink.py +0 -0
  79. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/text/pagebreak.py +0 -0
  80. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/text/parfmt.py +0 -0
  81. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/text/tabstops.py +0 -0
  82. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/toc.py +0 -0
  83. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/docx/typing.py +0 -0
  84. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/scripts/docx_exec_lab.py +0 -0
  85. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
  86. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
  87. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/scripts/docx_exec_lab_server.py +0 -0
  88. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/scripts/dump_wire_fixtures.py +0 -0
  89. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/scripts/round_trip_smoke.py +0 -0
  90. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/scripts/smoke_test_block_not_found.py +0 -0
  91. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/scripts/validate_find_replace_asset.py +0 -0
  92. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/__init__.py +0 -0
  93. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/conftest.py +0 -0
  94. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/METHODOLOGY.md +0 -0
  95. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/ab_probe_runner.py +0 -0
  96. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/auto_gen_cases.py +0 -0
  97. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/complex_cases.py +0 -0
  98. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/coverage_report.py +0 -0
  99. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/extract.py +0 -0
  100. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/extreme_cases.py +0 -0
  101. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/firm_templates/README.md +0 -0
  102. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/firm_templates/__init__.py +0 -0
  103. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/firm_templates/_runner.py +0 -0
  104. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/firm_templates/extractor.py +0 -0
  105. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
  106. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
  107. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/mega_cases.py +0 -0
  108. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshot.py +0 -0
  109. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
  110. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
  111. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
  112. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
  113. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
  114. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
  115. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
  116. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
  117. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
  118. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
  119. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
  120. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
  121. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
  122. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
  123. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
  124. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
  125. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
  126. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
  127. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
  128. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
  129. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
  130. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
  131. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
  132. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
  133. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
  134. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
  135. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
  136. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
  137. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
  138. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
  139. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
  140. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
  141. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
  142. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
  143. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
  144. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
  145. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
  146. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
  147. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
  148. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
  149. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
  150. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
  151. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
  152. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
  153. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
  154. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
  155. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
  156. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
  157. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
  158. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
  159. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
  160. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
  161. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
  162. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
  163. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
  164. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
  165. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
  166. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
  167. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
  168. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
  169. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
  170. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
  171. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
  172. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
  173. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
  174. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
  175. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
  176. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
  177. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
  178. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
  179. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
  180. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
  181. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
  182. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
  183. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
  184. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
  185. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
  186. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
  187. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
  188. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
  189. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
  190. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
  191. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
  192. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
  193. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
  194. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
  195. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
  196. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
  197. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
  198. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
  199. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
  200. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
  201. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
  202. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
  203. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
  204. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
  205. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
  206. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
  207. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
  208. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
  209. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
  210. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
  211. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
  212. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
  213. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
  214. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
  215. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
  216. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
  217. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
  218. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
  219. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
  220. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
  221. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
  222. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
  223. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
  224. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
  225. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
  226. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
  227. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
  228. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
  229. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
  230. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
  231. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
  232. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
  233. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
  234. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
  235. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
  236. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
  237. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
  238. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
  239. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
  240. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
  241. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
  242. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
  243. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
  244. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
  245. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
  246. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
  247. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
  248. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
  249. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
  250. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
  251. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
  252. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
  253. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
  254. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
  255. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
  256. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/ours_spec.json +0 -0
  257. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/parity_crawl.py +0 -0
  258. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/parity_diff.json +0 -0
  259. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/real_world_cases.py +0 -0
  260. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/round_trip_tests.py +0 -0
  261. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/stock_spec.json +0 -0
  262. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
  263. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/parity/README.md +0 -0
  264. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/parity/__init__.py +0 -0
  265. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/parity/baseline_gaps.json +0 -0
  266. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/parity/compare.py +0 -0
  267. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/parity/intentional_deviations.json +0 -0
  268. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/parity/introspect.py +0 -0
  269. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
  270. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/parity/reports/gap_report.json +0 -0
  271. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/parity/run_parity.py +0 -0
  272. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/parity/snapshots/athena_latest.json +0 -0
  273. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
  274. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/parity/test_parity_gap.py +0 -0
  275. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_add_section_extract_items.py +0 -0
  276. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_athena_extensions_contract.py +0 -0
  277. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_batching_perf.py +0 -0
  278. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_block_not_found_error.py +0 -0
  279. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_buffer.py +0 -0
  280. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
  281. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_cell_add_table_not_supported.py +0 -0
  282. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_cell_fastpath_runs.py +0 -0
  283. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
  284. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
  285. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_cell_inner_format_stash.py +0 -0
  286. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_cell_inner_run_format_stash.py +0 -0
  287. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_cell_inner_run_guard.py +0 -0
  288. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_cell_text_plain_fastpath.py +0 -0
  289. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_cell_text_replace_semantics.py +0 -0
  290. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_collapsed_range_format.py +0 -0
  291. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_command_dataclasses.py +0 -0
  292. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_commands.py +0 -0
  293. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_comments.py +0 -0
  294. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_document_asset_id_property.py +0 -0
  295. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_document_create.py +0 -0
  296. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_document_create_from_template.py +0 -0
  297. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_document_factory_validation.py +0 -0
  298. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_docx_exec_lab.py +0 -0
  299. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_docx_exec_lab_server.py +0 -0
  300. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
  301. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_find_replace_session_open.py +0 -0
  302. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_http_transport.py +0 -0
  303. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_hyperlink_coalescing.py +0 -0
  304. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_image_url_data_uri.py +0 -0
  305. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_insert_deferred.py +0 -0
  306. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_iter_inner_content.py +0 -0
  307. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_list_styles.py +0 -0
  308. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_merged_cell_secondary_slot.py +0 -0
  309. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_merged_cells.py +0 -0
  310. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_oxml_shim.py +0 -0
  311. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_paragraph_text_len_cache.py +0 -0
  312. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_parity_misc.py +0 -0
  313. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_parity_round2.py +0 -0
  314. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_partial_failure_cascade.py +0 -0
  315. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_phase_a_behavior.py +0 -0
  316. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_phase_b_headers_footers.py +0 -0
  317. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_phase_c_tables.py +0 -0
  318. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_positional_cell_id.py +0 -0
  319. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
  320. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
  321. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_postproc_ref_restore.py +0 -0
  322. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_pr19766_review_fixes.py +0 -0
  323. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_python_docx_api_parity.py +0 -0
  324. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_revisions.py +0 -0
  325. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_run_add_picture_anchor.py +0 -0
  326. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
  327. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_add_picture.py +0 -0
  328. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_add_run.py +0 -0
  329. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
  330. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
  331. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_comments_get.py +0 -0
  332. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_document_audit.py +0 -0
  333. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_document_element.py +0 -0
  334. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_enum_section.py +0 -0
  335. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_font_audit.py +0 -0
  336. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_header_footer.py +0 -0
  337. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_hyperlink.py +0 -0
  338. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_inline_shape.py +0 -0
  339. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
  340. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_misc.py +0 -0
  341. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
  342. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_paragraph_style.py +0 -0
  343. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
  344. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_parfmt.py +0 -0
  345. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_row_col_cell.py +0 -0
  346. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_run_add_break.py +0 -0
  347. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
  348. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_run_style.py +0 -0
  349. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_run_style_strict.py +0 -0
  350. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_run_text.py +0 -0
  351. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_run_underline.py +0 -0
  352. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_section_audit.py +0 -0
  353. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_section_dimensions.py +0 -0
  354. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_section_onoff.py +0 -0
  355. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_settings.py +0 -0
  356. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_shared_audit.py +0 -0
  357. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_style.py +0 -0
  358. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_styles.py +0 -0
  359. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_table_audit.py +0 -0
  360. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_table_cell.py +0 -0
  361. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_table_dimensions.py +0 -0
  362. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_silent_stub_table_layout.py +0 -0
  363. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_smoke_integration.py +0 -0
  364. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_style_acceptance.py +0 -0
  365. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_style_font.py +0 -0
  366. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_style_setters_contract.py +0 -0
  367. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_table_set_cell_perf.py +0 -0
  368. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_table_style_id_resolution.py +0 -0
  369. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_temporarily_unavailable.py +0 -0
  370. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_validate_find_replace_asset_script.py +0 -0
  371. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_xml_attr_guard.py +0 -0
  372. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/tests/test_zod_wire_contract.py +0 -0
  373. {athena_python_docx-0.21.8 → athena_python_docx-0.23.0}/uv.lock +0 -0
@@ -367,8 +367,8 @@ Issue numbers reference `python-openxml/python-docx`.
367
367
  list metadata. Routes through `NumberingGet`.
368
368
 
369
369
  - **`Document.to_pdf(*, quality=None, destination=None,
370
- include_revisions=False) -> bytes`** — render to PDF via the
371
- Daytona-hosted LibreOffice. python-docx issue #113 was closed as
370
+ include_revisions=False) -> bytes`** — render to PDF. python-docx
371
+ issue #113 was closed as
372
372
  wontfix upstream but the request keeps recurring. Routes through
373
373
  `ExportPDF` (DOCX Studio handles the actual conversion).
374
374
 
@@ -447,6 +447,53 @@ Issue numbers reference `python-openxml/python-docx`.
447
447
  only when you already have a DOCX editor anchor id. For normal writing,
448
448
  cite the ``Paragraph`` proxy you just created or found.
449
449
 
450
+ - **Table-cell citations** (0.22.0+): **`_Cell.add_citation(source, *,
451
+ anchor=None, citation_string=None, excerpt=None, display_value=None)
452
+ -> str`** + **`_Cell.clear_citations()`**, and ``Paragraph.add_citation``
453
+ / ``Paragraph.clear_citations`` on cell paragraphs (before 0.22.0 they
454
+ raised ``ValueError`` "top-level DOCX paragraphs only"). The SDK models a
455
+ cell as one paragraph addressed by the synthetic cell id
456
+ (``tc:table:row:col``; ``cell.paragraphs[0]`` stands for the whole cell),
457
+ so a cell citation sends that id. The docx-studio applier resolves it to
458
+ the cell's paragraph ids: one paragraph with text → ``pm_node``, several
459
+ → ``pm_node_range`` from the first to the last; a cell paragraph with no
460
+ paraId (or a repeated one) gets a new one. A horizontally merged cell
461
+ resolves to its master cell from any grid position it covers. A
462
+ paragraph that ``cell.add_paragraph(text)`` returned already carries its
463
+ own paraId and is cited alone. To cite one paragraph of a cell (or a
464
+ paragraph in a nested table), pass the ``data-id`` a Word read shows for
465
+ it: ``doc.add_citation(..., destination_anchor=PmNodeAnchor(node_id=...))``.
466
+ ``_Cell.clear_citations()`` removes every citation inside the cell.
467
+ Write the cell text first, then cite it:
468
+
469
+ ```python
470
+ table = doc.add_table(rows=2, cols=2)
471
+ table.cell(1, 0).text = "Revenue"
472
+ table.cell(1, 1).text = "$12.4M"
473
+ table.cell(1, 1).add_citation(
474
+ "/dashboard/spaces/?asset_ids=asset_sheet&type=spreadsheet"
475
+ "&anchor=sheet_range&sheet_id=0&range=C4",
476
+ excerpt="$12.4M",
477
+ )
478
+ ```
479
+
480
+ Needs a docx-studio API that resolves cell ids (the same release train);
481
+ an older API stores the record under the cell id, where nothing renders
482
+ it and the next edit retires it.
483
+
484
+ - **Image region citations**: cite a region of an image asset with
485
+ ``AssetReference(id=<image>)`` + ``PixelRangeAnchor(x=, y=, width=,
486
+ height=)`` in source image pixels, or with a
487
+ ``/dashboard/spaces/?asset_ids=<image>&type=image&anchor=pixel_range&x=…&y=…&width=…&height=…``
488
+ citation string. DOCX Studio stores ``type=image`` and keeps ``excerpt``
489
+ out of the URL (the record keeps it as the span of the cited paragraph to
490
+ highlight). Since 0.23.0, ``add_citation`` raises ``ValueError`` for the
491
+ image citations that chat and Sheets reject (agora
492
+ ``validate_image_citation_url``): an image URL with ``excerpt`` or
493
+ ``#:~:text=`` and no anchor, a non-region anchor, a region anchor without
494
+ x, y, width and height, and an ``AssetMeta(asset_type="image")`` source
495
+ with a non-region anchor or with an excerpt and no anchor.
496
+
450
497
  - **`Document.export_docx(path=None, *, include_revisions=False)
451
498
  -> bytes`** (0.11.4+) — export the document's current state as
452
499
  ``.docx`` bytes, optionally writing to ``path``. Closes the gap
@@ -769,7 +816,7 @@ This is a **thin HTTP client** that mimics the sync python-docx API.
769
816
  drain pending ops before running; the 100 ms idle timer fires on
770
817
  inactivity so collab updates stream live; `Document.save()` and
771
818
  the context-manager exit drain explicitly; `docx.flush_all()` is
772
- the Daytona-prelude hook that flushes every live buffer in the
819
+ the executor-postamble hook that flushes every live buffer in the
773
820
  process.
774
821
  - Execution scope: executor sandboxes may keep Python globals
775
822
  between tool calls so imports, constants, and helper functions can be
@@ -0,0 +1,5 @@
1
+ @AGENTS.md
2
+
3
+ <!-- AGENTS.md next to this file is the single source of truth for agent
4
+ instructions. Claude Code does not read AGENTS.md natively, so this file
5
+ imports it. Add Claude-Code-only rules below this line. -->
@@ -6,7 +6,7 @@ shortens the debug loop by running local SDK code directly against a
6
6
  chosen DOCX Studio API, capturing stdout/stderr/errors/timings, and
7
7
  optionally exporting the result to `.docx` for assertions.
8
8
 
9
- Use this before waiting on a full Agent -> Daytona -> preview cycle when
9
+ Use this before waiting on a full Agent -> executor sandbox -> preview cycle when
10
10
  debugging SDK behavior such as large-document replacement, table fills,
11
11
  unsupported OOXML internals, or export fidelity.
12
12
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: athena-python-docx
3
- Version: 0.21.8
3
+ Version: 0.23.0
4
4
  Summary: Drop-in replacement for python-docx that connects to Athena's DOCX Studio collaborative document stack
5
5
  Project-URL: Homepage, https://athenaintelligence.ai
6
6
  Author-email: Athena Intelligence <engineering@athenaintelligence.ai>
@@ -76,7 +76,7 @@ an Olympus Word document embed preview pane. See `DOCX_EXEC_LAB.md`.
76
76
 
77
77
  ## Environment variables
78
78
 
79
- Required when connecting to DOCX Studio (set by Athena backend when executing in Daytona):
79
+ Required when connecting to DOCX Studio (set by the Athena backend in the `execute_word_document_code` executor sandbox):
80
80
 
81
81
  - `ATHENA_DOCX_BASE_URL` — DOCX Studio API base URL
82
82
  - `ATHENA_DOCX_API_KEY` — Athena API key or service-issued DOCX Studio token
@@ -51,7 +51,7 @@ an Olympus Word document embed preview pane. See `DOCX_EXEC_LAB.md`.
51
51
 
52
52
  ## Environment variables
53
53
 
54
- Required when connecting to DOCX Studio (set by Athena backend when executing in Daytona):
54
+ Required when connecting to DOCX Studio (set by the Athena backend in the `execute_word_document_code` executor sandbox):
55
55
 
56
56
  - `ATHENA_DOCX_BASE_URL` — DOCX Studio API base URL
57
57
  - `ATHENA_DOCX_API_KEY` — Athena API key or service-issued DOCX Studio token
@@ -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.21.8"
9
+ __version__ = "0.23.0"
10
10
 
11
11
  from docx.api import Document
12
12
  from docx._buffer import discard_all, flush_all
@@ -20,7 +20,7 @@ guard the pending list with an RLock and treat the timer flush as a
20
20
  best-effort coalesce — if the loop thread races us we just flush twice
21
21
  (or zero times; the next loop-thread call drains it).
22
22
 
23
- `flush_all` is a process-wide hook used by the Daytona sandbox prelude to
23
+ `flush_all` is a process-wide hook used by Athena's sandbox executor to
24
24
  make sure pending writes hit Keryx before the sandbox is suspended. It walks
25
25
  a weak-ref registry so dead Workbook instances don't keep their buffers
26
26
  alive.
@@ -199,7 +199,7 @@ def _unregister(buffer: "CommandBuffer") -> None:
199
199
  def flush_all(*, strict: bool = False) -> None:
200
200
  """Flush every live CommandBuffer in this process.
201
201
 
202
- Used by the Daytona sandbox prelude after user code returns, so
202
+ Used by Athena's sandbox executor after user code returns, so
203
203
  buffered mutations make it to Keryx before the sandbox is suspended.
204
204
  Safe to call when no Buffers exist (no-op).
205
205
 
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import uuid
6
+ from collections.abc import Mapping
6
7
  from typing import Any
7
8
  from urllib.parse import parse_qs, urlparse
8
9
 
@@ -34,6 +35,87 @@ def source_ref_to_dict(source: AssetReference) -> dict[str, Any]:
34
35
  return source.to_dict()
35
36
 
36
37
 
38
+ _IMAGE_REGION_ANCHOR_TYPES: tuple[str, ...] = ("pixel_range", "rect")
39
+ _IMAGE_REGION_FIELDS: tuple[str, ...] = ("x", "y", "width", "height")
40
+
41
+
42
+ def _is_number(value: object) -> bool:
43
+ """True for what agora's anchor parser reads as a number (``float(...)``)."""
44
+ if isinstance(value, bool):
45
+ return False
46
+ if isinstance(value, (int, float)):
47
+ return True
48
+ if isinstance(value, str):
49
+ try:
50
+ float(value)
51
+ except ValueError:
52
+ return False
53
+ return True
54
+ return False
55
+
56
+
57
+ def check_image_citation(
58
+ *,
59
+ anchor_type: str | None,
60
+ has_excerpt: bool,
61
+ region: Mapping[str, object] | None = None,
62
+ ) -> None:
63
+ """Reject an image citation that chat and Sheets reject.
64
+
65
+ The same rules as agora's ``validate_citation_url`` for an image: an
66
+ image is cited by a region (``anchor=pixel_range`` with numeric x, y,
67
+ width and height in source image pixels), never by a page, a text anchor
68
+ or an excerpt. ``region`` holds the x, y, width and height values of a
69
+ region anchor (``None`` for a missing one).
70
+ """
71
+ if anchor_type and anchor_type not in _IMAGE_REGION_ANCHOR_TYPES:
72
+ raise ValueError(
73
+ f"An image citation cannot use anchor={anchor_type}. Cite a region "
74
+ "of the image: anchor=pixel_range with x, y, width and height in "
75
+ "source image pixels (PixelRangeAnchor)."
76
+ )
77
+ if not anchor_type and has_excerpt:
78
+ raise ValueError(
79
+ "An image citation cannot use an excerpt or a #:~:text= fragment: "
80
+ "an image has no text to find. Cite a region of the image: "
81
+ "anchor=pixel_range with x, y, width and height in source image "
82
+ "pixels (PixelRangeAnchor), and put the label in display_value."
83
+ )
84
+ if not anchor_type or region is None:
85
+ return
86
+ missing = [field for field in _IMAGE_REGION_FIELDS if region.get(field) is None]
87
+ if missing:
88
+ raise ValueError(
89
+ f"An image citation with anchor={anchor_type} needs x, y, width "
90
+ f"and height. Missing: {', '.join(missing)}."
91
+ )
92
+ not_numbers = [field for field in _IMAGE_REGION_FIELDS if not _is_number(region[field])]
93
+ if not_numbers:
94
+ raise ValueError(
95
+ f"An image citation with anchor={anchor_type} needs numbers for "
96
+ f"x, y, width and height. Not a number: {', '.join(not_numbers)}."
97
+ )
98
+
99
+
100
+ def _check_image_citation_url(query: str, fragment: str) -> None:
101
+ # Blank values are dropped, as agora's validator parses the query.
102
+ params = parse_qs(query)
103
+
104
+ def first(key: str) -> str | None:
105
+ values = params.get(key)
106
+ return values[0] if values else None
107
+
108
+ asset_type = (first("asset_type") or first("type") or "").lower()
109
+ if asset_type != "image":
110
+ return
111
+ anchor_type = (first("anchor") or "").lower() or None
112
+ check_image_citation(
113
+ anchor_type=anchor_type,
114
+ has_excerpt=first("excerpt") is not None or fragment.startswith(":~:text="),
115
+ region={field: first(field) for field in _IMAGE_REGION_FIELDS},
116
+ )
117
+
118
+
37
119
  def normalize_citation_string(citation_string: str) -> str:
38
120
  value = citation_string.strip()
39
121
  if not value:
@@ -49,6 +131,7 @@ def normalize_citation_string(citation_string: str) -> str:
49
131
  asset_ids = parse_qs(parsed.query).get("asset_ids")
50
132
  if not asset_ids or not asset_ids[0]:
51
133
  raise ValueError("Athena Spaces citation URLs must include asset_ids")
134
+ _check_image_citation_url(parsed.query, parsed.fragment)
52
135
  return value
53
136
 
54
137
  if not parsed.scheme and value.startswith("/"):
@@ -61,6 +144,7 @@ def normalize_citation_string(citation_string: str) -> str:
61
144
  if relative.path.rstrip("/") == "/dashboard/spaces":
62
145
  asset_ids = parse_qs(relative.query).get("asset_ids")
63
146
  if asset_ids and asset_ids[0]:
147
+ _check_image_citation_url(relative.query, relative.fragment)
64
148
  return value
65
149
  raise ValueError("Athena Spaces citation URLs must include asset_ids")
66
150
 
@@ -106,6 +190,35 @@ def destination_anchor_to_dict(destination_anchor: object) -> dict[str, Any]:
106
190
  )
107
191
 
108
192
 
193
+ def _check_typed_image_citation(
194
+ *,
195
+ source_ref: dict[str, Any],
196
+ source_anchor: dict[str, Any] | None,
197
+ excerpt: str | None,
198
+ ) -> None:
199
+ """Apply the image citation rules to an ``AssetReference`` source.
200
+
201
+ A ``PixelRangeAnchor`` / ``RectAnchor`` source is an image region:
202
+ DOCX Studio names it ``type=image`` and keeps the excerpt out of its URL
203
+ (the record keeps it as the span of the cited paragraph to highlight).
204
+ A source that ``AssetMeta(asset_type="image")`` names an image must use
205
+ one of those anchors.
206
+ """
207
+ anchor = source_anchor or source_ref.get("anchor")
208
+ anchor_type = anchor.get("type") if isinstance(anchor, dict) else None
209
+ meta = source_ref.get("meta")
210
+ asset_type = meta.get("assetType") if isinstance(meta, dict) else None
211
+ is_region = anchor_type in _IMAGE_REGION_ANCHOR_TYPES
212
+ if not is_region and str(asset_type or "").lower() != "image":
213
+ return
214
+ meta_excerpt = meta.get("excerpt") if isinstance(meta, dict) else None
215
+ check_image_citation(
216
+ anchor_type=anchor_type,
217
+ has_excerpt=excerpt is not None or meta_excerpt is not None,
218
+ region=anchor if is_region and isinstance(anchor, dict) else None,
219
+ )
220
+
221
+
109
222
  def build_add_citation_command(
110
223
  *,
111
224
  destination_anchor: object,
@@ -142,11 +255,19 @@ def build_add_citation_command(
142
255
  if source_ref is None and anchor is not None:
143
256
  raise ValueError("anchor requires an AssetReference source; encode the anchor in citation_string instead")
144
257
 
258
+ source_anchor = anchor_to_dict(anchor)
259
+ if source_ref is not None:
260
+ _check_typed_image_citation(
261
+ source_ref=source_ref,
262
+ source_anchor=source_anchor,
263
+ excerpt=excerpt,
264
+ )
265
+
145
266
  citation_id = new_citation_id()
146
267
  return citation_id, AddCitation(
147
268
  destination_anchor=destination_anchor_to_dict(destination_anchor),
148
269
  source_ref=source_ref,
149
- source_anchor=anchor_to_dict(anchor),
270
+ source_anchor=source_anchor,
150
271
  citation_string=normalized_citation_string,
151
272
  citation_id=citation_id,
152
273
  excerpt=excerpt,
@@ -5,7 +5,7 @@ bootstrap and upload calls on urllib so ``Document.create()`` can provision
5
5
  Word document assets without pulling in extra runtime dependencies.
6
6
 
7
7
  We use ``urllib`` from stdlib to avoid pulling ``httpx`` / ``requests``
8
- into the runtime — this code runs in Daytona sandboxes where every MB
8
+ into the runtime — this code runs in sandboxes where every MB
9
9
  matters, and the existing SDK already keeps its dep tree tight.
10
10
  """
11
11
 
@@ -46,6 +46,7 @@ from docx.commands import (
46
46
  BookmarksList,
47
47
  ChartsGet,
48
48
  ChartsList,
49
+ ClearBody,
49
50
  ClearParagraphAlignment,
50
51
  ClearParagraphIndentation,
51
52
  ClearParagraphSpacing,
@@ -1002,6 +1003,7 @@ _OP_TO_COMMAND: dict[str, type[Command]] = {
1002
1003
  "sdt.delete": ContentControlsDelete,
1003
1004
  "sections.set_columns": SetSectionColumns,
1004
1005
  "find_replace": FindReplace,
1006
+ "clear_body": ClearBody,
1005
1007
  "find_text": FindText,
1006
1008
  "iter_runs": IterRuns,
1007
1009
  "export_pdf": ExportPDF,
@@ -497,6 +497,27 @@ class PostExportOps:
497
497
  or bool(self._row_cant_split_ops)
498
498
  )
499
499
 
500
+ def discard_body_ops(self) -> None:
501
+ """Drop every rewrite that addresses body content by position.
502
+
503
+ Cell/table formatting, row heights, captured REF paragraphs and
504
+ header/footer relocations are all keyed by body-relative indices
505
+ (``table_doc_index``, ``source_paragraph_index``, body node ids).
506
+ Once the body story has been replaced they point at nodes that no
507
+ longer exist — and would silently re-target whatever the caller
508
+ creates next at the same index. Document-wide rewrites
509
+ (``font_override``) do not address the body and are kept.
510
+ """
511
+ self._cell_format_ops.clear()
512
+ self._cell_run_format_ops.clear()
513
+ self._cell_hyperlink_ops.clear()
514
+ self._cell_shading_ops.clear()
515
+ self._table_borders_ops.clear()
516
+ self._captured_refs.clear()
517
+ self._row_height_ops.clear()
518
+ self._row_cant_split_ops.clear()
519
+ self._header_footer_relocations.clear()
520
+
500
521
  def add_row_cant_split(
501
522
  self,
502
523
  *,
@@ -23,6 +23,11 @@ Emits are **synchronous, best-effort** HTTP POSTs:
23
23
  - Snapshot the URL at ``emit_begin`` time and carry it to ``emit_end``
24
24
  so late end events can't be misattributed if a new sandbox run
25
25
  swapped in a different URL between begin and end.
26
+ - Reuse one keep-alive connection per endpoint instead of a new TCP (and
27
+ TLS) connection per event. An idle connection the server has closed is
28
+ detected before sending and replaced; a send is never retried (the
29
+ registry does not deduplicate events). When a proxy applies to the URL
30
+ (``*_proxy`` env vars) each event goes through urllib as before.
26
31
 
27
32
  This module is *intentionally byte-identical* across the docx / pptx /
28
33
  xlsx SDKs — the three SDKs have no shared base. Duplicating ~100 LOC
@@ -31,10 +36,14 @@ of stdlib code is cheaper than spinning up a 4th release pipeline.
31
36
 
32
37
  from __future__ import annotations
33
38
 
39
+ import http.client
34
40
  import json
35
41
  import os
42
+ import select
43
+ import threading
36
44
  import time
37
45
  import urllib.error
46
+ import urllib.parse
38
47
  import urllib.request
39
48
  import uuid
40
49
  from typing import Any
@@ -46,6 +55,58 @@ _HTTP_TIMEOUT = 2.0
46
55
  # original run's endpoint even if ATHENA_PTC_URL changed since.
47
56
  _call_url: dict[str, str] = {}
48
57
 
58
+ # (scheme, host:port) -> an idle keep-alive connection. Checked out while a
59
+ # POST uses it, so no two threads ever share one.
60
+ _idle: dict[tuple[str, str], http.client.HTTPConnection] = {}
61
+ _idle_lock = threading.Lock()
62
+
63
+
64
+ def _dropped(conn: http.client.HTTPConnection) -> bool:
65
+ """An idle connection the server has closed (or that has unexpected data
66
+ waiting) reads as ready: never send on it."""
67
+ sock = conn.sock
68
+ if sock is None:
69
+ return True
70
+ try:
71
+ return bool(select.select([sock], [], [], 0)[0])
72
+ except (OSError, ValueError):
73
+ return True
74
+
75
+
76
+ def _post_keepalive(url: str, raw: bytes) -> None:
77
+ parts = urllib.parse.urlsplit(url)
78
+ if parts.scheme not in ("http", "https") or not parts.hostname:
79
+ raise ValueError(f"unsupported PTC URL: {url!r}")
80
+ target = (parts.path or "/") + (f"?{parts.query}" if parts.query else "")
81
+ key = (parts.scheme, parts.netloc)
82
+ with _idle_lock:
83
+ conn = _idle.pop(key, None)
84
+ if conn is not None and _dropped(conn):
85
+ conn.close()
86
+ conn = None
87
+ if conn is None:
88
+ factory = (
89
+ http.client.HTTPSConnection if parts.scheme == "https" else http.client.HTTPConnection
90
+ )
91
+ conn = factory(parts.netloc, timeout=_HTTP_TIMEOUT)
92
+ # Never retried: a failure after the request left may still have been
93
+ # processed, and the registry does not deduplicate events.
94
+ try:
95
+ conn.request("POST", target, body=raw, headers={"Content-Type": "application/json"})
96
+ response = conn.getresponse()
97
+ response.read()
98
+ except BaseException:
99
+ conn.close()
100
+ raise
101
+ if response.will_close:
102
+ conn.close()
103
+ return
104
+ with _idle_lock:
105
+ spare = _idle.pop(key, None)
106
+ _idle[key] = conn
107
+ if spare is not None:
108
+ spare.close()
109
+
49
110
 
50
111
  def _send(url: str, body: dict[str, Any]) -> None:
51
112
  """Synchronously POST one PTC event. Best-effort; never raises.
@@ -63,14 +124,19 @@ def _send(url: str, body: dict[str, Any]) -> None:
63
124
  key = "args" if body.get("phase") == "begin" else "result"
64
125
  body[key] = {"__truncated__": True}
65
126
  raw = json.dumps(body).encode("utf-8")
66
- req = urllib.request.Request(
67
- url,
68
- data=raw,
69
- method="POST",
70
- headers={"Content-Type": "application/json"},
71
- )
72
- urllib.request.urlopen(req, timeout=_HTTP_TIMEOUT).close()
73
- except (urllib.error.URLError, OSError, ValueError):
127
+ parts = urllib.parse.urlsplit(url)
128
+ proxy = urllib.request.getproxies().get(parts.scheme)
129
+ if proxy and not urllib.request.proxy_bypass(parts.hostname or ""):
130
+ req = urllib.request.Request(
131
+ url,
132
+ data=raw,
133
+ method="POST",
134
+ headers={"Content-Type": "application/json"},
135
+ )
136
+ urllib.request.urlopen(req, timeout=_HTTP_TIMEOUT).close()
137
+ else:
138
+ _post_keepalive(url, raw)
139
+ except (urllib.error.URLError, OSError, ValueError, http.client.HTTPException):
74
140
  pass # never propagate — PTC is best-effort
75
141
 
76
142
 
@@ -19,6 +19,9 @@ _LONG_RUNNING_COMMAND_TYPES: frozenset[str] = frozenset(
19
19
  # ordinary 60s budget committing a table batch even when the Python code
20
20
  # uses the fast no-read cell path.
21
21
  "TableSetCell",
22
+ # ``Document.clear()`` replaces the whole body story in one write;
23
+ # on a large document that single command can outlast the 60s budget.
24
+ "ClearBody",
22
25
  }
23
26
  )
24
27
 
@@ -36,7 +36,7 @@ if TYPE_CHECKING:
36
36
 
37
37
  def _log_info(msg: str) -> None:
38
38
  """Minimal stderr logger; we avoid loguru to keep the SDK runtime
39
- dependency tree small (this code runs inside Daytona sandboxes)."""
39
+ dependency tree small (this code runs inside sandboxes)."""
40
40
  import sys
41
41
 
42
42
  print(f"[docx-sdk] {msg}", file=sys.stderr)
@@ -84,6 +84,10 @@ class Session:
84
84
  from docx._postproc import PostExportOps
85
85
 
86
86
  self._post_export_ops: PostExportOps = PostExportOps()
87
+ # Bumped by ``Document.clear()``. ``Table`` proxies remember the
88
+ # generation they were created in, so a proxy from before a clear
89
+ # can never be re-anchored to a table created after it.
90
+ self._body_generation: int = 0
87
91
 
88
92
  @property
89
93
  def asset_id(self) -> str:
@@ -136,6 +136,10 @@ class Insert(Command):
136
136
  # on the DOCX Studio call (renamed in transit so it doesn't collide with our
137
137
  # discriminator).
138
138
  value_type: str | None = None
139
+ # Text mode only. ``True`` continues the formatting of the run at the
140
+ # insertion point (``Run.add_text``); omitted means the inserted text is
141
+ # a fresh, unformatted run (``Paragraph.add_run``).
142
+ inherit_marks: bool | None = None
139
143
  placement: str | None = None
140
144
  content: dict[str, Any] | None = None
141
145
 
@@ -1543,6 +1547,18 @@ class FindReplace(Command):
1543
1547
  max_replacements: int | None = None
1544
1548
 
1545
1549
 
1550
+ @dataclass
1551
+ class ClearBody(Command):
1552
+ """Replace the whole body story with a single empty paragraph.
1553
+
1554
+ Athena extension backing :meth:`Document.clear`. One server-side
1555
+ write instead of a ``Replace`` per paragraph and per table-cell
1556
+ paragraph; headers, footers and notes are untouched and the final
1557
+ section's page setup survives. Returns ``{nodeId}`` of the new
1558
+ empty paragraph.
1559
+ """
1560
+
1561
+
1546
1562
  @dataclass
1547
1563
  class FindText(Command):
1548
1564
  """Search document text and return bounded match context.
@@ -1576,8 +1592,8 @@ class IterRuns(Command):
1576
1592
 
1577
1593
  # ---------------------------------------------------------------------------
1578
1594
  # PDF Export (Athena extension — python-docx issue #113 keeps getting
1579
- # re-asked despite being closed wontfix. DOCX Studio renders via Daytona
1580
- # (LibreOffice headless) to bytes the client can download or persist.)
1595
+ # re-asked despite being closed wontfix. DOCX Studio renders the document
1596
+ # to bytes the client can download or persist.)
1581
1597
  # ---------------------------------------------------------------------------
1582
1598
 
1583
1599
 
@@ -1759,6 +1775,9 @@ _RESPONSE_BEARING_TYPES: frozenset[str] = frozenset(
1759
1775
  "ExportDocx",
1760
1776
  # FindReplace returns a replacement count for the caller.
1761
1777
  "FindReplace",
1778
+ # ClearBody replaces the whole body; Document.clear awaits it so a
1779
+ # rejected clear raises instead of leaving stale content behind.
1780
+ "ClearBody",
1762
1781
  }
1763
1782
  )
1764
1783
 
@@ -1852,6 +1871,7 @@ for _cls, _issue, _desc in [
1852
1871
  (ContentControlsDelete, "python-docx#155", "SDT delete"),
1853
1872
  (SetSectionColumns, "python-docx#167", "Section multi-column layout"),
1854
1873
  (FindReplace, "python-docx#30", "Formatting-preserving find/replace"),
1874
+ (ClearBody, None, "Document.clear — bulk body reset"),
1855
1875
  (FindText, "python-docx#980", "Fast document text search"),
1856
1876
  (IterRuns, "python-docx#980", "Run stream for run-isolation"),
1857
1877
  (ExportPDF, "python-docx#113", "PDF export"),
@@ -1990,6 +2010,7 @@ __all__ = [
1990
2010
  "ContentControlsDelete",
1991
2011
  "SetSectionColumns",
1992
2012
  "FindReplace",
2013
+ "ClearBody",
1993
2014
  "FindText",
1994
2015
  "IterRuns",
1995
2016
  "ExportPDF",