athena-python-docx 0.29.0__tar.gz → 0.31.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 (455) hide show
  1. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/AGENTS.md +67 -15
  2. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/PKG-INFO +1 -1
  3. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/__init__.py +1 -1
  4. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_buffer.py +107 -3
  5. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_capabilities.py +8 -0
  6. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_http_doc.py +25 -5
  7. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_postproc.py +242 -1
  8. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_tracked.py +11 -0
  9. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/commands.py +20 -2
  10. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/comments.py +11 -4
  11. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/document.py +62 -7
  12. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/drawing/__init__.py +4 -1
  13. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/errors.py +18 -1
  14. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/revisions.py +4 -1
  15. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/section.py +16 -5
  16. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/shape.py +4 -1
  17. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/styles/style.py +107 -18
  18. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/table.py +173 -29
  19. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/text/hyperlink.py +10 -1
  20. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/text/paragraph.py +96 -8
  21. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/text/run.py +3 -2
  22. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/pyproject.toml +1 -1
  23. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/METHODOLOGY.md +35 -9
  24. athena_python_docx-0.31.0/tests/fidelity/op_snapshots/rw18_header_table_properties.json +34 -0
  25. athena_python_docx-0.31.0/tests/fidelity/op_snapshots/rw19_header_table_rows_columns.json +38 -0
  26. athena_python_docx-0.31.0/tests/fidelity/op_snapshots/rw20_header_table_merges.json +28 -0
  27. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/real_world_cases.py +74 -0
  28. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fixtures/field_reads.json +320 -20
  29. athena_python_docx-0.31.0/tests/live/baseline.json +478 -0
  30. athena_python_docx-0.31.0/tests/live/fixture_scripts.py +68 -0
  31. athena_python_docx-0.31.0/tests/live/reader.py +676 -0
  32. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/live/runner.py +140 -15
  33. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/live/test_live_runner.py +220 -2
  34. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/intentional_deviations.json +46 -2
  35. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_athena_extensions_registry.py +1 -0
  36. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_merge.py +17 -7
  37. athena_python_docx-0.31.0/tests/test_cell_nested_tables.py +82 -0
  38. athena_python_docx-0.31.0/tests/test_comments_first_read.py +101 -0
  39. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_export_docx_clean.py +1 -1
  40. athena_python_docx-0.31.0/tests/test_export_rescue_anchors.py +348 -0
  41. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_field_reads_python_docx_parity.py +20 -6
  42. athena_python_docx-0.31.0/tests/test_flushed_write_failures.py +590 -0
  43. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_http_transport.py +1 -1
  44. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_hyperlink_coalescing.py +40 -0
  45. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_iter_inner_content.py +35 -0
  46. athena_python_docx-0.31.0/tests/test_nested_table_refused.py +95 -0
  47. athena_python_docx-0.31.0/tests/test_paragraph_delete.py +187 -0
  48. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_postproc_ref_restore.py +48 -0
  49. athena_python_docx-0.31.0/tests/test_style_tab_stops_read.py +127 -0
  50. athena_python_docx-0.31.0/tests/test_table_anchors.py +219 -0
  51. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_table_read_reuse.py +29 -0
  52. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_tracked_revisions_offsets.py +28 -15
  53. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_wire_contract.py +1 -0
  54. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/uv.lock +7 -7
  55. athena_python_docx-0.29.0/tests/live/baseline.json +0 -262
  56. athena_python_docx-0.29.0/tests/live/reader.py +0 -243
  57. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/.gitignore +0 -0
  58. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/CLAUDE.md +0 -0
  59. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/DOCX_EXEC_LAB.md +0 -0
  60. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/README.md +0 -0
  61. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_athena_extension.py +0 -0
  62. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_batching.py +0 -0
  63. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_citations.py +0 -0
  64. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_execution.py +0 -0
  65. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_http.py +0 -0
  66. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_image_utils.py +0 -0
  67. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_ptc.py +0 -0
  68. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_references.py +0 -0
  69. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_run_order.py +0 -0
  70. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_table_styles.py +0 -0
  71. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/_timeouts.py +0 -0
  72. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/api.py +0 -0
  73. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/blkcntnr.py +0 -0
  74. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/bookmarks.py +0 -0
  75. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/charts.py +0 -0
  76. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/client.py +0 -0
  77. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/enum/__init__.py +0 -0
  78. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/enum/base.py +0 -0
  79. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/enum/dml.py +0 -0
  80. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/enum/section.py +0 -0
  81. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/enum/shape.py +0 -0
  82. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/enum/style.py +0 -0
  83. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/enum/table.py +0 -0
  84. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/enum/text.py +0 -0
  85. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/exceptions.py +0 -0
  86. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/fields.py +0 -0
  87. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/footnotes.py +0 -0
  88. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/image/__init__.py +0 -0
  89. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/image/exceptions.py +0 -0
  90. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/image/image.py +0 -0
  91. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/math.py +0 -0
  92. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/opc/__init__.py +0 -0
  93. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/opc/coreprops.py +0 -0
  94. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/oxml/__init__.py +0 -0
  95. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/sdt.py +0 -0
  96. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/session.py +0 -0
  97. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/settings.py +0 -0
  98. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/shared.py +0 -0
  99. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/styles/__init__.py +0 -0
  100. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/styles/_table.py +0 -0
  101. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/styles/latent.py +0 -0
  102. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/styles/styles.py +0 -0
  103. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/text/__init__.py +0 -0
  104. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/text/font.py +0 -0
  105. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/text/pagebreak.py +0 -0
  106. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/text/parfmt.py +0 -0
  107. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/text/tabstops.py +0 -0
  108. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/toc.py +0 -0
  109. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/docx/typing.py +0 -0
  110. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/scripts/docx_exec_lab.py +0 -0
  111. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
  112. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
  113. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/scripts/docx_exec_lab_server.py +0 -0
  114. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/scripts/dump_wire_fixtures.py +0 -0
  115. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/scripts/release.sh +0 -0
  116. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/scripts/round_trip_smoke.py +0 -0
  117. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/scripts/smoke_test_block_not_found.py +0 -0
  118. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/scripts/validate_find_replace_asset.py +0 -0
  119. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/__init__.py +0 -0
  120. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/conftest.py +0 -0
  121. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/README.md +0 -0
  122. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/__init__.py +0 -0
  123. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/ab_probe_cases.py +0 -0
  124. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/ab_probe_runner.py +0 -0
  125. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/auto_gen_cases.py +0 -0
  126. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/binary_round_trip.py +0 -0
  127. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/cases.py +0 -0
  128. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/complex_cases.py +0 -0
  129. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/coverage_report.py +0 -0
  130. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/extract.py +0 -0
  131. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/extreme_cases.py +0 -0
  132. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/fake_session.py +0 -0
  133. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/firm_templates/README.md +0 -0
  134. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/firm_templates/__init__.py +0 -0
  135. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/firm_templates/_runner.py +0 -0
  136. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/firm_templates/extractor.py +0 -0
  137. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
  138. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
  139. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/local_runner.py +0 -0
  140. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/mega_cases.py +0 -0
  141. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshot.py +0 -0
  142. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
  143. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
  144. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
  145. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
  146. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
  147. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
  148. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
  149. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
  150. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
  151. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
  152. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
  153. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
  154. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
  155. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
  156. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
  157. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
  158. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
  159. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
  160. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
  161. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
  162. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
  163. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
  164. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
  165. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
  166. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
  167. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
  168. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
  169. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
  170. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
  171. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
  172. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
  173. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
  174. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
  175. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
  176. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
  177. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
  178. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
  179. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
  180. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
  181. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
  182. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
  183. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
  184. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
  185. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
  186. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
  187. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
  188. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
  189. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
  190. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
  191. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
  192. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
  193. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
  194. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
  195. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
  196. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
  197. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
  198. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
  199. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
  200. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
  201. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
  202. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
  203. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
  204. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
  205. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
  206. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
  207. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
  208. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
  209. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
  210. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
  211. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
  212. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
  213. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
  214. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
  215. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
  216. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
  217. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
  218. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
  219. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
  220. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
  221. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
  222. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
  223. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
  224. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
  225. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
  226. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
  227. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
  228. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
  229. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
  230. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
  231. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
  232. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
  233. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
  234. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
  235. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
  236. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
  237. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
  238. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
  239. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
  240. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
  241. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
  242. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
  243. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
  244. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
  245. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
  246. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
  247. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
  248. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
  249. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
  250. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
  251. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
  252. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
  253. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
  254. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
  255. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
  256. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
  257. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
  258. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
  259. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
  260. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
  261. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
  262. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
  263. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
  264. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
  265. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
  266. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
  267. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
  268. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
  269. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
  270. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
  271. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
  272. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
  273. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
  274. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
  275. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
  276. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
  277. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
  278. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
  279. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
  280. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
  281. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
  282. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
  283. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
  284. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
  285. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
  286. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
  287. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
  288. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
  289. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw16_header_footer_tables.json +0 -0
  290. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/op_snapshots/rw17_header_letterhead_filled.json +0 -0
  291. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/ours_spec.json +0 -0
  292. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/parity_crawl.py +0 -0
  293. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/parity_diff.json +0 -0
  294. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/round_trip_tests.py +0 -0
  295. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/stock_spec.json +0 -0
  296. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
  297. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fixtures/note_reference_reads.json +0 -0
  298. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fixtures/ruby_reads.json +0 -0
  299. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/fixtures/symbol_reads.json +0 -0
  300. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/live/__init__.py +0 -0
  301. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/live/sdk_driver.py +0 -0
  302. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/live/stock_driver.py +0 -0
  303. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/README.md +0 -0
  304. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/__init__.py +0 -0
  305. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/baseline_gaps.json +0 -0
  306. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/compare.py +0 -0
  307. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/introspect.py +0 -0
  308. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
  309. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/reports/gap_report.json +0 -0
  310. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/run_parity.py +0 -0
  311. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/snapshots/athena_latest.json +0 -0
  312. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
  313. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/parity/test_parity_gap.py +0 -0
  314. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_add_section.py +0 -0
  315. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_add_section_extract_items.py +0 -0
  316. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_athena_extension_creates_session_open.py +0 -0
  317. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_athena_extensions_contract.py +0 -0
  318. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_batch_chunking.py +0 -0
  319. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_batching_perf.py +0 -0
  320. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_block_not_found_error.py +0 -0
  321. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_buffer.py +0 -0
  322. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
  323. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_add_table_not_supported.py +0 -0
  324. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_borders_spec.py +0 -0
  325. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_fastpath_runs.py +0 -0
  326. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
  327. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
  328. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_inner_format_stash.py +0 -0
  329. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_inner_run_format_stash.py +0 -0
  330. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_inner_run_guard.py +0 -0
  331. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_paragraphs_listed.py +0 -0
  332. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_text_plain_fastpath.py +0 -0
  333. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_cell_text_replace_semantics.py +0 -0
  334. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_citations.py +0 -0
  335. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_collapsed_range_format.py +0 -0
  336. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_color_format_theme.py +0 -0
  337. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_command_dataclasses.py +0 -0
  338. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_commands.py +0 -0
  339. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_comment_paragraphs.py +0 -0
  340. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_comments.py +0 -0
  341. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_content_control_value.py +0 -0
  342. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_core_properties.py +0 -0
  343. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_document_asset_id_property.py +0 -0
  344. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_document_clear.py +0 -0
  345. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_document_create.py +0 -0
  346. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_document_create_from_template.py +0 -0
  347. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_document_factory_validation.py +0 -0
  348. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_docx_exec_lab.py +0 -0
  349. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_docx_exec_lab_server.py +0 -0
  350. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_drawing_image.py +0 -0
  351. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
  352. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_empty_hyperlinks.py +0 -0
  353. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_enum_parity.py +0 -0
  354. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_execution_scope.py +0 -0
  355. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_field_refresh.py +0 -0
  356. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_field_runs.py +0 -0
  357. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_find_replace_session_open.py +0 -0
  358. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_image_url_data_uri.py +0 -0
  359. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_inline_page_break.py +0 -0
  360. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_inline_shape_alt_text.py +0 -0
  361. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_inline_shape_size.py +0 -0
  362. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_inline_shape_type.py +0 -0
  363. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_inline_shapes_floating.py +0 -0
  364. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_insert_deferred.py +0 -0
  365. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_list_styles.py +0 -0
  366. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_merged_cell_secondary_slot.py +0 -0
  367. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_merged_cells.py +0 -0
  368. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_missing_modules.py +0 -0
  369. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_note_reference_reads_python_docx_parity.py +0 -0
  370. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_oxml_shim.py +0 -0
  371. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_page_break.py +0 -0
  372. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_paragraph_runs_hyperlinks.py +0 -0
  373. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_paragraph_text_len_cache.py +0 -0
  374. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_parity_misc.py +0 -0
  375. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_parity_round2.py +0 -0
  376. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_partial_failure_cascade.py +0 -0
  377. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_pdf_export_and_page_renders.py +0 -0
  378. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_phase_a_behavior.py +0 -0
  379. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_phase_b_headers_footers.py +0 -0
  380. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_phase_c_tables.py +0 -0
  381. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_positional_cell_id.py +0 -0
  382. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
  383. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
  384. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_pr19766_review_fixes.py +0 -0
  385. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_ptc.py +0 -0
  386. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_python_docx_api_parity.py +0 -0
  387. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_read_run_ranges.py +0 -0
  388. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_repeated_node_read.py +0 -0
  389. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_restart_numbering.py +0 -0
  390. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_revisions.py +0 -0
  391. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_round_trip_smoke_script.py +0 -0
  392. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_ruby_reads_python_docx_parity.py +0 -0
  393. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_run_add_picture_anchor.py +0 -0
  394. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_run_edges.py +0 -0
  395. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_run_formatting_wire.py +0 -0
  396. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_run_identity.py +0 -0
  397. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_run_mark_inheritance.py +0 -0
  398. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_run_table_style_objects.py +0 -0
  399. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_run_text_python_docx.py +0 -0
  400. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_run_text_refreshes_paragraph.py +0 -0
  401. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_sdk_reads_served.py +0 -0
  402. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_section_read_lengths.py +0 -0
  403. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
  404. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_add_picture.py +0 -0
  405. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_add_run.py +0 -0
  406. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
  407. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
  408. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_comments_get.py +0 -0
  409. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_document_audit.py +0 -0
  410. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_document_element.py +0 -0
  411. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_enum_section.py +0 -0
  412. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_font_audit.py +0 -0
  413. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_header_footer.py +0 -0
  414. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_hyperlink.py +0 -0
  415. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_inline_shape.py +0 -0
  416. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
  417. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_misc.py +0 -0
  418. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
  419. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_paragraph_style.py +0 -0
  420. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
  421. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_parfmt.py +0 -0
  422. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_row_col_cell.py +0 -0
  423. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_run_add_break.py +0 -0
  424. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
  425. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_run_style.py +0 -0
  426. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_run_style_strict.py +0 -0
  427. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_run_text.py +0 -0
  428. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_run_underline.py +0 -0
  429. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_section_audit.py +0 -0
  430. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_section_dimensions.py +0 -0
  431. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_section_onoff.py +0 -0
  432. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_settings.py +0 -0
  433. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_shared_audit.py +0 -0
  434. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_style.py +0 -0
  435. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_styles.py +0 -0
  436. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_table_audit.py +0 -0
  437. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_table_cell.py +0 -0
  438. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_table_dimensions.py +0 -0
  439. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_silent_stub_table_layout.py +0 -0
  440. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_smoke_integration.py +0 -0
  441. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_style_acceptance.py +0 -0
  442. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_style_font.py +0 -0
  443. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_style_formatting_writes.py +0 -0
  444. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_style_metadata_writes.py +0 -0
  445. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_style_names.py +0 -0
  446. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_style_setters_contract.py +0 -0
  447. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_symbol_reads_python_docx_parity.py +0 -0
  448. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_table_set_cell_perf.py +0 -0
  449. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_table_style_id_resolution.py +0 -0
  450. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_temporarily_unavailable.py +0 -0
  451. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_text_control_characters.py +0 -0
  452. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_uv_lock_version.py +0 -0
  453. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_validate_find_replace_asset_script.py +0 -0
  454. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_xml_attr_guard.py +0 -0
  455. {athena_python_docx-0.29.0 → athena_python_docx-0.31.0}/tests/test_zod_wire_contract.py +0 -0
