athena-python-docx 0.31.0__tar.gz → 0.32.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 (457) hide show
  1. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/AGENTS.md +6 -0
  2. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/PKG-INFO +1 -1
  3. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/__init__.py +1 -1
  4. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/_run_order.py +39 -0
  5. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/commands.py +20 -4
  6. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/drawing/__init__.py +7 -2
  7. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/settings.py +12 -2
  8. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/table.py +33 -19
  9. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/text/paragraph.py +8 -1
  10. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/text/run.py +154 -14
  11. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/pyproject.toml +1 -1
  12. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/complex_cases.py +1 -1
  13. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/fake_session.py +2 -0
  14. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +1 -0
  15. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/live/baseline.json +6 -49
  16. athena_python_docx-0.32.0/tests/live/fixture_scripts.py +188 -0
  17. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_drawing_image.py +38 -0
  18. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_enum_parity.py +4 -2
  19. athena_python_docx-0.32.0/tests/test_run_break_inner_content.py +121 -0
  20. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_run_edges.py +25 -0
  21. athena_python_docx-0.32.0/tests/test_run_later_runs.py +580 -0
  22. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_settings.py +33 -0
  23. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_table_layout.py +8 -6
  24. athena_python_docx-0.32.0/tests/test_table_direction.py +102 -0
  25. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/uv.lock +1 -1
  26. athena_python_docx-0.31.0/tests/live/fixture_scripts.py +0 -68
  27. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/.gitignore +0 -0
  28. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/CLAUDE.md +0 -0
  29. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/DOCX_EXEC_LAB.md +0 -0
  30. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/README.md +0 -0
  31. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/_athena_extension.py +0 -0
  32. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/_batching.py +0 -0
  33. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/_buffer.py +0 -0
  34. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/_capabilities.py +0 -0
  35. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/_citations.py +0 -0
  36. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/_execution.py +0 -0
  37. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/_http.py +0 -0
  38. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/_http_doc.py +0 -0
  39. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/_image_utils.py +0 -0
  40. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/_postproc.py +0 -0
  41. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/_ptc.py +0 -0
  42. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/_references.py +0 -0
  43. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/_table_styles.py +0 -0
  44. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/_timeouts.py +0 -0
  45. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/_tracked.py +0 -0
  46. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/api.py +0 -0
  47. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/blkcntnr.py +0 -0
  48. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/bookmarks.py +0 -0
  49. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/charts.py +0 -0
  50. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/client.py +0 -0
  51. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/comments.py +0 -0
  52. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/document.py +0 -0
  53. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/enum/__init__.py +0 -0
  54. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/enum/base.py +0 -0
  55. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/enum/dml.py +0 -0
  56. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/enum/section.py +0 -0
  57. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/enum/shape.py +0 -0
  58. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/enum/style.py +0 -0
  59. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/enum/table.py +0 -0
  60. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/enum/text.py +0 -0
  61. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/errors.py +0 -0
  62. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/exceptions.py +0 -0
  63. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/fields.py +0 -0
  64. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/footnotes.py +0 -0
  65. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/image/__init__.py +0 -0
  66. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/image/exceptions.py +0 -0
  67. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/image/image.py +0 -0
  68. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/math.py +0 -0
  69. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/opc/__init__.py +0 -0
  70. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/opc/coreprops.py +0 -0
  71. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/oxml/__init__.py +0 -0
  72. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/revisions.py +0 -0
  73. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/sdt.py +0 -0
  74. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/section.py +0 -0
  75. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/session.py +0 -0
  76. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/shape.py +0 -0
  77. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/shared.py +0 -0
  78. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/styles/__init__.py +0 -0
  79. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/styles/_table.py +0 -0
  80. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/styles/latent.py +0 -0
  81. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/styles/style.py +0 -0
  82. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/styles/styles.py +0 -0
  83. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/text/__init__.py +0 -0
  84. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/text/font.py +0 -0
  85. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/text/hyperlink.py +0 -0
  86. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/text/pagebreak.py +0 -0
  87. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/text/parfmt.py +0 -0
  88. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/text/tabstops.py +0 -0
  89. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/toc.py +0 -0
  90. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/docx/typing.py +0 -0
  91. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/scripts/docx_exec_lab.py +0 -0
  92. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
  93. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
  94. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/scripts/docx_exec_lab_server.py +0 -0
  95. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/scripts/dump_wire_fixtures.py +0 -0
  96. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/scripts/release.sh +0 -0
  97. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/scripts/round_trip_smoke.py +0 -0
  98. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/scripts/smoke_test_block_not_found.py +0 -0
  99. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/scripts/validate_find_replace_asset.py +0 -0
  100. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/__init__.py +0 -0
  101. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/conftest.py +0 -0
  102. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/METHODOLOGY.md +0 -0
  103. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/README.md +0 -0
  104. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/__init__.py +0 -0
  105. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/ab_probe_cases.py +0 -0
  106. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/ab_probe_runner.py +0 -0
  107. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/auto_gen_cases.py +0 -0
  108. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/binary_round_trip.py +0 -0
  109. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/cases.py +0 -0
  110. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/coverage_report.py +0 -0
  111. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/extract.py +0 -0
  112. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/extreme_cases.py +0 -0
  113. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/firm_templates/README.md +0 -0
  114. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/firm_templates/__init__.py +0 -0
  115. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/firm_templates/_runner.py +0 -0
  116. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/firm_templates/extractor.py +0 -0
  117. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
  118. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
  119. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/local_runner.py +0 -0
  120. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/mega_cases.py +0 -0
  121. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshot.py +0 -0
  122. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
  123. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
  124. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
  125. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
  126. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
  127. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
  128. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
  129. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
  130. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
  131. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
  132. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
  133. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
  134. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
  135. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
  136. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
  137. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
  138. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
  139. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
  140. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
  141. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
  142. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
  143. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
  144. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
  145. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
  146. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
  147. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
  148. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
  149. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
  150. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
  151. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
  152. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
  153. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
  154. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
  155. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
  156. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
  157. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
  158. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
  159. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
  160. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
  161. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
  162. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
  163. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
  164. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
  165. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
  166. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
  167. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
  168. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
  169. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
  170. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
  171. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
  172. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
  173. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
  174. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
  175. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
  176. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
  177. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
  178. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
  179. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
  180. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
  181. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
  182. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
  183. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
  184. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
  185. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
  186. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
  187. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
  188. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
  189. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
  190. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
  191. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
  192. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
  193. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
  194. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
  195. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
  196. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
  197. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
  198. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
  199. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
  200. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
  201. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
  202. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
  203. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
  204. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
  205. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
  206. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
  207. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
  208. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
  209. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
  210. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
  211. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
  212. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
  213. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
  214. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
  215. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
  216. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
  217. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
  218. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
  219. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
  220. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
  221. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
  222. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
  223. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
  224. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
  225. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
  226. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
  227. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
  228. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
  229. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
  230. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
  231. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
  232. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
  233. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
  234. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
  235. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
  236. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
  237. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
  238. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
  239. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
  240. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
  241. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
  242. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
  243. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
  244. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
  245. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
  246. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
  247. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
  248. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
  249. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
  250. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
  251. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
  252. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
  253. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
  254. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
  255. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
  256. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
  257. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
  258. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
  259. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
  260. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
  261. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
  262. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
  263. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
  264. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
  265. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
  266. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
  267. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
  268. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/rw16_header_footer_tables.json +0 -0
  269. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/rw17_header_letterhead_filled.json +0 -0
  270. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/rw18_header_table_properties.json +0 -0
  271. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/rw19_header_table_rows_columns.json +0 -0
  272. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/op_snapshots/rw20_header_table_merges.json +0 -0
  273. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/ours_spec.json +0 -0
  274. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/parity_crawl.py +0 -0
  275. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/parity_diff.json +0 -0
  276. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/real_world_cases.py +0 -0
  277. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/round_trip_tests.py +0 -0
  278. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/stock_spec.json +0 -0
  279. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
  280. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fixtures/field_reads.json +0 -0
  281. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fixtures/note_reference_reads.json +0 -0
  282. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fixtures/ruby_reads.json +0 -0
  283. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/fixtures/symbol_reads.json +0 -0
  284. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/live/__init__.py +0 -0
  285. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/live/reader.py +0 -0
  286. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/live/runner.py +0 -0
  287. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/live/sdk_driver.py +0 -0
  288. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/live/stock_driver.py +0 -0
  289. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/live/test_live_runner.py +0 -0
  290. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/parity/README.md +0 -0
  291. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/parity/__init__.py +0 -0
  292. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/parity/baseline_gaps.json +0 -0
  293. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/parity/compare.py +0 -0
  294. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/parity/intentional_deviations.json +0 -0
  295. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/parity/introspect.py +0 -0
  296. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
  297. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/parity/reports/gap_report.json +0 -0
  298. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/parity/run_parity.py +0 -0
  299. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/parity/snapshots/athena_latest.json +0 -0
  300. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
  301. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/parity/test_parity_gap.py +0 -0
  302. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_add_section.py +0 -0
  303. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_add_section_extract_items.py +0 -0
  304. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_athena_extension_creates_session_open.py +0 -0
  305. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_athena_extensions_contract.py +0 -0
  306. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_athena_extensions_registry.py +0 -0
  307. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_batch_chunking.py +0 -0
  308. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_batching_perf.py +0 -0
  309. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_block_not_found_error.py +0 -0
  310. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_buffer.py +0 -0
  311. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
  312. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_cell_add_table_not_supported.py +0 -0
  313. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_cell_borders_spec.py +0 -0
  314. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_cell_fastpath_runs.py +0 -0
  315. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
  316. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
  317. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_cell_inner_format_stash.py +0 -0
  318. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_cell_inner_run_format_stash.py +0 -0
  319. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_cell_inner_run_guard.py +0 -0
  320. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_cell_merge.py +0 -0
  321. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_cell_nested_tables.py +0 -0
  322. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_cell_paragraphs_listed.py +0 -0
  323. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_cell_text_plain_fastpath.py +0 -0
  324. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_cell_text_replace_semantics.py +0 -0
  325. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_citations.py +0 -0
  326. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_collapsed_range_format.py +0 -0
  327. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_color_format_theme.py +0 -0
  328. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_command_dataclasses.py +0 -0
  329. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_commands.py +0 -0
  330. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_comment_paragraphs.py +0 -0
  331. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_comments.py +0 -0
  332. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_comments_first_read.py +0 -0
  333. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_content_control_value.py +0 -0
  334. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_core_properties.py +0 -0
  335. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_document_asset_id_property.py +0 -0
  336. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_document_clear.py +0 -0
  337. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_document_create.py +0 -0
  338. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_document_create_from_template.py +0 -0
  339. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_document_factory_validation.py +0 -0
  340. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_docx_exec_lab.py +0 -0
  341. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_docx_exec_lab_server.py +0 -0
  342. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
  343. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_empty_hyperlinks.py +0 -0
  344. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_execution_scope.py +0 -0
  345. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_export_docx_clean.py +0 -0
  346. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_export_rescue_anchors.py +0 -0
  347. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_field_reads_python_docx_parity.py +0 -0
  348. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_field_refresh.py +0 -0
  349. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_field_runs.py +0 -0
  350. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_find_replace_session_open.py +0 -0
  351. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_flushed_write_failures.py +0 -0
  352. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_http_transport.py +0 -0
  353. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_hyperlink_coalescing.py +0 -0
  354. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_image_url_data_uri.py +0 -0
  355. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_inline_page_break.py +0 -0
  356. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_inline_shape_alt_text.py +0 -0
  357. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_inline_shape_size.py +0 -0
  358. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_inline_shape_type.py +0 -0
  359. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_inline_shapes_floating.py +0 -0
  360. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_insert_deferred.py +0 -0
  361. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_iter_inner_content.py +0 -0
  362. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_list_styles.py +0 -0
  363. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_merged_cell_secondary_slot.py +0 -0
  364. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_merged_cells.py +0 -0
  365. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_missing_modules.py +0 -0
  366. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_nested_table_refused.py +0 -0
  367. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_note_reference_reads_python_docx_parity.py +0 -0
  368. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_oxml_shim.py +0 -0
  369. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_page_break.py +0 -0
  370. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_paragraph_delete.py +0 -0
  371. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_paragraph_runs_hyperlinks.py +0 -0
  372. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_paragraph_text_len_cache.py +0 -0
  373. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_parity_misc.py +0 -0
  374. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_parity_round2.py +0 -0
  375. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_partial_failure_cascade.py +0 -0
  376. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_pdf_export_and_page_renders.py +0 -0
  377. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_phase_a_behavior.py +0 -0
  378. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_phase_b_headers_footers.py +0 -0
  379. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_phase_c_tables.py +0 -0
  380. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_positional_cell_id.py +0 -0
  381. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
  382. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
  383. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_postproc_ref_restore.py +0 -0
  384. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_pr19766_review_fixes.py +0 -0
  385. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_ptc.py +0 -0
  386. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_python_docx_api_parity.py +0 -0
  387. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_read_run_ranges.py +0 -0
  388. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_repeated_node_read.py +0 -0
  389. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_restart_numbering.py +0 -0
  390. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_revisions.py +0 -0
  391. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_round_trip_smoke_script.py +0 -0
  392. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_ruby_reads_python_docx_parity.py +0 -0
  393. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_run_add_picture_anchor.py +0 -0
  394. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_run_formatting_wire.py +0 -0
  395. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_run_identity.py +0 -0
  396. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_run_mark_inheritance.py +0 -0
  397. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_run_table_style_objects.py +0 -0
  398. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_run_text_python_docx.py +0 -0
  399. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_run_text_refreshes_paragraph.py +0 -0
  400. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_sdk_reads_served.py +0 -0
  401. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_section_read_lengths.py +0 -0
  402. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
  403. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_add_picture.py +0 -0
  404. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_add_run.py +0 -0
  405. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
  406. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
  407. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_comments_get.py +0 -0
  408. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_document_audit.py +0 -0
  409. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_document_element.py +0 -0
  410. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_enum_section.py +0 -0
  411. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_font_audit.py +0 -0
  412. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_header_footer.py +0 -0
  413. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_hyperlink.py +0 -0
  414. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_inline_shape.py +0 -0
  415. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
  416. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_misc.py +0 -0
  417. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
  418. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_paragraph_style.py +0 -0
  419. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
  420. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_parfmt.py +0 -0
  421. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_row_col_cell.py +0 -0
  422. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_run_add_break.py +0 -0
  423. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
  424. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_run_style.py +0 -0
  425. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_run_style_strict.py +0 -0
  426. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_run_text.py +0 -0
  427. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_run_underline.py +0 -0
  428. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_section_audit.py +0 -0
  429. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_section_dimensions.py +0 -0
  430. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_section_onoff.py +0 -0
  431. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_shared_audit.py +0 -0
  432. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_style.py +0 -0
  433. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_styles.py +0 -0
  434. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_table_audit.py +0 -0
  435. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_table_cell.py +0 -0
  436. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_silent_stub_table_dimensions.py +0 -0
  437. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_smoke_integration.py +0 -0
  438. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_style_acceptance.py +0 -0
  439. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_style_font.py +0 -0
  440. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_style_formatting_writes.py +0 -0
  441. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_style_metadata_writes.py +0 -0
  442. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_style_names.py +0 -0
  443. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_style_setters_contract.py +0 -0
  444. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_style_tab_stops_read.py +0 -0
  445. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_symbol_reads_python_docx_parity.py +0 -0
  446. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_table_anchors.py +0 -0
  447. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_table_read_reuse.py +0 -0
  448. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_table_set_cell_perf.py +0 -0
  449. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_table_style_id_resolution.py +0 -0
  450. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_temporarily_unavailable.py +0 -0
  451. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_text_control_characters.py +0 -0
  452. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_tracked_revisions_offsets.py +0 -0
  453. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_uv_lock_version.py +0 -0
  454. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_validate_find_replace_asset_script.py +0 -0
  455. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_wire_contract.py +0 -0
  456. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_xml_attr_guard.py +0 -0
  457. {athena_python_docx-0.31.0 → athena_python_docx-0.32.0}/tests/test_zod_wire_contract.py +0 -0
