athena-python-docx 0.21.2__tar.gz → 0.21.5__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 (383) hide show
  1. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/PKG-INFO +1 -1
  2. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/__init__.py +3 -2
  3. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_buffer.py +93 -5
  4. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/client.py +8 -3
  5. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/table.py +74 -6
  6. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/text/paragraph.py +34 -0
  7. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/text/run.py +10 -0
  8. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/pyproject.toml +1 -1
  9. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/fake_session.py +60 -46
  10. athena_python_docx-0.21.5/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +6 -0
  11. athena_python_docx-0.21.5/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +9 -0
  12. athena_python_docx-0.21.5/tests/fidelity/op_snapshots/35_full_report.json +39 -0
  13. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -2
  14. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -6
  15. athena_python_docx-0.21.5/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +8 -0
  16. athena_python_docx-0.21.5/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +19 -0
  17. athena_python_docx-0.21.5/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +604 -0
  18. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/68_invoice.json +0 -8
  19. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/69_newsletter.json +0 -24
  20. athena_python_docx-0.21.5/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +35 -0
  21. athena_python_docx-0.21.5/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +8 -0
  22. athena_python_docx-0.21.5/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +124 -0
  23. athena_python_docx-0.21.5/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +34 -0
  24. athena_python_docx-0.21.5/tests/fidelity/op_snapshots/ex14_styled_report_table.json +37 -0
  25. athena_python_docx-0.21.5/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +272 -0
  26. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/mega01_book_chapter.json +3 -87
  27. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/mega02_research_proposal.json +5 -53
  28. athena_python_docx-0.21.5/tests/fidelity/op_snapshots/mega03_financial_statement.json +45 -0
  29. athena_python_docx-0.21.5/tests/fidelity/op_snapshots/mega05_user_manual.json +49 -0
  30. athena_python_docx-0.21.5/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +50 -0
  31. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/mega09_signed_contract.json +2 -24
  32. athena_python_docx-0.21.5/tests/fidelity/op_snapshots/mega10_api_documentation.json +95 -0
  33. athena_python_docx-0.21.5/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +36 -0
  34. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_buffer.py +97 -0
  35. athena_python_docx-0.21.5/tests/test_cell_fastpath_runs.py +208 -0
  36. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_positional_cell_id.py +69 -3
  37. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_ptc.py +35 -0
  38. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/uv.lock +1 -1
  39. athena_python_docx-0.21.2/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -12
  40. athena_python_docx-0.21.2/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -19
  41. athena_python_docx-0.21.2/tests/fidelity/op_snapshots/35_full_report.json +0 -78
  42. athena_python_docx-0.21.2/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -19
  43. athena_python_docx-0.21.2/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -53
  44. athena_python_docx-0.21.2/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -2804
  45. athena_python_docx-0.21.2/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -89
  46. athena_python_docx-0.21.2/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -14
  47. athena_python_docx-0.21.2/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -764
  48. athena_python_docx-0.21.2/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -94
  49. athena_python_docx-0.21.2/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -148
  50. athena_python_docx-0.21.2/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -632
  51. athena_python_docx-0.21.2/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -122
  52. athena_python_docx-0.21.2/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -137
  53. athena_python_docx-0.21.2/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -170
  54. athena_python_docx-0.21.2/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -271
  55. athena_python_docx-0.21.2/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -132
  56. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/.gitignore +0 -0
  57. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/CLAUDE.md +0 -0
  58. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/DOCX_EXEC_LAB.md +0 -0
  59. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/README.md +0 -0
  60. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_athena_extension.py +0 -0
  61. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_batching.py +0 -0
  62. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_citations.py +0 -0
  63. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_execution.py +0 -0
  64. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_http.py +0 -0
  65. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_http_doc.py +0 -0
  66. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_image_utils.py +0 -0
  67. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_postproc.py +0 -0
  68. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_ptc.py +0 -0
  69. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_references.py +0 -0
  70. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_table_styles.py +0 -0
  71. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/_timeouts.py +0 -0
  72. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/api.py +0 -0
  73. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/bookmarks.py +0 -0
  74. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/charts.py +0 -0
  75. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/commands.py +0 -0
  76. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/comments.py +0 -0
  77. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/document.py +0 -0
  78. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/enum/__init__.py +0 -0
  79. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/enum/section.py +0 -0
  80. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/enum/shape.py +0 -0
  81. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/enum/style.py +0 -0
  82. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/enum/table.py +0 -0
  83. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/enum/text.py +0 -0
  84. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/errors.py +0 -0
  85. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/exceptions.py +0 -0
  86. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/fields.py +0 -0
  87. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/footnotes.py +0 -0
  88. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/math.py +0 -0
  89. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/opc/__init__.py +0 -0
  90. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/opc/coreprops.py +0 -0
  91. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/oxml/__init__.py +0 -0
  92. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/revisions.py +0 -0
  93. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/sdt.py +0 -0
  94. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/section.py +0 -0
  95. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/session.py +0 -0
  96. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/settings.py +0 -0
  97. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/shape.py +0 -0
  98. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/shared.py +0 -0
  99. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/styles/__init__.py +0 -0
  100. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/styles/style.py +0 -0
  101. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/styles/styles.py +0 -0
  102. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/text/__init__.py +0 -0
  103. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/text/font.py +0 -0
  104. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/text/hyperlink.py +0 -0
  105. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/text/pagebreak.py +0 -0
  106. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/text/parfmt.py +0 -0
  107. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/text/tabstops.py +0 -0
  108. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/toc.py +0 -0
  109. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/docx/typing.py +0 -0
  110. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/docx_exec_lab.py +0 -0
  111. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
  112. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
  113. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/docx_exec_lab_server.py +0 -0
  114. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/dump_wire_fixtures.py +0 -0
  115. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/publish.sh +0 -0
  116. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/release.sh +0 -0
  117. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/round_trip_smoke.py +0 -0
  118. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/smoke_test_block_not_found.py +0 -0
  119. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/scripts/validate_find_replace_asset.py +0 -0
  120. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/__init__.py +0 -0
  121. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/conftest.py +0 -0
  122. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/METHODOLOGY.md +0 -0
  123. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/README.md +0 -0
  124. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/__init__.py +0 -0
  125. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/ab_probe_cases.py +0 -0
  126. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/ab_probe_runner.py +0 -0
  127. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/auto_gen_cases.py +0 -0
  128. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/binary_round_trip.py +0 -0
  129. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/cases.py +0 -0
  130. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/complex_cases.py +0 -0
  131. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/coverage_report.py +0 -0
  132. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/extract.py +0 -0
  133. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/extreme_cases.py +0 -0
  134. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/firm_templates/README.md +0 -0
  135. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/firm_templates/__init__.py +0 -0
  136. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/firm_templates/_runner.py +0 -0
  137. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/firm_templates/extractor.py +0 -0
  138. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
  139. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
  140. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/local_runner.py +0 -0
  141. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/mega_cases.py +0 -0
  142. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshot.py +0 -0
  143. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
  144. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
  145. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
  146. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
  147. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
  148. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
  149. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
  150. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
  151. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
  152. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
  153. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
  154. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
  155. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
  156. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
  157. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
  158. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
  159. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
  160. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
  161. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
  162. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
  163. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
  164. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
  165. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
  166. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
  167. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
  168. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
  169. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
  170. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
  171. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
  172. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
  173. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
  174. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
  175. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
  176. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
  177. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
  178. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
  179. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
  180. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
  181. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
  182. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
  183. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
  184. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
  185. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
  186. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
  187. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
  188. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
  189. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
  190. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
  191. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
  192. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
  193. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
  194. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
  195. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
  196. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
  197. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
  198. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
  199. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
  200. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
  201. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
  202. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
  203. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
  204. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
  205. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
  206. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
  207. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
  208. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
  209. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
  210. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
  211. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
  212. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
  213. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
  214. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
  215. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
  216. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
  217. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
  218. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
  219. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
  220. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
  221. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
  222. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
  223. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
  224. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
  225. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
  226. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
  227. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
  228. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
  229. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
  230. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
  231. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
  232. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
  233. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
  234. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
  235. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
  236. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
  237. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
  238. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
  239. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
  240. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
  241. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
  242. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
  243. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
  244. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
  245. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
  246. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
  247. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
  248. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
  249. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
  250. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
  251. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
  252. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
  253. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
  254. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
  255. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
  256. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
  257. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
  258. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
  259. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
  260. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
  261. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
  262. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
  263. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
  264. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
  265. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
  266. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/ours_spec.json +0 -0
  267. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/parity_crawl.py +0 -0
  268. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/parity_diff.json +0 -0
  269. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/real_world_cases.py +0 -0
  270. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/round_trip_tests.py +0 -0
  271. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/stock_spec.json +0 -0
  272. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/fidelity/test_e2e_against_staging.py +0 -0
  273. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/README.md +0 -0
  274. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/__init__.py +0 -0
  275. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/baseline_gaps.json +0 -0
  276. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/compare.py +0 -0
  277. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/intentional_deviations.json +0 -0
  278. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/introspect.py +0 -0
  279. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
  280. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/reports/gap_report.json +0 -0
  281. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/run_parity.py +0 -0
  282. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/snapshots/athena_latest.json +0 -0
  283. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
  284. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/parity/test_parity_gap.py +0 -0
  285. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_add_section_extract_items.py +0 -0
  286. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_athena_extensions_contract.py +0 -0
  287. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_athena_extensions_registry.py +0 -0
  288. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_batching_perf.py +0 -0
  289. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_block_not_found_error.py +0 -0
  290. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
  291. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_cell_add_table_not_supported.py +0 -0
  292. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
  293. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
  294. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_cell_inner_format_stash.py +0 -0
  295. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_cell_inner_run_format_stash.py +0 -0
  296. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_cell_inner_run_guard.py +0 -0
  297. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_cell_text_plain_fastpath.py +0 -0
  298. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_cell_text_replace_semantics.py +0 -0
  299. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_citations.py +0 -0
  300. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_collapsed_range_format.py +0 -0
  301. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_command_dataclasses.py +0 -0
  302. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_commands.py +0 -0
  303. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_comments.py +0 -0
  304. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_document_asset_id_property.py +0 -0
  305. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_document_clear.py +0 -0
  306. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_document_create.py +0 -0
  307. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_document_create_from_template.py +0 -0
  308. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_document_factory_validation.py +0 -0
  309. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_docx_exec_lab.py +0 -0
  310. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_docx_exec_lab_server.py +0 -0
  311. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_e2e_partial_failure_cascade.py +0 -0
  312. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_execution_scope.py +0 -0
  313. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_find_replace_session_open.py +0 -0
  314. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_http_transport.py +0 -0
  315. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_hyperlink_coalescing.py +0 -0
  316. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_image_url_data_uri.py +0 -0
  317. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_insert_deferred.py +0 -0
  318. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_iter_inner_content.py +0 -0
  319. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_list_styles.py +0 -0
  320. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_merged_cell_secondary_slot.py +0 -0
  321. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_merged_cells.py +0 -0
  322. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_oxml_shim.py +0 -0
  323. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_paragraph_text_len_cache.py +0 -0
  324. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_parity_misc.py +0 -0
  325. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_parity_round2.py +0 -0
  326. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_partial_failure_cascade.py +0 -0
  327. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_phase_a_behavior.py +0 -0
  328. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_phase_b_headers_footers.py +0 -0
  329. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_phase_c_tables.py +0 -0
  330. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_postproc_cell_format_rewrite.py +0 -0
  331. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
  332. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_postproc_ref_restore.py +0 -0
  333. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_pr19766_review_fixes.py +0 -0
  334. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_python_docx_api_parity.py +0 -0
  335. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_revisions.py +0 -0
  336. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_add_paragraph_style.py +0 -0
  337. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_add_picture.py +0 -0
  338. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_add_run.py +0 -0
  339. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
  340. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_comments_add_comment.py +0 -0
  341. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_comments_get.py +0 -0
  342. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_document_audit.py +0 -0
  343. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_document_element.py +0 -0
  344. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_enum_section.py +0 -0
  345. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_font_audit.py +0 -0
  346. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_header_footer.py +0 -0
  347. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_hyperlink.py +0 -0
  348. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_inline_shape.py +0 -0
  349. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
  350. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_misc.py +0 -0
  351. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_paragraph_strict.py +0 -0
  352. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_paragraph_style.py +0 -0
  353. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
  354. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_parfmt.py +0 -0
  355. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_row_col_cell.py +0 -0
  356. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_run_add_break.py +0 -0
  357. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_run_bool_setters.py +0 -0
  358. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_run_style.py +0 -0
  359. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_run_style_strict.py +0 -0
  360. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_run_text.py +0 -0
  361. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_run_underline.py +0 -0
  362. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_section_audit.py +0 -0
  363. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_section_dimensions.py +0 -0
  364. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_section_onoff.py +0 -0
  365. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_settings.py +0 -0
  366. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_shared_audit.py +0 -0
  367. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_style.py +0 -0
  368. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_styles.py +0 -0
  369. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_table_audit.py +0 -0
  370. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_table_cell.py +0 -0
  371. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_table_dimensions.py +0 -0
  372. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_silent_stub_table_layout.py +0 -0
  373. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_smoke_integration.py +0 -0
  374. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_style_acceptance.py +0 -0
  375. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_style_font.py +0 -0
  376. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_style_setters_contract.py +0 -0
  377. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_table_set_cell_perf.py +0 -0
  378. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_table_style_id_resolution.py +0 -0
  379. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_temporarily_unavailable.py +0 -0
  380. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_validate_find_replace_asset_script.py +0 -0
  381. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_wire_contract.py +0 -0
  382. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/tests/test_xml_attr_guard.py +0 -0
  383. {athena_python_docx-0.21.2 → athena_python_docx-0.21.5}/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.21.2