@@ -204,14 +204,21 @@ not file-backed. Each is documented in the relevant docstring.
204
204
  are sent with `changeMode: "tracked"`, and docx-studio records their text
205
205
  edits as Word revisions (`w:ins` / `w:del`): run and paragraph text,
206
206
  `find_replace` in the body, new paragraphs and headings (an inserted
207
- paragraph mark), tabs and line breaks, run formatting (`w:rPrChange`) and
207
+ paragraph mark), whole paragraphs deleted (`paragraph.delete()`, 0.30.0+:
208
+ its content, fields included, and its paragraph mark), tabs and line breaks,
209
+ run formatting (`w:rPrChange`) and
208
210
  `paragraph_format` properties (`w:pPrChange`). The replaced text stays, deleted,
209
211
  and the new text follows it, so the SDK keeps its paragraph lengths and
210
212
  run ranges that way (`docx/_tracked.py`): a tracked `run.text = value`
211
213
  leaves the run over `value`, after its deleted text, and the paragraph
212
- grows by `len(value)`. Every batch says so to docx-studio
213
- (`client.capabilities: ["trackedAuthoring"]`); a docx-studio refuses a
214
- tracked edit from an SDK that does not. A batch holding an edit it cannot
214
+ grows by `len(value)`. `find_replace` and (0.30.0+) `paragraph.text = value`
215
+ mark only the words that change, the words the old and new text share at
216
+ their start and end left as they are, so after a tracked `paragraph.text`
217
+ the SDK reads the paragraph's length back. Every batch says so to docx-studio
218
+ (`client.capabilities: ["trackedAuthoring", "trackedMinimalParagraphText"]`);
219
+ a docx-studio refuses a tracked edit from an SDK that does not declare
220
+ `trackedAuthoring`, and replaces a paragraph's whole text for one that
221
+ does not declare `trackedMinimalParagraphText`. A batch holding an edit it cannot
215
222
  record yet (paragraph styles, tables, pictures, lists, sections, notes,
216
223
  page breaks, header/footer/comment text) is refused (HTTP 422,
217
224
  `TrackedChangesUnsupported`) and applies nothing; reads, exports,