@@ -107,6 +107,12 @@ pinned by focused tests and documented in
107
107
  `horz_dpi`, `width`, `scaled_dimensions`, …). A picture in a table cell or
108
108
  a header has no id to read its image by, so `.image` raises `ValueError`;
109
109
  an older docx-studio does not report pictures, so they are not runs there.
110
+ - **A run holding only a page or column break yields `""` from
111
+ `Run.iter_inner_content()`,** as python-docx reads a `w:br` of type `page`
112
+ or `column` as empty text; an empty run (a comment reference's) yields
113
+ nothing. docx-studio's read marks such a run with `break`, and
114
+ `Run.add_break` marks the run it adds one to. Against an older docx-studio,
115
+ which sends no `break`, a re-read run yields nothing.
110
116
  - **`Styles.latent_styles`** (`docx.styles.latent.LatentStyles`) reads
111
117
  the document's `<w:latentStyles>` (defaults and each `w:lsdException`)
112
118
  from docx-studio's `Info` (`includeStyles` → `latentStyles`), as python-docx
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: athena-python-docx
3
- Version: 0.31.0
3
+ Version: 0.32.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.31.0"
9
+ __version__ = "0.32.0"
10
10
 
11
11
  from docx.api import Document
12
12
  from docx._buffer import discard_all, flush_all
@@ -332,6 +332,45 @@ def note_picture(run: "Run", picture_id: str, offset: int, *, before: str | None
332
332
  record.pictures.insert(index, (picture_id, offset))
333
333
 
334
334
 
335
+ def read_stamp() -> int:
336
+ """The moment a read gives its runs (``Paragraph.runs``): a run ``add_run``
337
+ creates after it has a higher ``seq``."""
338
+ return next(_SEQ)
339
+
340
+
341
+ def later_runs_at_end(run: "Run") -> int | None:
342
+ """How many runs after ``run`` end where it does, by which docx-studio
343
+ tells ``run`` from them (``run.laterRuns``) where the offset cannot: an
344
+ empty run ``add_run()`` made after it, or one of breaks alone, ends there
345
+ too.
346
+
347
+ For a run ``add_run`` created, the runs it created after it; for a run a
348
+ read gave (``Paragraph.runs``), the later runs of that read and the runs
349
+ ``add_run`` created since. Each where it ends now, not where it ended at
350
+ the read: a write through a run moves the live proxies (:func:`track_run`),
351
+ as clearing a run of text after a run of a break leaves an empty run that
352
+ ends where the break's run does. A read run holds the next run of its
353
+ read (``_next_read``), so the later ones stay live, and current, while it
354
+ is. None for a run proxy that is neither, which the SDK cannot tell from
355
+ the runs around it.
356
+ """
357
+ end = run._range[1]
358
+ records = _records_of(run)
359
+ own = getattr(run, "_run_record", None) or getattr(run, "_owner_record", None)
360
+ if own is not None and own in records:
361
+ return sum(1 for other in records[records.index(own) + 1 :] if other.range[1] == end)
362
+ read_seq = getattr(run, "_read_seq", None)
363
+ if read_seq is None:
364
+ return None
365
+ later = 0
366
+ following: Run | None = getattr(run, "_next_read", None)
367
+ while following is not None:
368
+ if following._range[1] == end:
369
+ later += 1
370
+ following = getattr(following, "_next_read", None)
371
+ return later + sum(1 for other in records if other.seq > read_seq and other.range[1] == end)
372
+
373
+
335
374
  def ends_last_at_its_end(run: "Run") -> bool:
336
375
  """True when no run ``add_run`` created after ``run`` ends where it does.
337
376
 
@@ -223,6 +223,10 @@ class Replace(Command):
223
223
  # …``), whose own properties docx-studio keeps for the new text; an older
224
224
  # docx-studio ignores it.
225
225
  whole_run: bool | None = None
226
+ # An empty run's identity among the runs at its place (``{"laterRuns": n}``,
227
+ # as ``Insert.run``), sent to a docx-studio reporting
228
+ # ``capabilities.runIdentity``.
229
+ run: dict[str, Any] | None = None
226
230
 
227
231
 
228
232
  @dataclass
@@ -233,13 +237,17 @@ class InsertLineBreak(Command):
233
237
  # ``True`` gives the break the formatting of the run it ends
234
238
  # (``Run.add_break``); an older docx-studio ignores it.
235
239
  inherit_marks: bool | None = None
236
- # ``"column"``: a column break (``Run.add_break(WD_BREAK.COLUMN)``), which
237
- # docx-studio writes after the paragraph's content, outside its
238
- # text-address space; an older docx-studio ignores it and inserts a line
239
- # break.
240
+ # ``"column"``: a column break (``Run.add_break(WD_BREAK.COLUMN)``),
241
+ # outside the paragraph's text-address space: with ``inline``, at the end
242
+ # of the run, in it; otherwise after the paragraph's content. An older
243
+ # docx-studio ignores it and inserts a line break.
240
244
  break_type: str | None = None
241
245
  # As ``Insert.run_edge``.
242
246
  run_edge: str | None = None
247
+ # ``True`` with ``break_type="column"``: the break at the end of the run,
248
+ # in it, as python-docx writes it. Sent only to a docx-studio reporting
249
+ # ``capabilities.inlineColumnBreak``; an older one ignores it.
250
+ inline: bool | None = None
243
251
 
244
252
 
245
253
  @dataclass
@@ -261,6 +269,10 @@ class FormatApply(Command):
261
269
  # (``run.bold = True``), which docx-studio keeps apart with their new
262
270
  # formatting; an older docx-studio ignores it.
263
271
  whole_run: bool | None = None
272
+ # The identity of an empty run that holds a page or column break
273
+ # (``{"laterRuns": n}``, as ``Insert.run``): its breaks take the
274
+ # formatting. Sent to a docx-studio reporting ``capabilities.runIdentity``.
275
+ run: dict[str, Any] | None = None
264
276
 
265
277
 
266
278
  @dataclass
@@ -725,6 +737,10 @@ class Info(Command):
725
737
  # ``docProps/core.xml``. An older docx-studio ignores the field, which
726
738
  # tells the SDK it has no ``SetCoreProperties``.
727
739
  include_core_properties: bool | None = None
740
+ # ``True`` also returns ``settings``: ``evenAndOddHeaders``, the
741
+ # document's ``w:evenAndOddHeaders`` in ``word/settings.xml``. An older
742
+ # docx-studio ignores the field and sends none.
743
+ include_settings: bool | None = None
728
744
 
729
745
 
730
746
  @dataclass
@@ -29,9 +29,12 @@ class Drawing:
29
29
  def has_picture(self) -> bool:
30
30
  """True when `drawing` contains an embedded picture.
31
31
 
32
- A drawing docx-studio reports in a run is always a picture.
32
+ As python-docx: False for a drawing whose graphic is not a picture (a
33
+ chart, a diagram, a group), which docx-studio reports with
34
+ ``hasPicture: false``. An older docx-studio reports none, and every
35
+ drawing it reports reads as a picture.
33
36
  """
34
- return True
37
+ return self._info.get("hasPicture") is not False
35
38
 
36
39
  @property
37
40
  def image(self) -> "Image":
@@ -44,6 +47,8 @@ class Drawing:
44
47
  from docx.image.image import Image
45
48
  from docx.shape import _strip_data_uri
46
49
 
50
+ if not self.has_picture:
51
+ raise ValueError("drawing does not contain a picture")
47
52
  image_id = self._info.get("imageId")
48
53
  if not isinstance(image_id, str) or not image_id:
49
54
  raise ValueError("drawing image cannot be read: docx-studio reported no id for it")
@@ -4,8 +4,9 @@ Most Word app settings are not surfaced by DOCX Studio, so this proxy is
4
4
  intentionally minimal. The fields that ARE exposed have one-to-one
5
5
  DOCX Studio semantics:
6
6
 
7
- * ``odd_and_even_pages_header_footer`` — toggles the document-level
8
- ``<w:evenAndOddHeaders/>`` flag via DOCX Studio 1.8.1's
7
+ * ``odd_and_even_pages_header_footer`` — reads the document-level
8
+ ``<w:evenAndOddHeaders/>`` flag from ``Info`` (``includeSettings``) and
9
+ toggles it via DOCX Studio 1.8.1's
9
10
  ``doc.sections.setOddEvenHeadersFooters`` (doc-level despite its
10
11
  namespace — see ``commands.SetSectionsOddEvenHeadersFooters``).
11
12
  * ``track_revisions`` — alias for :attr:`docx.document.Document.track_revisions`.
@@ -50,6 +51,15 @@ class Settings:
50
51
 
51
52
  @property
52
53
  def odd_and_even_pages_header_footer(self) -> bool:
54
+ """The document's ``w:evenAndOddHeaders`` in ``word/settings.xml``, as
55
+ python-docx reads it (False when absent or off). A docx-studio that
56
+ does not report it (``Info`` without ``settings``) gives the value this
57
+ proxy last set."""
58
+ info: object = run_sync(self._session.doc.info({"includeSettings": True}))
59
+ settings: object = info.get("settings") if isinstance(info, dict) else None
60
+ reported: object = settings.get("evenAndOddHeaders") if isinstance(settings, dict) else None
61
+ if isinstance(reported, bool):
62
+ return reported
53
63
  return self._odd_and_even_pages_header_footer
54
64
 
55
65
  @odd_and_even_pages_header_footer.setter
@@ -23,9 +23,11 @@ class PendingTableClearWarning(UserWarning):
23
23
  setter is asked to clear a property (``None`` assignment) that
24
24
  python-docx 1.x supports but DOCX Studio 1.8 has no clear-op for.
25
25
 
26
- Covers: ``Table.alignment / direction / autofit``,
26
+ Covers: ``Table.alignment / autofit``,
27
27
  ``_Row.height / height_rule``, ``_Column.width``,
28
- ``_Cell.width / vertical_alignment``. DOCX Studio's
28
+ ``_Cell.width / vertical_alignment``, and any ``Table.table_direction``
29
+ write to a docx-studio that does not report
30
+ ``capabilities.tableDirection`` (one that does writes None too). DOCX Studio's
29
31
  ``tables.set_layout`` / ``set_row_height`` / ``set_column_width``
30
32
  / ``set_cell_properties`` require explicit values — the wire
31
33
  can't represent the python-docx "revert to inherited" intent.
@@ -1241,36 +1243,48 @@ class Table:
1241
1243
  )
1242
1244
 
1243
1245
  @property
1244
- def direction(self) -> "WD_TABLE_DIRECTION | None":
1245
- from docx.enum.table import WD_TABLE_DIRECTION
1246
-
1246
+ def table_direction(self) -> "WD_TABLE_DIRECTION | None":
1247
+ """The table's ``w:bidiVisual`` as python-docx 1.2.0 reads it: ``True``
1248
+ (right to left), ``False`` (an explicit left to right) or None (none),
1249
+ the ``bool`` python-docx returns behind its ``WD_TABLE_DIRECTION`` hint
1250
+ (``True == WD_TABLE_DIRECTION.RTL``)."""
1247
1251
  info: object = run_sync(
1248
1252
  self._session.doc.tables.get_properties({"nodeId": self._fresh_node_id()}),
1249
1253
  )
1250
- if isinstance(info, dict):
1251
- return WD_TABLE_DIRECTION.from_docx_studio(info.get("direction"))
1254
+ direction: object = info.get("direction") if isinstance(info, dict) else None
1255
+ if direction == "rtl":
1256
+ return cast("WD_TABLE_DIRECTION", True)
1257
+ if direction == "ltr":
1258
+ return cast("WD_TABLE_DIRECTION", False)
1252
1259
  return None
1253
1260
 
1254
- @direction.setter
1255
- def direction(self, value: "WD_TABLE_DIRECTION | int | str | None") -> None:
1261
+ @table_direction.setter
1262
+ def table_direction(self, value: "WD_TABLE_DIRECTION | int | str | None") -> None:
1263
+ """As python-docx 1.2.0: RTL writes ``w:bidiVisual``, LTR writes
1264
+ ``w:bidiVisual w:val="0"`` and None removes it. A docx-studio that does
1265
+ not report ``capabilities.tableDirection`` drops the write, so the SDK
1266
+ sends none there and warns (``PendingTableClearWarning``)."""
1267
+ from docx._capabilities import server_takes
1268
+ from docx.commands import NULL
1256
1269
  from docx.enum.table import WD_TABLE_DIRECTION
1257
1270
 
1258
- if value is None:
1259
- _warn_table_clear("direction")
1271
+ # None goes on the wire as JSON null (``NULL``): a ``None`` field is left off it.
1272
+ wire: object = NULL if value is None else WD_TABLE_DIRECTION._wire(value, "Table.table_direction")
1273
+ if not server_takes(self._session, "tableDirection"):
1274
+ warnings.warn(
1275
+ "Table.table_direction was IGNORED: this DOCX Studio does not write a table's "
1276
+ "direction (w:bidiVisual); the table keeps its previous direction.",
1277
+ PendingTableClearWarning,
1278
+ stacklevel=2,
1279
+ )
1260
1280
  return
1261
- v: str = WD_TABLE_DIRECTION._wire(value, "Table.direction")
1262
- # DOCX Studio's setLayout takes ``tableDirection`` (not
1263
- # ``direction``). Same diagnostic as alignment above —
1264
- # setTableOptions silently swallowed the field.
1281
+ # DOCX Studio's setLayout takes ``tableDirection`` (not ``direction``).
1265
1282
  run_sync(
1266
1283
  self._session.doc.tables.set_layout(
1267
- {"nodeId": self._fresh_node_id(), "tableDirection": v},
1284
+ {"nodeId": self._fresh_node_id(), "tableDirection": wire},
1268
1285
  ),
1269
1286
  )
1270
1287
 
1271
- # python-docx aliases
1272
- table_direction = direction
1273
-
1274
1288
  @property
1275
1289
  def autofit(self) -> bool:
1276
1290
  info: object = run_sync(
@@ -9,7 +9,7 @@ from typing import TYPE_CHECKING, Any
9
9
  from docx._athena_extension import athena_extension
10
10
  from docx._batching import run_sync
11
11
  from docx._buffer import writes_unapplied
12
- from docx._run_order import forget_paragraph, register_run
12
+ from docx._run_order import forget_paragraph, read_stamp, register_run
13
13
  from docx._tracked import tracking
14
14
 
15
15
  #: The session attribute ``Paragraph.delete`` bumps, after which every
@@ -818,6 +818,13 @@ class Paragraph:
818
818
  # cache for free (avoids a follow-up ``get_node_by_id`` if the
819
819
  # caller does ``p.runs[...]; p.add_run(...)``).
820
820
  self._text_len = offset
821
+ # Each run holds the next one of this read: docx-studio tells it from
822
+ # the later ones that end where it does when content goes to it, by
823
+ # where they end then (``docx._run_order.later_runs_at_end``).
824
+ stamp = read_stamp()
825
+ for index, run_obj in enumerate(runs):
826
+ run_obj._next_read = runs[index + 1] if index + 1 < len(runs) else None
827
+ run_obj._read_seq = stamp
821
828
  return runs
822
829
 
823
830
  @property
@@ -10,6 +10,7 @@ from docx._batching import run_sync
10
10
  from docx._capabilities import server_takes
11
11
  from docx._run_order import (
12
12
  ends_last_at_its_end,
13
+ later_runs_at_end,
13
14
  note_length_change,
14
15
  note_picture,
15
16
  note_tracked_replacement,
@@ -401,12 +402,47 @@ class Run:
401
402
 
402
403
  def _mark_append(self, command: dict) -> dict:
403
404
  """Mark content added at the end of this run as this run's
404
- (``runEdge: "append"``), which docx-studio keeps one run, when the
405
- offset names it: no later run ends there. Returns ``command``."""
406
- if ends_last_at_its_end(self):
405
+ (``runEdge: "append"``), which docx-studio keeps one run. Where later
406
+ runs end where this one does (an empty run, or one of breaks alone),
407
+ to a docx-studio that reports ``capabilities.runIdentity``, with how
408
+ many (``run.laterRuns``), so the content goes into this run; to an
409
+ older one, only when the offset names it: no later run ends there.
410
+ The server is asked only then. Returns ``command``."""
411
+ later = later_runs_at_end(self)
412
+ if later and server_takes(self._session, "runIdentity"):
413
+ command["runEdge"] = "append"
414
+ command["run"] = {**(command.get("run") or {}), "laterRuns": later}
415
+ elif ends_last_at_its_end(self):
407
416
  command["runEdge"] = "append"
408
417
  return command
409
418
 
419
+ def _later_runs(self) -> int | None:
420
+ """The runs after this one that end where it does, by which a
421
+ docx-studio reporting ``capabilities.runIdentity`` tells this run from
422
+ them (``docx._run_order.later_runs_at_end``); None for an older one, or
423
+ a run the SDK cannot tell from the runs around it."""
424
+ if not server_takes(self._session, "runIdentity"):
425
+ return None
426
+ return later_runs_at_end(self)
427
+
428
+ def _break_run_identity(self) -> int | None:
429
+ """This empty run's identity (:meth:`_later_runs`) while it holds a
430
+ page or column break, which a whole-run ``FormatApply`` of its
431
+ collapsed range formats, untracked; else None. A column break only in
432
+ a room that reports ``capabilities.inlineColumnBreak``: one imported
433
+ before keeps it as a block after the paragraph, which takes no
434
+ formatting, so the formatting waits for content the run takes. So
435
+ does a break the read says is no node of the paragraph
436
+ (``breakApart``): one only its source run holds, a column-break
437
+ block's, or the page break that opens the paragraph."""
438
+ if not self._is_collapsed_range or not self._holds_break() or tracking(self._session):
439
+ return None
440
+ if self._inline_snapshot.get("breakApart") is True:
441
+ return None
442
+ if self._inline_snapshot.get("break") == "column" and not server_takes(self._session, "inlineColumnBreak"):
443
+ return None
444
+ return self._later_runs()
445
+
410
446
  def _add_run_ref(self, command: dict) -> dict:
411
447
  """Add the run identity of an insert at the end of this run.
412
448
 
@@ -415,7 +451,7 @@ class Run:
415
451
  """
416
452
  ref = run_ref(self)
417
453
  if ref is not None:
418
- command["run"] = ref
454
+ command["run"] = {**(command.get("run") or {}), **ref}
419
455
  return command
420
456
 
421
457
  def _note_length_change(
@@ -580,9 +616,28 @@ class Run:
580
616
  set, so we ``getattr`` it.
581
617
  """
582
618
  pending = getattr(self, "_pending_inline", None)
583
- if not pending or self._is_collapsed_range:
619
+ if not pending:
620
+ return
621
+ # An empty run that holds a page or column break: docx-studio formats
622
+ # the break's run, which it tells by the run's identity.
623
+ identity = self._break_run_identity()
624
+ if self._is_collapsed_range and identity is None:
584
625
  return
585
626
  self._pending_inline = {}
627
+ if identity is not None:
628
+ run_sync(
629
+ self._session.doc.format.apply(
630
+ {
631
+ "target": self._text_range,
632
+ "inline": pending,
633
+ "wholeRun": True,
634
+ "run": {"laterRuns": identity},
635
+ },
636
+ ),
637
+ )
638
+ # Its break holds the formatting now, which what it takes next continues.
639
+ self._fresh = False
640
+ return
586
641
  # An emptied run's kept formatting turns off each toggle it did not
587
642
  # read (``_formatting_from_snapshot``), only so that its next text takes
588
643
  # none from the paragraph or the run before it. Those are no
@@ -669,7 +724,9 @@ class Run:
669
724
  )
670
725
 
671
726
  drawing = self._inline_snapshot.get("drawing")
672
- if isinstance(drawing, dict):
727
+ # A legacy VML picture or an OLE object (``w:pict``, ``w:object``) is no
728
+ # ``w:drawing``: python-docx yields nothing for it.
729
+ if isinstance(drawing, dict) and drawing.get("legacy") is not True:
673
730
  from docx.drawing import Drawing
674
731
 
675
732
  # The picture is at the run's start: text added to the run follows it.
@@ -682,6 +739,11 @@ class Run:
682
739
  if "\f" not in text:
683
740
  if _word_text(text, font):
684
741
  yield _word_text(text, font)
742
+ elif self._inline_snapshot.get("break") in ("page", "column"):
743
+ # python-docx reads a page or column break as text, an empty
744
+ # string: a run holding one and no other text yields "". An
745
+ # empty run (a comment reference's) yields nothing.
746
+ yield ""
685
747
  return
686
748
  # Split on hard page-break sentinel; yield text segments and
687
749
  # RenderedPageBreak proxies between them. The proxy carries the
@@ -752,6 +814,16 @@ class Run:
752
814
  self._flush_pending_inline()
753
815
  return
754
816
  if self._is_collapsed_range:
817
+ # python-docx replaces all of the run's content, its page or
818
+ # column break too: an empty run holding one has it removed first.
819
+ if self._holds_break() and self._replaces_breaks():
820
+ clear: dict = {"target": self._text_range, "text": "", "wholeRun": True}
821
+ # Which of the runs at its place this one is, where more than one holds a break.
822
+ later = self._later_runs()
823
+ if later is not None:
824
+ clear["run"] = {"laterRuns": later}
825
+ run_sync(self._session.doc.replace(clear))
826
+ self._forget_break()
755
827
  # An empty run has nothing to replace: the text is appended to
756
828
  # it, with the formatting rule of ``add_text``.
757
829
  if value and tracking(self._session) and any(ch in "\t\r\n" for ch in value):
@@ -814,6 +886,9 @@ class Run:
814
886
  # write replaces the run's ``w:footnoteReference`` too (``""`` included),
815
887
  # and the export then leaves out the note nothing references.
816
888
  getattr(self, "_inline_snapshot", {}).pop("noteReference", None)
889
+ # The run's page and column breaks went with its text.
890
+ if self._holds_break() and not tracked and self._replaces_breaks():
891
+ self._forget_break()
817
892
  if tracked:
818
893
  self._note_tracked_replacement(old_range, value)
819
894
  else:
@@ -929,6 +1004,41 @@ class Run:
929
1004
  # any pending inline formatting now that there's text to mark.
930
1005
  self._flush_pending_inline()
931
1006
 
1007
+ def _holds_break(self) -> bool:
1008
+ """Whether this run holds a page or column break (``break``, as
1009
+ docx-studio's read of it or :meth:`add_break` noted it)."""
1010
+ snapshot: object = getattr(self, "_inline_snapshot", None)
1011
+ return isinstance(snapshot, dict) and snapshot.get("break") in ("page", "column")
1012
+
1013
+ def _replaces_breaks(self) -> bool:
1014
+ """Whether a ``wholeRun`` Replace of this run replaces its page and
1015
+ column breaks too, as python-docx's ``run.text = …`` and
1016
+ ``run.clear()`` do: on a docx-studio that reports
1017
+ ``capabilities.wholeRunBreaks``, without tracked changes (a tracked
1018
+ replacement keeps the breaks). An older one keeps a break at the run's
1019
+ edge, and the proxy keeps it too."""
1020
+ return not tracking(self._session) and server_takes(self._session, "wholeRunBreaks")
1021
+
1022
+ def _forget_break(self) -> None:
1023
+ """The run holds no page or column break now: its ``break`` goes, and
1024
+ whether the paragraph held it (``breakApart``) with it."""
1025
+ self._inline_snapshot.pop("break", None)
1026
+ self._inline_snapshot.pop("breakApart", None)
1027
+
1028
+ def _note_break(self, kind: str, *, held: bool) -> None:
1029
+ """Record that this run holds a page or column break (``kind``), as
1030
+ docx-studio's read of it says (``break``), so that
1031
+ :meth:`iter_inner_content` yields python-docx's ``""`` for it while it
1032
+ has no text. A break the paragraph holds (``held``: one ``add_break``
1033
+ put in the run, inline) is one a format of the run reaches, with the
1034
+ break apart from the paragraph it may hold too, as docx-studio then
1035
+ reads the run: no longer ``breakApart``."""
1036
+ snapshot: object = getattr(self, "_inline_snapshot", None)
1037
+ if isinstance(snapshot, dict):
1038
+ snapshot.setdefault("break", kind)
1039
+ if held:
1040
+ snapshot.pop("breakApart", None)
1041
+
932
1042
  def add_break(self, break_type: "WD_BREAK" = WD_BREAK.LINE) -> None:
933
1043
  """Insert a break at the end of the run.
934
1044
 
@@ -949,11 +1059,13 @@ class Run:
949
1059
  matching ``breakType``
950
1060
  - ``LINE`` → ``doc.insertLineBreak`` (a real Word line-break node)
951
1061
  - ``COLUMN`` → ``doc.insertLineBreak`` with ``breakType: "column"``:
952
- a ``<w:br w:type="column"/>`` that docx-studio writes after the
953
- paragraph's content (where python-docx ends this run with it), so
954
- it takes no room in the paragraph's text-address space and reads
955
- as ``""``, as python-docx reads it. A docx-studio before column
956
- breaks inserts a line break instead.
1062
+ the ``<w:br w:type="column"/>`` at the end of the run, in it, as
1063
+ python-docx writes it, when the server reports
1064
+ ``capabilities.inlineColumnBreak`` in ``Info``. It takes no room in
1065
+ the paragraph's text-address space, and a run holding nothing else
1066
+ yields ``""``, as python-docx's does. An older server writes it
1067
+ after the paragraph's content, as a run of its own; one before
1068
+ column breaks inserts a line break instead.
957
1069
  - ``LINE_CLEAR_LEFT`` / ``LINE_CLEAR_RIGHT`` / ``LINE_CLEAR_ALL``
958
1070
  (``TEXT_WRAPPING`` is python-docx's alias of ``LINE_CLEAR_ALL``) —
959
1071
  DOCX Studio has no dedicated wire op for these, so they degrade to
@@ -1002,6 +1114,9 @@ class Run:
1002
1114
  if self._continues_own_formatting():
1003
1115
  page_break["inheritMarks"] = True
1004
1116
  run_sync(self._session.doc.insert_line_break(page_break))
1117
+ self._note_break("page", held=True)
1118
+ # Formatting set on the empty run before its break: the break's run takes it.
1119
+ self._flush_pending_inline()
1005
1120
  return
1006
1121
 
1007
1122
  if bt is WD_BREAK.PAGE:
@@ -1040,9 +1155,17 @@ class Run:
1040
1155
  return
1041
1156
 
1042
1157
  if bt is WD_BREAK.COLUMN:
1043
- column_break = self._add_run_ref({"blockId": self._block_id, "offset": end})
1044
- column_break["breakType"] = "column"
1045
- run_sync(self._session.doc.insert_line_break(column_break))
1158
+ column_break: dict = {"blockId": self._block_id, "offset": end, "breakType": "column"}
1159
+ if server_takes(self._session, "inlineColumnBreak"):
1160
+ # At the end of this run, in it, as python-docx writes it.
1161
+ column_break["inline"] = True
1162
+ self._mark_append(column_break)
1163
+ if self._continues_own_formatting():
1164
+ column_break["inheritMarks"] = True
1165
+ run_sync(self._session.doc.insert_line_break(self._add_run_ref(column_break)))
1166
+ self._note_break("column", held=column_break.get("inline") is True)
1167
+ # Formatting set on the empty run before its break: the break's run takes it.
1168
+ self._flush_pending_inline()
1046
1169
  return
1047
1170
 
1048
1171
  if bt in degraded_to_line:
@@ -1603,6 +1726,23 @@ class Run:
1603
1726
  )
1604
1727
  return
1605
1728
  self._note_inline_applied(kwargs)
1729
+ identity = self._break_run_identity()
1730
+ if identity is not None:
1731
+ # The run holds only a page or column break: python-docx formats its
1732
+ # ``w:r``, and docx-studio its break, which it tells by the run's identity.
1733
+ run_sync(
1734
+ self._session.doc.format.apply(
1735
+ {
1736
+ "target": self._text_range,
1737
+ "inline": kwargs,
1738
+ "wholeRun": True,
1739
+ "run": {"laterRuns": identity},
1740
+ },
1741
+ ),
1742
+ )
1743
+ # Its break holds the formatting now, which what it takes next continues.
1744
+ self._fresh = False
1745
+ return
1606
1746
  if self._is_collapsed_range:
1607
1747
  pending = getattr(self, "_pending_inline", None)
1608
1748
  if pending is None:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "athena-python-docx"
7
- version = "0.31.0"
7
+ version = "0.32.0"
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"
@@ -299,7 +299,7 @@ CASES: list[ComplexCase] = [
299
299
  "from docx.enum.table import WD_TABLE_ALIGNMENT, WD_TABLE_DIRECTION\n"
300
300
  "t = doc.add_table(rows=2, cols=2)\n"
301
301
  "t.alignment = WD_TABLE_ALIGNMENT.CENTER\n"
302
- "t.direction = WD_TABLE_DIRECTION.LTR\n"
302
+ "t.table_direction = WD_TABLE_DIRECTION.LTR\n"
303
303
  "t.autofit = True"
304
304
  ),
305
305
  expected_ops=("tables.setTableOptions", "tables.setLayout"),
@@ -248,6 +248,8 @@ class FakeDocState:
248
248
  info["latentStyles"] = self.latent_styles
249
249
  if (params or {}).get("includeCoreProperties") and self.core_properties is not None:
250
250
  info["coreProperties"] = dict(self.core_properties)
251
+ # `tables.setLayout` below writes `tableDirection`, as a docx-studio reporting it does.
252
+ info["capabilities"] = {"tableDirection": True}
251
253
  return info
252
254
  return {"ok": True}
253
255
 
@@ -2,6 +2,7 @@
2
2
  "create.table",
3
3
  "tables.get",
4
4
  "tables.setLayout",
5
+ "info",
5
6
  "tables.get",
6
7
  "tables.setLayout",
7
8
  "tables.get",