3
+ Version: 0.21.5
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>
@@ -6,10 +6,10 @@ See CLAUDE.md for the API parity contract.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- __version__ = "0.21.2"
9
+ __version__ = "0.21.5"
10
10
 
11
11
  from docx.api import Document
12
- from docx._buffer import flush_all
12
+ from docx._buffer import discard_all, flush_all
13
13
 
14
14
  # Re-exports python-docx ships at docx top-level for convenience.
15
15
  from docx.shared import Emu, Inches, Pt, Cm, Mm, Twips, Length, RGBColor
@@ -25,6 +25,7 @@ __all__ = [
25
25
  "Length",
26
26
  "RGBColor",
27
27
  "flush_all",
28
+ "discard_all",
28
29
  "__version__",
29
30
  ]
30
31
 
@@ -124,10 +124,12 @@ if TYPE_CHECKING:
124
124
  # HTTP POST to agora (see ``_ptc._send``), so emitting one card per low-level
125
125
  # mutation produces hundreds of sub-cards per script and pays full network
126
126
  # RTT on every one. The allow-list keeps the per-action signal — one card
127
- # per logical block creation (paragraph, heading, table) — without the spam
128
- # or the cumulative latency. Asset-level creation events (``CreateDocument``)
129
- # are emitted from ``Document.create`` directly, not through this buffer path.
130
- _PTC_EMIT_TOOLS: frozenset[str] = frozenset({"CreateParagraph", "CreateHeading", "CreateTable"})
127
+ # per logical block/citation creation — without the spam or the cumulative
128
+ # latency. Asset-level creation events (``CreateDocument``) are emitted from
129
+ # ``Document.create`` directly, not through this buffer path.
130
+ _PTC_EMIT_TOOLS: frozenset[str] = frozenset(
131
+ {"CreateParagraph", "CreateHeading", "CreateTable", "AddCitation"}
132
+ )
131
133
 