@@ -233,9 +240,11 @@ not file-backed. Each is documented in the relevant docstring.
233
240
 
234
241
  - **`Document.user_name: str | None` / `Document.user_email: str |
235
242
  None`** — the name sent with each batch, which DOCX Studio uses as the
236
- author of a comment that names none. It is not a tracked change's author:
237
- DOCX Studio stamps those with the signed-in user agora's access check
238
- names, so a script cannot choose who a redline's changes are by.
243
+ author of a comment that names none (unset, such a comment is by the
244
+ signed-in user, as "Jane Doe (Athena)"). It is not a tracked change's
245
+ author: DOCX Studio stamps those with the signed-in user agora's access
246
+ check names, as an agent writes for them ("Jane Doe (Athena)"), so a
247
+ script cannot choose who a redline's changes are by.
239
248
  Separate from `Document.core_properties.last_modified_by`, the
240
249
  `docProps/core.xml` metadata the SDK reads and writes as python-docx
241
250
  does.
@@ -451,6 +460,26 @@ Issue numbers reference `python-openxml/python-docx`.
451
460
  numbering reads. python-docx issue #471 (13 👍) demands access to
452
461
  list metadata. Routes through `NumberingGet`.
453
462
 
463
+ - **`Paragraph.delete()`** (0.30.0+) — delete the paragraph, its
464
+ paragraph mark included. python-docx has no paragraph delete (issue #33);
465
+ code written for it removes the element
466
+ (`p._element.getparent().remove(p._element)`), which this SDK cannot reach.
467
+ Routes through `DeleteParagraph`, sent in a request of its own after the
468
+ buffered edits (response-bearing, so a refusal raises at the call). With
469
+ tracking off docx-studio removes the paragraph, with the page and column
470
+ breaks the import keeps beside it; with `track_revisions` on it records
471
+ Word's tracked deletion of a whole paragraph: its content in `w:del`
472
+ (cross-reference and other fields with their codes and results, links,
473
+ note references) and its paragraph mark deleted, one revision. Accept All
474
+ removes it and the numbered paragraphs after it number on; Reject All
475
+ restores it. `paragraph.text = ""` leaves an empty, still numbered,
476
+ paragraph instead. docx-studio refuses (`DocxError`, nothing applied) the
477
+ document's last paragraph, one that ends a section, the only paragraph of
478
+ a table cell or content control, one holding part of a field that spans
479
+ paragraphs, one in a header, footer, comment or text box, and, tracked,
480
+ one in a table cell or holding a page break, a content control, a shape or
481
+ an equation (`DOCX_STUDIO_LIMITATIONS.md` § 32).
482
+
454
483
  - **`Paragraph.restart_numbering(start=1)`** (0.26.0+) — restart the
455
484
  paragraph's numbered list at `start`, as Word's "Restart at 1" does
456
485
  ("Set Numbering Value" for another start). All `"List Number"`
@@ -648,8 +677,11 @@ Issue numbers reference `python-openxml/python-docx`.
648
677
  leaves out the author, last-modified-by, revision, company, manager,
649
678
  template, editing-time and hyperlink-base properties, rsids, document
650
679
  variables, custom properties, Custom XML (a content control bound to it
651
- keeps its content, not its binding), the attached template and the
652
- thumbnail; ``clear_title=True`` drops the title too. The live document is
680
+ keeps its content, not its binding), the attached template, the attached
681
+ XML schema and the thumbnail; ``clear_title=True`` drops the title too.
682
+ Bookmarks, the creation date and the sensitivity label stay (the route's
683
+ ``removeSensitivityLabel=1`` drops the label; the SDK does not send it).
684
+ The live document is
653
685
  not changed. The SDK refuses (``DocxUnsupportedCommandError``) an answer
654
686
  without ``X-Docx-Clean-Copy: 1``, the plain export an older docx-studio
655
687
  sends, and raises ``DocxError`` with docx-studio's reason when a header,
@@ -954,13 +986,32 @@ architecture or a DOCX Studio constraint:
954
986
  `Table.autofit` reads `False` (python-docx leaves autofit on); its columns
955
987
  share 9,360 twips, where python-docx shares the section's text width; and
956
988
  in the fixed layout a column width the script sets sizes the column's
957
- cells too, where python-docx sets the `w:gridCol` alone. It also draws
958
- borders and a zebra fill, which no python-docx read sees. Recorded as
959
- `live_deviations` for the script cases' `tables.autofit`, `tables.widths`
960
- and `tables.cell_format` in `tests/parity/intentional_deviations.json`;
989
+ cells too, where python-docx sets the `w:gridCol` alone. Its cell
990
+ paragraphs carry their own spacing (`w:spacing w:after="0" w:line="240"
991
+ w:lineRule="auto"`: no space after, single lines), so a cell paragraph's
992
+ `paragraph_format.space_after` reads `0` and `line_spacing` `1.0`, where
993
+ python-docx's `add_table` writes none. It also draws borders and a zebra
994
+ fill, which no python-docx read sees. Recorded as `live_deviations` for
995
+ the script cases' `tables.autofit`, `tables.widths`, `tables.cell_format`
996
+ and `tables.cell_paragraph_format` in `tests/parity/intentional_deviations.json`;
961
997
  an imported table's gaps stay gaps, and so do the two cases whose