132
134
 
133
135
  def _should_emit_ptc(cmd: Command) -> bool:
@@ -243,6 +245,42 @@ def flush_all(*, strict: bool = False) -> None:
243
245
  raise FlushAllError(failures=failures)
244
246
 
245
247
 
248
+ def discard_all() -> int:
249
+ """Drop the pending commands of every live CommandBuffer; return the count.
250
+
251
+ The failed-script counterpart of :func:`flush_all`: after user code
252
+ raises, the executor discards the half-buffered tail instead of posting
253
+ it (see :meth:`CommandBuffer.discard_pending`). Safe to call when no
254
+ buffers exist (returns 0).
255
+
256
+ Buffers from a previous ``execute_word_document_code`` call are skipped
257
+ (mirroring :func:`flush_all`'s stale handling) so cross-execution
258
+ leftovers stay visible to diagnostics instead of being silently
259
+ swallowed. Best-effort like :meth:`CommandBuffer.discard_pending`: a
260
+ batch already handed to an in-flight flush cannot be recalled.
261
+ """
262
+ with _registry_lock:
263
+ snapshot = list(_active_buffers)
264
+ live = [ref for ref in snapshot if ref() is not None]
265
+ _active_buffers[:] = live
266
+
267
+ dropped = 0
268
+ for ref in live:
269
+ buf = ref()
270
+ if buf is None:
271
+ continue
272
+ if buf.is_stale_for_current_execution:
273
+ pending_count = buf.pending_count
274
+ if pending_count > 0:
275
+ sys.stderr.write(
276
+ f"[docx-sdk] discard_all: skipping stale buffer "
277
+ f"{buf.asset_id} with {pending_count} unflushed command(s)\n",
278
+ )
279
+ continue
280
+ dropped += buf.discard_pending()
281
+ return dropped
282
+
283
+
246
284
  # ---------------------------------------------------------------------------