962
998
  `cell_format` also differs for another cause (`except`).
963
999
 
1000
+ - **A held `Table` raises `StaleTableError` once its table is gone.** The SDK
1001
+ holds the anchored ids docx-studio gives it (`t:N/R:C/K…@FIRST.LAST`: the
1002
+ table's place, then the paraIds of its first and last paragraphs), and
1003
+ docx-studio finds the table by those paragraphs, so a table an edit moved (a
1004
+ row added above a table nested in a cell, a table added before it, a merge
1005
+ moving a nested table into the merged cell) is still the one read and
1006
+ written, as python-docx's proxies follow the elements they hold. When no
1007
+ table holds them (the table was deleted, or the cell holding it had its
1008
+ text replaced, which removes the tables it held), docx-studio refuses the
1009
+ batch and the SDK raises `StaleTableError`: "the table this proxy named is
1010
+ gone; re-read `document.tables`". python-docx writes the detached element
1011
+ and changes nothing; raising tells the script instead of losing its write
1012
+ (coordinator, 2026-10-14). A held `_Cell` or `_Row` is still found by its
1013
+ place in its table.
1014
+
964
1015
  - **`_Cell.merge` raises `InvalidSpanError` for every span that would cut
965
1016
  through a merged cell.** It merges what python-docx merges and refuses
966
1017
  what python-docx refuses (an L- or T-shaped pair of corners), but where
@@ -990,8 +1041,9 @@ architecture or a DOCX Studio constraint:
990
1041
  `""`.** `Document.add_comment` / `Comments.add_comment` send `author`
991
1042
  and `initials` and docx-studio stores them as `w:author` /
992
1043
  `w:initials`, as python-docx does; with none (python-docx's default
993
- `""`) the author is `Document.user_name`, else `"Athena"`, and no
994
- initials are stored (`initials` then reads `None`). The SDK raises
1044
+ `""`) the author is `Document.user_name`, else the signed-in user agora's
1045
+ access check names as an agent writes for them, "Jane Doe (Athena)" (the
1046
+ author of the batch's tracked changes), else `"Athena"`, and no initials are stored (`initials` then reads `None`). The SDK raises
995
1047
  `ValueError` for what lxml refuses in an attribute, as python-docx
996
1048
  does, and also past docx-studio's caps (255 UTF-16 code units for an
997
1049
  author, 32 for initials); docx-studio turns tabs, line breaks and other
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: athena-python-docx
3
- Version: 0.29.0
3
+ Version: 0.31.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>
@@ -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.29.0"
9
+ __version__ = "0.31.0"
10
10
 
11
11
  from docx.api import Document
12
12
  from docx._buffer import discard_all, flush_all
@@ -102,6 +102,54 @@ def _apply_proxy_id_rewrites(
102
102
  pass
103
103
 
104
104
 
105
+ _WRITES_UNAPPLIED = "_docx_writes_unapplied"
106
+
107
+
108
+ def writes_unapplied(exc: BaseException) -> bool:
109
+ """True for the failure of a batch whose buffered writes docx-studio did
110
+ not apply (or may not have): a flush of them, a read they rode with, or
111
+ the idle timer's flush. A read that flushed them raises it rather than
112
+ fall back as it would for a failure of its own, so the script hears of
113
+ the write as python-docx would have failed at the write itself."""
114
+ return getattr(exc, _WRITES_UNAPPLIED, False) is True
115
+
116
+
117
+ def _unapplied_writes(exc: BaseException) -> BaseException:
118
+ """``exc``, marked as the failure of buffered writes (:func:`writes_unapplied`)."""
119
+ if not writes_unapplied(exc):
120
+ setattr(exc, _WRITES_UNAPPLIED, True)
121
+ exc.add_note("the buffered writes this call sent to docx-studio were not applied")
122
+ return exc
123
+
124
+
125
+ _REPORTED_ARGS = "_docx_reported_args"
126
+
127
+
128
+ def keep_reported_message(exc: BaseException) -> None:
129
+ """Keep ``exc``'s message as it was raised, before a flush's account of
130
+ what of it docx-studio applied is put ahead of it (``_flush_outcome``), so
131
+ that a read whose buffered writes were sent again on their own can raise
132
+ its failure as its own (:func:`_as_the_read_s_own_failure`)."""
133
+ if not hasattr(exc, _REPORTED_ARGS):
134
+ setattr(exc, _REPORTED_ARGS, exc.args)
135
+
136
+
137
+ def _as_the_read_s_own_failure(exc: BaseException, read: Command) -> None:
138
+ """``exc``, the failure of the read ``read`` whose buffered writes were
139
+ sent again on their own and applied, saying only that the read failed:
140
+ its message as docx-studio gave it, without the flush's account that the
141
+ writes did not land, ``read`` the one command that did not land
142
+ (``unapplied_commands``, ``failed_command``), and nothing applied before
143
+ it (``partial_applied``). A caller re-applying what the error names then
144
+ repeats no write."""
145
+ reported: object = getattr(exc, _REPORTED_ARGS, None)
146
+ if isinstance(reported, tuple):
147
+ exc.args = reported
148
+ exc.unapplied_commands = [read] # type: ignore[attr-defined]
149
+ exc.failed_command = read # type: ignore[attr-defined]
150
+ exc.partial_applied = [] # type: ignore[attr-defined]
151
+
152
+
105
153
  def _repeatable_read_key(cmd: Command) -> str | None:
106
154
  """The key a repeat of ``cmd`` answers from (:meth:`CommandBuffer.call`):
107
155
  a ``GetNodeById``'s wire form; ``None`` for any other command."""
@@ -677,9 +725,11 @@ class CommandBuffer:
677
725
  # See ``_apply_proxy_id_rewrites`` for the full rationale.
678
726
  _apply_proxy_id_rewrites(_extract_partial_results(exc), proxy_id_refs)
679
727
  _ptc_emit_end_batch(pending, is_error=True)
728
+ _unapplied_writes(exc)
680
729
  raise
681
- except Exception:
730
+ except Exception as exc:
682
731
  _ptc_emit_end_batch(pending, is_error=True)
732
+ _unapplied_writes(exc)
683
733
  raise
684
734
  finally:
685
735
  _forget_reads_of_others(self)
@@ -751,10 +801,17 @@ class CommandBuffer:
751
801
  # the trailing ``cmd`` would otherwise abandon every
752
802
  # buffered Create's rewrite at once.
753
803
  _apply_proxy_id_rewrites(_extract_partial_results(exc), proxy_id_refs)
754
- _ptc_emit_end_batch(all_cmds, is_error=True)
804
+ _ptc_emit_end_batch([cmd], is_error=True)
805
+ if pending:
806
+ self._settle_writes_of_failed_read(
807
+ exc, pending, change_mode=change_mode, user=user, proxy_id_refs=proxy_id_refs
808
+ )
755
809
  raise
756
- except Exception:
810
+ except Exception as exc:
757
811
  _ptc_emit_end_batch(all_cmds, is_error=True)
812
+ if pending:
813
+ # Whether the request landed is not known (a timeout, a dropped connection).
814
+ _unapplied_writes(exc)
758
815
  raise
759
816
  finally:
760
817
  # A batch of one node read changes nothing another buffer read.
@@ -788,6 +845,51 @@ class CommandBuffer:
788
845
  trailing = results[-1]
789
846
  return trailing if trailing is not None else {}
790
847
 
848
+ def _settle_writes_of_failed_read(
849
+ self,
850
+ exc: DocxError,
851
+ pending: list[Command],
852
+ *,
853
+ change_mode: str | None,
854
+ user: dict[str, str] | None,
855
+ proxy_id_refs: "dict[str, list[tuple[object, str]]]",
856
+ ) -> None:
857
+ """The buffered writes ``pending`` that rode with a read whose batch
858
+ failed (``exc``). docx-studio applies a batch all or nothing, so when
859
+ it reported the read itself failing, it applied none of the writes:
860
+ they are sent again on their own, and the read's failure stays its own
861
+ to report, as a read's fallback expects: once they landed, it names the
862
+ read alone as what did not land (:func:`_as_the_read_s_own_failure`).
863
+ Any other failure (one of the writes, or one not reported) marks
864
+ ``exc`` as one of unapplied writes (:func:`writes_unapplied`), and a
865
+ failure of the writes sent again is raised as one."""
866
+ unapplied = getattr(exc, "unapplied_commands", None)
867
+ failed = getattr(exc, "failed_command", None)
868
+ if not (isinstance(unapplied, list) and unapplied and failed is unapplied[-1]):
869
+ _ptc_emit_end_batch(pending, is_error=True)
870
+ _unapplied_writes(exc)
871
+ return
872
+ writes = unapplied[:-1]
873
+ if not writes:
874
+ # An older docx-studio kept the writes ahead of the read.
875
+ _ptc_emit_end_batch(pending, is_error=False)
876
+ return
877
+ try:
878
+ results, _skipped = self._client.execute_batch(
879
+ self._asset_id, writes, change_mode=change_mode, user=user
880
+ )
881
+ except Exception as again:
882
+ # The writes sent again go in requests of at most
883
+ # ``MAX_BATCH_COMMANDS``: those a failed request's earlier ones
884
+ # applied keep their ids, as a flush's do, and only the rest are
885
+ # the failure's unapplied writes.
886
+ _apply_proxy_id_rewrites(_extract_partial_results(again), proxy_id_refs)
887
+ _ptc_emit_end_batch(pending, is_error=True)
888
+ raise _unapplied_writes(again) from exc
889
+ _ptc_emit_end_batch(pending, is_error=False)
890
+ _apply_proxy_id_rewrites(results, proxy_id_refs)
891
+ _as_the_read_s_own_failure(exc, failed)
892
+
791
893
  def _reset_timer_locked(self) -> None:
792
894
  # Caller must hold self._lock.
793
895
  self._cancel_timer_locked()
@@ -852,6 +954,8 @@ class CommandBuffer:
852
954
  "raised by a background docx-sdk auto-flush that failed earlier; "
853
955
  "the commands in that batch were NOT applied",
854
956
  )