247
285
  # CommandBuffer
248
286
  # ---------------------------------------------------------------------------
@@ -314,6 +352,12 @@ class CommandBuffer:
314
352
  # original semantics.
315
353
  self._change_mode: str | None = None
316
354
  self._user: dict[str, str] | None = None
355
+ # A batch the background idle timer failed to deliver. The timer
356
+ # thread can't raise into user code, so the failure is stashed here
357
+ # and re-raised on the NEXT buffer interaction (call/flush) — a
358
+ # dropped batch must never be reported as success by a later
359
+ # ``doc.save()`` that finds an empty buffer.
360
+ self._deferred_flush_error: Exception | None = None
317
361
  # client_node_id → list of (proxy, attr) pairs to update with the
318
362
  # real nodeId after flush. Populated by ``add_paragraph`` /
319
363
  # ``add_heading`` / etc. when they queue a Create with a
@@ -420,6 +464,7 @@ class CommandBuffer:
420
464
  proxy's ``_node_id`` in-place.
421
465
  """
422
466
  self._assert_current_execution()
467
+ self._raise_deferred_flush_error()
423
468
  if self._closed:
424
469
  raise RuntimeError(
425
470
  f"CommandBuffer for {self._asset_id} is closed",
@@ -488,6 +533,7 @@ class CommandBuffer:
488
533
  UUID swap.
489
534
  """
490
535
  self._assert_current_execution()
536
+ self._raise_deferred_flush_error()
491
537
  with self._lock:
492
538
  self._cancel_timer_locked()
493
539
  pending = self._pending
@@ -633,12 +679,54 @@ class CommandBuffer:
633
679
  self._timer.cancel()
634
680
  self._timer = None
635
681
 
682
+ def _raise_deferred_flush_error(self) -> None:
683
+ """Re-raise a failure captured by the background idle timer.
684
+
685
+ Cleared on raise so the surfaced traceback is delivered exactly once;
686
+ the batch it describes is already gone either way.
687
+ """
688
+ with self._lock:
689
+ err = self._deferred_flush_error
690
+ self._deferred_flush_error = None
691
+ if err is not None:
692
+ raise err
693
+
694
+ def discard_pending(self) -> int:
695
+ """Drop buffered commands without sending them; return the count.
696
+
697
+ Used by the sandbox executor when user code raised: posting the dead
698
+ script's half-buffered tail writes content whose prerequisites failed
699
+ (and duplicates it when the agent retries the whole script).
700
+
701
+ Best-effort: only commands still sitting in the pending queue are
702
+ dropped. A batch the idle timer has already drained and handed to the
703
+ HTTP client is in flight and cannot be recalled (the module
704
+ docstring's timer-race note applies here too).
705
+ """
706
+ with self._lock:
707
+ self._cancel_timer_locked()
708
+ pending = self._pending
709
+ self._pending = []
710
+ self._proxy_id_refs = {}
711
+ self._deferred_flush_error = None
712
+ _ptc_emit_end_batch(pending, is_error=True)
713
+ return len(pending)
714
+
636
715
  def _auto_flush(self) -> None:
637
716
  if self.is_stale_for_current_execution:
638
717
  return
639
718
  try:
640
719
  self.flush()
641
720
  except Exception as e: # noqa: BLE001
721
+ # The timer thread has no caller to raise into. Record the
722
+ # failure so the next call()/flush() (e.g. ``doc.save()``)
723
+ # surfaces it instead of reporting a drained buffer as success.
724
+ e.add_note(
725
+ "raised by a background docx-sdk auto-flush that failed earlier; "
726
+ "the commands in that batch were NOT applied",
727
+ )
728
+ with self._lock:
729
+ self._deferred_flush_error = e
642
730
  sys.stderr.write(
643
731
  f"[docx-sdk] auto-flush failed for {self._asset_id}: {e}\n",
644
732
  )
@@ -650,4 +738,4 @@ class CommandBuffer:
650
738
  )
651
739
 
652
740
 