957
+ # Marked as unapplied writes by the flush (``_flush_in_order``):
958
+ # the read it surfaces in next raises it rather than fall back.
855
959
  with self._lock:
856
960
  self._deferred_flush_error = e
857
961
  sys.stderr.write(
@@ -14,6 +14,14 @@ from typing import Any
14
14
 
15
15
  from docx._batching import run_sync
16
16
 
17
+ # What this SDK declares in ``client.capabilities``: it holds the anchored table
18
+ # ids (``t:N/R:C/K…@FIRST.LAST``) a docx-studio gives a client declaring it, and
19
+ # sends them back as given. docx-studio then finds each held table by its
20
+ # paragraphs, wherever an edit moved it, and refuses the command with
21
+ # ``StaleTable`` when no one table holds them (``docx.errors.StaleTableError``).
22
+ # An older docx-studio gives none, so the SDK needs no check.
23
+ TABLE_ANCHORS_CLIENT_CAPABILITY = "tableAnchors"
24
+
17
25
  # Session -> the capabilities its server reported, read once.
18
26
  _capabilities: "weakref.WeakKeyDictionary[Any, dict[str, Any]]" = weakref.WeakKeyDictionary()
19
27
 
@@ -4,7 +4,8 @@ Wire format (matches ``apps/api/src/commands/types.ts``):
4
4
 
5
5
  POST /docs/:id/commands
6
6
  {
7
- "client": {"type": "python-sdk", "version": "<x.y.z>", "capabilities": ["trackedAuthoring"]},
7
+ "client": {"type": "python-sdk", "version": "<x.y.z>",
8
+ "capabilities": ["trackedAuthoring", "trackedMinimalParagraphText", "tableAnchors"]},
8
9
  "commands": [
9
10
  {"type": "CreateParagraph", "text": "Hi", "at": {"kind": "documentEnd"}},
10
11
  ...
@@ -41,8 +42,9 @@ from requests.adapters import HTTPAdapter
41
42
  from urllib3.util.retry import Retry
42
43
 
43
44
  from docx._batching import call_abandoned, note_progress
44
- from docx._buffer import CommandBuffer
45
- from docx._tracked import TRACKED_AUTHORING_CAPABILITY
45
+ from docx._buffer import CommandBuffer, keep_reported_message
46
+ from docx._tracked import TRACKED_AUTHORING_CAPABILITY, TRACKED_MINIMAL_PARAGRAPH_TEXT_CAPABILITY
47
+ from docx._capabilities import TABLE_ANCHORS_CLIENT_CAPABILITY
46
48
  from docx._timeouts import (
47
49
  DEFAULT_OP_TIMEOUT_SECONDS,
48
50
  LONG_RUNNING_COMMAND_TIMEOUT_SECONDS,
@@ -89,6 +91,7 @@ from docx.commands import (
89
91
  CreateTOC,
90
92
  CreateWatermark,
91
93
  DeleteBookmark,
94
+ DeleteParagraph,
92
95
  DeleteWatermark,
93
96
  EndnotesDelete,
94
97
  EndnotesGet,
@@ -192,6 +195,7 @@ from docx.errors import (
192
195
  NotFoundError,
193
196
  NotNumberedListItemError,
194
197
  SessionError,
198
+ StaleTableError,
195
199
  _missing_block_id,
196
200
  )
197
201
  from docx.exceptions import InvalidSpanError
@@ -282,6 +286,7 @@ def _flush_outcome(
282
286
  f" applied{not_sent}.{offered} Re-read the document to see whether that"
283
287
  " request landed before re-applying anything."
284
288
  )
289
+ keep_reported_message(exc)
285
290
  exc.args = (f"{prefix}\n\n{exc.args[0] if exc.args else ''}", *exc.args[1:])
286
291
  return exc
287
292
  applied = min(start + landed, total)
@@ -331,6 +336,7 @@ def _flush_outcome(
331
336
  f" nothing{not_sent}. `exc.unapplied_commands` holds the {len(unapplied)}"
332
337
  f" that did not land{which}. {redo}"
333
338
  )
339
+ keep_reported_message(exc)
334
340
  exc.args = (f"{prefix}\n\n{exc.args[0] if exc.args else ''}", *exc.args[1:])
335
341
  return exc
336
342
 
@@ -836,6 +842,13 @@ def _http_post_json(
836
842
  err_obj,
837
843
  partial_applied,
838
844
  )
845
+ # A held ``Table`` whose table is gone (``docx.table`` anchored ids).
846
+ if err_obj.get("error") == "StaleTable":
847
+ raise _reported(
848
+ StaleTableError(str(err_obj.get("message") or base_msg)),
849
+ err_obj,
850
+ partial_applied,
851
+ )
839
852
  # ``Paragraph.restart_numbering`` on a paragraph that takes no number.
840
853
  if err_obj.get("error") == "NotNumberedListItem":
841
854
  raise _reported(
@@ -1194,8 +1207,14 @@ class HttpClient:
1194
1207
  "client": {
1195
1208
  "type": "python-sdk",
1196
1209
  "version": _sdk_version(),
1197
- # Keeps its offsets as a tracked edit moves them (``docx._tracked``).
1198
- "capabilities": [TRACKED_AUTHORING_CAPABILITY],
1210
+ # Keeps its offsets as a tracked edit moves them, and reads a
1211
+ # paragraph's length back after a tracked ``paragraph.text``
1212
+ # (``docx._tracked``); holds anchored table ids (``docx.table``).
1213
+ "capabilities": [
1214
+ TRACKED_AUTHORING_CAPABILITY,
1215
+ TRACKED_MINIMAL_PARAGRAPH_TEXT_CAPABILITY,
1216
+ TABLE_ANCHORS_CLIENT_CAPABILITY,
1217
+ ],
1199
1218
  },
1200
1219
  "commands": [
1201
1220
  _with_real_ids(c.to_dict(), ids) if ids else c.to_dict() for c in commands
@@ -1475,6 +1494,7 @@ _OP_TO_COMMAND: dict[str, type[Command]] = {
1475
1494
  "sections.set_columns": SetSectionColumns,
1476
1495
  "find_replace": FindReplace,
1477
1496
  "clear_body": ClearBody,
1497
+ "delete_paragraph": DeleteParagraph,
1478
1498
  "find_text": FindText,
1479
1499
  "iter_runs": IterRuns,
1480
1500
  "export_pdf": ExportPDF,