653
- __all__ = ["CommandBuffer", "flush_all", "DEFAULT_AUTO_FLUSH_SECONDS"]
741
+ __all__ = ["CommandBuffer", "flush_all", "discard_all", "DEFAULT_AUTO_FLUSH_SECONDS"]
@@ -223,9 +223,14 @@ class Session:
223
223
 
224
224
  handle = self._doc_handle
225
225
  buffer = getattr(handle, "buffer", None) if handle is not None else None
226
- if buffer is not None:
227
- buffer.flush()
228
- _log_info(f"Saved {self._asset_id} (buffer drained)")
226
+ if buffer is None:
227
+ _log_info(f"Saved {self._asset_id} (no command buffer)")
228
+ return
229
+ flushed = buffer.flush()
230
+ # flush() raises on delivery failure — including one captured earlier
231
+ # by the background auto-flush — so this success line is only reached
232
+ # when nothing the buffer ever held was silently dropped.
233
+ _log_info(f"Saved {self._asset_id} ({len(flushed)} buffered command(s) flushed)")
229
234
 
230
235
  async def close(self) -> None:
231
236
  """Close the session. Idempotent."""
@@ -458,6 +458,13 @@ class Table:
458
458
  # Cells written before 0.15 (the pre-tracking world) silently fall
459
459
  # back to the legacy append behavior on their first 0.15+ rewrite.
460
460
  self._written_cells: set[tuple[int, int]] = set()
461
+ # Cell → plain text the SDK itself wrote this session via ``.text``.
462
+ # Powers the read-free ``_Cell.paragraphs``/``runs`` fast path: while
463
+ # a cell's full content is SDK-known (single plain-text run), header/
464
+ # zebra formatting loops need zero query round-trips. Any write the
465
+ # SDK can't model (markdown text, ``add_paragraph``, cell-inner
466
+ # ``add_run``) evicts the entry and restores the query path.
467
+ self._fastpath_cell_text: dict[tuple[int, int], str] = {}
461
468
 
462
469
  def _fresh_node_info(self) -> tuple[str, dict]:
463
470
  """Resolve the live nodeId and return ``(node_id, info_dict)``.
@@ -1541,11 +1548,13 @@ class _Cell:
1541
1548
 
1542
1549
  Reads the proxy's stored ``_node_id`` with NO round-trip. DOCX Studio
1543
1550
  tables carry real (non-``t:N``) node ids → returns ``None`` so the
1544
- caller queries (DOCX Studio is pooled, so that read is cheap anyway). A
1545
- freshly-created docx-editor table still holds a client UUID until its
1546
- create flushes, so the first cell op falls back to the query (which
1547
- flushes + rotates ``_node_id`` to ``t:N``) and every subsequent cell
1548
- op fast-paths.
1551
+ caller queries (DOCX Studio is pooled, so that read is cheap anyway).
1552
+ A freshly-created docx-editor table still holds a client UUID until its
1553
+ create flushes. For docx-editor, :meth:`Document.add_table` already
1554
+ knows the table's eventual document-order index, so SDK-created body
1555
+ tables can fast-path before a fragile first ``tables.get_cells`` query.
1556
+ We first try to flush the pending create so DOCX Studio's non-positional
1557
+ table ids can still fall back to the query path after the id rewrite.
1549
1558
 
1550
1559
  Gated to a **known uniform grid**: the table must have been created
1551
1560
  via :meth:`Document.add_table` (so ``_doc_index`` is set and the full
@@ -1557,11 +1566,27 @@ class _Cell:
1557
1566
  fall back to the query path to stay address-correct.
1558
1567
  """
1559
1568
  table = self._table
1560
- if getattr(table, "_doc_index", None) is None or getattr(table, "_has_merges", False):
1569
+ doc_index = getattr(table, "_doc_index", None)
1570
+ if doc_index is None or getattr(table, "_has_merges", False):
1561
1571
  return None
1562
1572
  nid = getattr(table, "_node_id", None)
1563
1573
  if isinstance(nid, str) and nid.startswith("t:") and nid[2:].isdigit():
1564
1574
  return f"tc:{nid[2:]}:{self._row}:{self._col}"
1575
+ if (
1576
+ isinstance(nid, str)
1577
+ and nid.startswith("t_")
1578
+ and getattr(table, "_header_footer_ref_id", None) is None
1579
+ ):
1580
+ handle = getattr(table._session, "_doc_handle", None)
1581
+ buf = getattr(handle, "buffer", None) if handle is not None else None
1582
+ if buf is not None:
1583
+ buf.flush()
1584
+ nid = getattr(table, "_node_id", None)
1585
+ if isinstance(nid, str) and nid.startswith("t:") and nid[2:].isdigit():
1586
+ return f"tc:{nid[2:]}:{self._row}:{self._col}"
1587
+ if not (isinstance(nid, str) and nid.startswith("t_")):
1588
+ return None
1589
+ return f"tc:{doc_index}:{self._row}:{self._col}"
1565
1590
  return None
1566
1591
 
1567
1592
  def _cell_id(self) -> str:
@@ -1882,6 +1907,10 @@ class _Cell:
1882
1907
  # to the same position takes the clear-then-insert branch above
1883
1908
  # (see ``Table._written_cells`` docstring).
1884
1909
  self._table._written_cells.add(cell_key)
1910
+ if _is_plain_text(value) and value:
1911
+ self._table._fastpath_cell_text[cell_key] = value
1912
+ else:
1913
+ self._table._fastpath_cell_text.pop(cell_key, None)
1885
1914
  return
1886
1915
  except Exception as e:
1887
1916
  raise RuntimeError(
@@ -1902,6 +1931,38 @@ class _Cell:
1902
1931
  # debugger and gives ``_apply_inline`` and friends something
1903
1932
  # stable to consult when we eventually add a materialization
1904
1933
  # fallback at the SDK layer.
1934
+ fast_text = self._table._fastpath_cell_text.get((self._row, self._col))
1935
+ if fast_text is not None:
1936
+ positional = self._positional_cell_id()
1937
+ if positional is not None:
1938
+ # Read-free fast path: the SDK wrote this cell's entire
1939
+ # content itself (one plain-text run), so it can hand back a
1940
+ # positional paragraph proxy without the per-cell
1941
+ # ``getNodeById`` walk. Formatting on its runs buffers a
1942
+ # whole-cell ``FormatApply`` against the ``tc:`` id — the
1943
+ # applier's documented cell-formatting semantics — so header/
1944
+ # zebra loops batch instead of paying one Y.Doc reload per
1945
+ # cell.
1946
+ ooxml_base = self._ooxml_address_base()
1947
+ fast_p = Paragraph(
1948
+ session=self._table._session,
1949
+ node_id=positional,
1950
+ node_type="paragraph",
1951
+ in_cell=True,
1952
+ cell_ooxml_address=(
1953
+ (*ooxml_base, 0) if ooxml_base is not None else None
1954
+ ),
1955
+ cell=self,
1956
+ )
1957
+ fast_p._cell_inner = True # type: ignore[attr-defined]
1958
+ fast_p._cell_address = { # type: ignore[attr-defined]
1959
+ "kind": "block",
1960
+ "nodeType": "tableCell",
1961
+ "nodeId": positional,
1962
+ }
1963
+ fast_p._fastpath_runs_text = fast_text # type: ignore[attr-defined]
1964
+ return [fast_p]
1965
+
1905
1966
  pids = self._inner_paragraph_ids()
1906
1967
  # Resolve the cell address ONCE and re-use it for every
1907
1968
  # paragraph; the original draft called ``self._cell_address()``
@@ -1970,6 +2031,9 @@ class _Cell:
1970
2031
  ) -> "Paragraph":
1971
2032
  """Append a paragraph to the cell.
1972
2033
 
2034
+ Evicts the cell from the read-free ``paragraphs`` fast path — the
2035
+ cell no longer holds the single SDK-written run that path models.
2036
+
1973
2037
  Mirrors python-docx ``_Cell.add_paragraph(text, style)``. The
1974
2038
  ``style`` argument accepts either a style id string or a
1975
2039
  :class:`docx.styles.style.ParagraphStyle` instance; previously
@@ -2007,6 +2071,10 @@ class _Cell:
2007
2071
  f"{type(style).__name__} {style!r}",
2008
2072
  )
2009
2073
 
2074
+ # Evict AFTER validation so a rejected call leaves the read-free
2075
+ # fast path intact (nothing mutated).
2076
+ self._table._fastpath_cell_text.pop((self._row, self._col), None)
2077
+
2010
2078
  fragment: dict = {
2011
2079
  "kind": "paragraph",
2012
2080
  "paragraph": {
@@ -448,6 +448,27 @@ class Paragraph:
448
448
  """
449
449
  from docx.text.run import Run
450
450
 
451
+ fast_text: object = getattr(self, "_fastpath_runs_text", None)
452
+ if isinstance(fast_text, str) and fast_text:
453
+ # Read-free fast path for a cell paragraph whose entire content
454
+ # the SDK wrote itself (see ``Table._fastpath_cell_text``): one
455
+ # synthetic run over the known text, no ``getNodeById`` walk.
456
+ # Format setters on it buffer a whole-cell ``FormatApply``
457
+ # against the positional ``tc:`` id; property READS may still
458
+ # trigger a live walk (the applier resolves ``tc:`` ids), so
459
+ # only the hot format-loop path is short-circuited.
460
+ self._text_len = len(fast_text)
461
+ fast_run = Run(
462
+ session=self._session,
463
+ block_id=self._node_id,
464
+ range_=(0, len(fast_text)),
465
+ inline={"type": "text", "text": fast_text},
466
+ in_cell=self._in_cell,
467
+ cell_ooxml_address=self._cell_ooxml_address,
468
+ )
469
+ fast_run._fastpath_text = fast_text # type: ignore[attr-defined]
470
+ return [fast_run]
471
+
451
472
  info: object = self._node_info()
452
473
  inlines: list[dict] = _walk_inlines(info)
453
474
  runs: list["Run"] = []
@@ -1131,6 +1152,19 @@ class Paragraph:
1131
1152
  f"or None; got {type(style).__name__} {style!r}",
1132
1153
  )
1133
1154
 
1155
+ # Content beyond the single SDK-written run: evict this paragraph's
1156
+ # cell from the read-free fast path so the next ``paragraphs``/
1157
+ # ``runs`` read reflects real server state. After validation so a
1158
+ # rejected call leaves the fast path intact (nothing mutated).
1159
+ self._fastpath_runs_text = None # type: ignore[attr-defined]
1160
+ cell = self._cell
1161
+ table = getattr(cell, "_table", None) if cell is not None else None
1162
+ if table is not None:
1163
+ row = getattr(cell, "_row", None)
1164
+ col = getattr(cell, "_col", None)
1165
+ if row is not None and col is not None:
1166
+ getattr(table, "_fastpath_cell_text", {}).pop((row, col), None)
1167
+
1134
1168
  # Use the cached paragraph text length when known — every
1135
1169
  # avoided ``_node_text`` here drops one ``get_node_by_id`` HTTP
1136
1170
  # round-trip. Linear ``doc.add_paragraph(); p.add_run(...);
@@ -336,6 +336,12 @@ class Run:
336
336
  # ---- text ----
337
337
  @property
338
338
  def text(self) -> str:
339
+ # Fast-path runs (see ``Table._fastpath_cell_text``) carry their
340
+ # known text — the whole point is zero read round-trips. Mutations
341
+ # through this proxy keep the attr in sync or clear it.
342
+ fast: object = getattr(self, "_fastpath_text", None)
343
+ if isinstance(fast, str):
344
+ return fast
339
345
  full: str = _block_text(self._session, self._block_id)
340
346
  return full[self._range[0] : self._range[1]]
341
347
 
@@ -399,7 +405,10 @@ class Run:
399
405
  f"{value!r}",
400
406
  )
401
407
  if self._check_cell_inner("text"):
408
+ # Warn-and-skip: nothing mutated, so the fast-path text cache
409
+ # (when present) still reflects reality — keep it.
402
410
  return
411
+ self._fastpath_text = None # type: ignore[attr-defined]
403
412
  old_len: int = self._range[1] - self._range[0]
404
413
  run_sync(
405
414
  self._session.doc.replace(
@@ -435,6 +444,7 @@ class Run:
435
444
  in_cell=True,
436
445
  cell_ooxml_address=getattr(self, "_cell_ooxml_address", None),
437
446
  )
447
+ self._fastpath_text = None # type: ignore[attr-defined]
438
448
  end: int = self._range[1]
439
449
  cursor: dict = {"kind": "text", "blockId": self._block_id, "offset": end}
440
450
  run_sync(
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "athena-python-docx"
7
- version = "0.21.2"
7
+ version = "0.21.5"
8
8
  description = "Drop-in replacement for python-docx that connects to Athena's DOCX Studio collaborative document stack"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -162,6 +162,31 @@ class FakeDocState:
162
162
  return t
163
163
  return None
164
164
 
165
+ def _resolve_cell_ref(self, node_id: object) -> tuple[_Table, int, int, _Block] | None:
166
+ if not isinstance(node_id, str) or not node_id:
167
+ return None
168
+
169
+ parts = node_id.split(":")
170
+ if len(parts) == 4 and parts[0] == "tc":
171
+ try:
172
+ table_idx = int(parts[1])
173
+ row_idx = int(parts[2])
174
+ col_idx = int(parts[3])
175
+ except ValueError:
176
+ return None
177
+ if 0 <= table_idx < len(self.tables):
178
+ table = self.tables[table_idx]
179
+ if 0 <= row_idx < len(table.cells) and 0 <= col_idx < len(table.cells[row_idx]):
180
+ return (table, row_idx, col_idx, table.cells[row_idx][col_idx])
181
+ return None
182
+
183
+ for table in self.tables:
184
+ for row_idx, row in enumerate(table.cells):
185
+ for col_idx, cell in enumerate(row):
186
+ if node_id in {cell.node_id, f"cell_{table.node_id}_{row_idx}_{col_idx}"}:
187
+ return (table, row_idx, col_idx, cell)
188
+ return None
189
+
165
190
  def respond(self, op: str, params: dict) -> Any:
166
191
  # ---- header/footer part orchestration ----
167
192
  # _HeaderFooter.add_paragraph / add_table mint a new part on
@@ -273,33 +298,29 @@ class FakeDocState:
273
298
  "node": node,
274
299
  "address": {"kind": "block", "nodeType": b.node_type, "nodeId": b.node_id},
275
300
  }
276
- # Table cell
277
- for t in self.tables:
278
- for r_idx, row in enumerate(t.cells):
279
- for c_idx, cell in enumerate(row):
280
- if cell.node_id == nid or (
281
- params.get("nodeType") == "tableCell"
282
- and nid == f"cell_{t.node_id}_{r_idx}_{c_idx}"
283
- ):
284
- # Return a tree containing the inner paragraph
285
- inner = {
286
- "type": "paragraph",
287
- "attrs": {"nodeId": cell.node_id},
288
- "content": [{"type": "text", "text": cell.text}]
289
- if cell.text
290
- else [],
291
- }
292
- return {
293
- "node": {
294
- "nodeType": "tableCell",
295
- "tableCell": {"content": [inner]},
296
- },
297
- "address": {
298
- "kind": "block",
299
- "nodeType": "tableCell",
300
- "nodeId": cell.node_id,
301
- },
302
- }
301
+ # Table cell. The real docx-editor surface addresses cells by
302
+ # positional ids (tc:<tableIndex>:<row>:<col>), while DOCX Studio
303
+ # also exposes table-specific ids from tables.getCells. Support
304
+ # both so the fake exercises the same no-query fast path as agents.
305
+ cell_ref = self._resolve_cell_ref(nid)
306
+ if cell_ref is not None:
307
+ _table, _row_idx, _col_idx, cell = cell_ref
308
+ inner = {
309
+ "type": "paragraph",
310
+ "attrs": {"nodeId": cell.node_id},
311
+ "content": [{"type": "text", "text": cell.text}] if cell.text else [],
312
+ }
313
+ return {
314
+ "node": {
315
+ "nodeType": "tableCell",
316
+ "tableCell": {"content": [inner]},
317
+ },
318
+ "address": {
319
+ "kind": "block",
320
+ "nodeType": "tableCell",
321
+ "nodeId": cell.node_id,
322
+ },
323
+ }
303
324
  return {"node": {}, "address": {}}
304
325
 
305
326
  if op == "blocks.list":
@@ -515,16 +536,11 @@ class FakeDocState:
515
536
  if isinstance(t, str):
516
537
  appended += t
517
538
  # Find cell and append to its first paragraph.
518
- for t in self.tables:
519
- for r_idx, row in enumerate(t.cells):
520
- for c_idx, cell in enumerate(row):
521
- expected_ids = {
522
- f"cell_{t.node_id}_{r_idx}_{c_idx}",
523
- cell.node_id,
524
- }
525
- if cell_nid in expected_ids:
526
- cell.text = cell.text + appended
527
- cell.inlines = [{"run": {"text": cell.text}}] if cell.text else []
539
+ cell_ref = self._resolve_cell_ref(cell_nid)
540
+ if cell_ref is not None:
541
+ _table, _row_idx, _col_idx, cell = cell_ref
542
+ cell.text = cell.text + appended
543
+ cell.inlines = [{"run": {"text": cell.text}}] if cell.text else []
528
544
  return {"ok": True}
529
545
 
530
546
  # TableSetCell: address a cell by (tableNodeId, row, col) and
@@ -979,15 +995,13 @@ class FakeDocState:
979
995
  if op == "tables.setCellProperties":
980
996
  target = params.get("target") or {}
981
997
  nid = target.get("nodeId") or params.get("nodeId")
982
- # Find the cell
983
- for t in self.tables:
984
- for r_idx, row in enumerate(t.cells):
985
- for c_idx, _cell in enumerate(row):
986
- if nid == f"cell_{t.node_id}_{r_idx}_{c_idx}":
987
- if "verticalAlign" in params:
988
- t.cell_verticals[r_idx][c_idx] = params["verticalAlign"]
989
- if "preferredWidthPt" in params:
990
- t.cell_widths[r_idx][c_idx] = params["preferredWidthPt"]
998
+ cell_ref = self._resolve_cell_ref(nid)
999
+ if cell_ref is not None:
1000
+ t, r_idx, c_idx, _cell = cell_ref
1001
+ if "verticalAlign" in params:
1002
+ t.cell_verticals[r_idx][c_idx] = params["verticalAlign"]
1003
+ if "preferredWidthPt" in params:
1004
+ t.cell_widths[r_idx][c_idx] = params["preferredWidthPt"]
991
1005
  return {"ok": True}
992
1006
  if op == "tables.setTableOptions":
993
1007
  # Per DOCX Studio 1.8.1 setTableOptions only handles
@@ -0,0 +1,6 @@
1
+ [
2
+ "create.table",
3
+ "tables.setCellProperties",
4
+ "tables.setCellProperties",
5
+ "tables.setCellProperties"
6
+ ]
@@ -0,0 +1,9 @@
1
+ [
2
+ "create.table",
3
+ "tables.setCell",
4
+ "insert",
5
+ "getNodeById",
6
+ "getNodeById",
7
+ "getNodeById",
8
+ "getNodeById"
9
+ ]
@@ -0,0 +1,39 @@
1
+ [
2
+ "sections.list",
3
+ "sections.setPageMargins",
4
+ "sections.setPageMargins",
5
+ "create.paragraph",
6
+ "styles.paragraph.setStyle",
7
+ "format.paragraph.setAlignment",
8
+ "create.paragraph",
9
+ "insert",
10
+ "format.apply",
11
+ "insert",
12
+ "insert",
13
+ "format.apply",
14
+ "format.apply",
15
+ "insert",
16
+ "format.paragraph.setSpacing",
17
+ "create.heading",
18
+ "create.table",
19
+ "tables.setStyle",
20
+ "tables.get",
21
+ "tables.setLayout",
22
+ "tables.get",
23
+ "tables.get",
24
+ "tables.setCell",
25
+ "tables.setCell",
26
+ "tables.setCell",
27
+ "tables.setCellProperties",
28
+ "format.apply",
29
+ "tables.setCellProperties",
30
+ "format.apply",
31
+ "tables.setCellProperties",
32
+ "format.apply",
33
+ "tables.setCell",
34
+ "tables.setCell",
35
+ "tables.setCell",
36
+ "tables.setCell",
37
+ "tables.setCell",
38
+ "tables.setCell"
39
+ ]
@@ -1,8 +1,6 @@
1
1
  [
2
2
  "create.table",
3
3
  "tables.setCell",
4
- "tables.get",
5
- "tables.getCells",
6
4
  "getNodeById",
7
5
  "getNodeById"
8
6
  ]
@@ -29,16 +29,10 @@
29
29
  "tables.get",
30
30
  "tables.setLayout",
31
31
  "tables.setCell",
32
- "tables.get",
33
- "tables.getCells",
34
32
  "tables.setCellProperties",
35
33
  "tables.setCell",
36
- "tables.get",
37
- "tables.getCells",
38
34
  "tables.setCellProperties",
39
35
  "tables.setCell",
40
- "tables.get",
41
- "tables.getCells",
42
36
  "tables.setCellProperties",
43
37
  "tables.setCell",
44
38
  "tables.setCell",
@@ -0,0 +1,8 @@
1
+ [
2
+ "create.table",
3
+ "tables.setCell",
4
+ "getNodeById",
5
+ "insert",
6
+ "getNodeById",
7
+ "getNodeById"
8
+ ]