athena-python-docx 0.32.0__tar.gz → 0.33.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 (458) hide show
  1. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/PKG-INFO +1 -1
  2. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/__init__.py +1 -1
  3. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/text/hyperlink.py +7 -4
  4. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/text/pagebreak.py +65 -18
  5. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/text/paragraph.py +42 -12
  6. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/text/run.py +60 -6
  7. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/pyproject.toml +1 -1
  8. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fixtures/field_reads.json +1 -0
  9. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/live/baseline.json +1 -59
  10. athena_python_docx-0.33.0/tests/test_rendered_page_breaks.py +196 -0
  11. athena_python_docx-0.33.0/tests/test_run_unmodeled_toggles.py +86 -0
  12. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/uv.lock +1 -1
  13. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/.gitignore +0 -0
  14. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/AGENTS.md +0 -0
  15. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/CLAUDE.md +0 -0
  16. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/DOCX_EXEC_LAB.md +0 -0
  17. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/README.md +0 -0
  18. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/_athena_extension.py +0 -0
  19. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/_batching.py +0 -0
  20. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/_buffer.py +0 -0
  21. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/_capabilities.py +0 -0
  22. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/_citations.py +0 -0
  23. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/_execution.py +0 -0
  24. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/_http.py +0 -0
  25. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/_http_doc.py +0 -0
  26. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/_image_utils.py +0 -0
  27. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/_postproc.py +0 -0
  28. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/_ptc.py +0 -0
  29. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/_references.py +0 -0
  30. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/_run_order.py +0 -0
  31. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/_table_styles.py +0 -0
  32. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/_timeouts.py +0 -0
  33. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/_tracked.py +0 -0
  34. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/api.py +0 -0
  35. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/blkcntnr.py +0 -0
  36. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/bookmarks.py +0 -0
  37. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/charts.py +0 -0
  38. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/client.py +0 -0
  39. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/commands.py +0 -0
  40. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/comments.py +0 -0
  41. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/document.py +0 -0
  42. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/drawing/__init__.py +0 -0
  43. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/enum/__init__.py +0 -0
  44. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/enum/base.py +0 -0
  45. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/enum/dml.py +0 -0
  46. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/enum/section.py +0 -0
  47. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/enum/shape.py +0 -0
  48. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/enum/style.py +0 -0
  49. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/enum/table.py +0 -0
  50. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/enum/text.py +0 -0
  51. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/errors.py +0 -0
  52. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/exceptions.py +0 -0
  53. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/fields.py +0 -0
  54. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/footnotes.py +0 -0
  55. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/image/__init__.py +0 -0
  56. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/image/exceptions.py +0 -0
  57. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/image/image.py +0 -0
  58. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/math.py +0 -0
  59. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/opc/__init__.py +0 -0
  60. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/opc/coreprops.py +0 -0
  61. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/oxml/__init__.py +0 -0
  62. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/revisions.py +0 -0
  63. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/sdt.py +0 -0
  64. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/section.py +0 -0
  65. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/session.py +0 -0
  66. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/settings.py +0 -0
  67. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/shape.py +0 -0
  68. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/shared.py +0 -0
  69. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/styles/__init__.py +0 -0
  70. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/styles/_table.py +0 -0
  71. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/styles/latent.py +0 -0
  72. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/styles/style.py +0 -0
  73. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/styles/styles.py +0 -0
  74. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/table.py +0 -0
  75. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/text/__init__.py +0 -0
  76. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/text/font.py +0 -0
  77. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/text/parfmt.py +0 -0
  78. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/text/tabstops.py +0 -0
  79. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/toc.py +0 -0
  80. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/docx/typing.py +0 -0
  81. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/scripts/docx_exec_lab.py +0 -0
  82. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
  83. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
  84. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/scripts/docx_exec_lab_server.py +0 -0
  85. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/scripts/dump_wire_fixtures.py +0 -0
  86. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/scripts/release.sh +0 -0
  87. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/scripts/round_trip_smoke.py +0 -0
  88. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/scripts/smoke_test_block_not_found.py +0 -0
  89. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/scripts/validate_find_replace_asset.py +0 -0
  90. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/__init__.py +0 -0
  91. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/conftest.py +0 -0
  92. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/METHODOLOGY.md +0 -0
  93. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/README.md +0 -0
  94. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/__init__.py +0 -0
  95. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/ab_probe_cases.py +0 -0
  96. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/ab_probe_runner.py +0 -0
  97. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/auto_gen_cases.py +0 -0
  98. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/binary_round_trip.py +0 -0
  99. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/cases.py +0 -0
  100. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/complex_cases.py +0 -0
  101. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/coverage_report.py +0 -0
  102. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/extract.py +0 -0
  103. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/extreme_cases.py +0 -0
  104. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/fake_session.py +0 -0
  105. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/firm_templates/README.md +0 -0
  106. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/firm_templates/__init__.py +0 -0
  107. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/firm_templates/_runner.py +0 -0
  108. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/firm_templates/extractor.py +0 -0
  109. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
  110. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
  111. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/local_runner.py +0 -0
  112. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/mega_cases.py +0 -0
  113. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshot.py +0 -0
  114. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
  115. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
  116. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
  117. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
  118. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
  119. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
  120. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
  121. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
  122. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
  123. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
  124. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
  125. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
  126. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
  127. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
  128. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
  129. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
  130. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
  131. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
  132. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
  133. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
  134. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
  135. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
  136. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
  137. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
  138. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
  139. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
  140. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
  141. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
  142. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
  143. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
  144. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
  145. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
  146. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
  147. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
  148. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
  149. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
  150. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
  151. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
  152. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
  153. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
  154. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
  155. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
  156. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
  157. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
  158. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
  159. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
  160. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
  161. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
  162. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
  163. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
  164. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
  165. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
  166. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
  167. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
  168. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
  169. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
  170. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
  171. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
  172. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
  173. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
  174. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
  175. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
  176. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
  177. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
  178. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
  179. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
  180. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
  181. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
  182. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
  183. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
  184. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
  185. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
  186. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
  187. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
  188. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
  189. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
  190. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
  191. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
  192. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
  193. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
  194. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
  195. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
  196. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
  197. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
  198. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
  199. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
  200. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
  201. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
  202. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
  203. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
  204. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
  205. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
  206. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
  207. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
  208. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
  209. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
  210. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
  211. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
  212. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
  213. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
  214. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
  215. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
  216. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
  217. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
  218. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
  219. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
  220. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
  221. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
  222. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
  223. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
  224. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
  225. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
  226. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
  227. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
  228. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
  229. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
  230. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
  231. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
  232. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
  233. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
  234. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
  235. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
  236. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
  237. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
  238. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
  239. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
  240. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
  241. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
  242. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
  243. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
  244. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
  245. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
  246. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
  247. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
  248. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
  249. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
  250. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
  251. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
  252. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
  253. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
  254. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
  255. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
  256. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
  257. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
  258. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
  259. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
  260. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
  261. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/rw16_header_footer_tables.json +0 -0
  262. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/rw17_header_letterhead_filled.json +0 -0
  263. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/rw18_header_table_properties.json +0 -0
  264. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/rw19_header_table_rows_columns.json +0 -0
  265. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/op_snapshots/rw20_header_table_merges.json +0 -0
  266. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/ours_spec.json +0 -0
  267. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/parity_crawl.py +0 -0
  268. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/parity_diff.json +0 -0
  269. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/real_world_cases.py +0 -0
  270. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/round_trip_tests.py +0 -0
  271. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/stock_spec.json +0 -0
  272. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
  273. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fixtures/note_reference_reads.json +0 -0
  274. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fixtures/ruby_reads.json +0 -0
  275. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/fixtures/symbol_reads.json +0 -0
  276. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/live/__init__.py +0 -0
  277. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/live/fixture_scripts.py +0 -0
  278. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/live/reader.py +0 -0
  279. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/live/runner.py +0 -0
  280. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/live/sdk_driver.py +0 -0
  281. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/live/stock_driver.py +0 -0
  282. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/live/test_live_runner.py +0 -0
  283. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/parity/README.md +0 -0
  284. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/parity/__init__.py +0 -0
  285. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/parity/baseline_gaps.json +0 -0
  286. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/parity/compare.py +0 -0
  287. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/parity/intentional_deviations.json +0 -0
  288. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/parity/introspect.py +0 -0
  289. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
  290. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/parity/reports/gap_report.json +0 -0
  291. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/parity/run_parity.py +0 -0
  292. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/parity/snapshots/athena_latest.json +0 -0
  293. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
  294. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/parity/test_parity_gap.py +0 -0
  295. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_add_section.py +0 -0
  296. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_add_section_extract_items.py +0 -0
  297. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_athena_extension_creates_session_open.py +0 -0
  298. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_athena_extensions_contract.py +0 -0
  299. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_athena_extensions_registry.py +0 -0
  300. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_batch_chunking.py +0 -0
  301. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_batching_perf.py +0 -0
  302. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_block_not_found_error.py +0 -0
  303. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_buffer.py +0 -0
  304. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
  305. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_cell_add_table_not_supported.py +0 -0
  306. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_cell_borders_spec.py +0 -0
  307. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_cell_fastpath_runs.py +0 -0
  308. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
  309. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
  310. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_cell_inner_format_stash.py +0 -0
  311. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_cell_inner_run_format_stash.py +0 -0
  312. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_cell_inner_run_guard.py +0 -0
  313. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_cell_merge.py +0 -0
  314. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_cell_nested_tables.py +0 -0
  315. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_cell_paragraphs_listed.py +0 -0
  316. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_cell_text_plain_fastpath.py +0 -0
  317. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_cell_text_replace_semantics.py +0 -0
  318. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_citations.py +0 -0
  319. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_collapsed_range_format.py +0 -0
  320. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_color_format_theme.py +0 -0
  321. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_command_dataclasses.py +0 -0
  322. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_commands.py +0 -0
  323. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_comment_paragraphs.py +0 -0
  324. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_comments.py +0 -0
  325. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_comments_first_read.py +0 -0
  326. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_content_control_value.py +0 -0
  327. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_core_properties.py +0 -0
  328. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_document_asset_id_property.py +0 -0
  329. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_document_clear.py +0 -0
  330. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_document_create.py +0 -0
  331. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_document_create_from_template.py +0 -0
  332. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_document_factory_validation.py +0 -0
  333. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_docx_exec_lab.py +0 -0
  334. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_docx_exec_lab_server.py +0 -0
  335. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_drawing_image.py +0 -0
  336. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
  337. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_empty_hyperlinks.py +0 -0
  338. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_enum_parity.py +0 -0
  339. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_execution_scope.py +0 -0
  340. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_export_docx_clean.py +0 -0
  341. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_export_rescue_anchors.py +0 -0
  342. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_field_reads_python_docx_parity.py +0 -0
  343. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_field_refresh.py +0 -0
  344. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_field_runs.py +0 -0
  345. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_find_replace_session_open.py +0 -0
  346. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_flushed_write_failures.py +0 -0
  347. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_http_transport.py +0 -0
  348. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_hyperlink_coalescing.py +0 -0
  349. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_image_url_data_uri.py +0 -0
  350. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_inline_page_break.py +0 -0
  351. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_inline_shape_alt_text.py +0 -0
  352. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_inline_shape_size.py +0 -0
  353. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_inline_shape_type.py +0 -0
  354. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_inline_shapes_floating.py +0 -0
  355. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_insert_deferred.py +0 -0
  356. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_iter_inner_content.py +0 -0
  357. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_list_styles.py +0 -0
  358. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_merged_cell_secondary_slot.py +0 -0
  359. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_merged_cells.py +0 -0
  360. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_missing_modules.py +0 -0
  361. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_nested_table_refused.py +0 -0
  362. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_note_reference_reads_python_docx_parity.py +0 -0
  363. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_oxml_shim.py +0 -0
  364. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_page_break.py +0 -0
  365. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_paragraph_delete.py +0 -0
  366. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_paragraph_runs_hyperlinks.py +0 -0
  367. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_paragraph_text_len_cache.py +0 -0
  368. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_parity_misc.py +0 -0
  369. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_parity_round2.py +0 -0
  370. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_partial_failure_cascade.py +0 -0
  371. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_pdf_export_and_page_renders.py +0 -0
  372. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_phase_a_behavior.py +0 -0
  373. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_phase_b_headers_footers.py +0 -0
  374. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_phase_c_tables.py +0 -0
  375. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_positional_cell_id.py +0 -0
  376. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
  377. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
  378. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_postproc_ref_restore.py +0 -0
  379. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_pr19766_review_fixes.py +0 -0
  380. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_ptc.py +0 -0
  381. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_python_docx_api_parity.py +0 -0
  382. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_read_run_ranges.py +0 -0
  383. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_repeated_node_read.py +0 -0
  384. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_restart_numbering.py +0 -0
  385. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_revisions.py +0 -0
  386. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_round_trip_smoke_script.py +0 -0
  387. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_ruby_reads_python_docx_parity.py +0 -0
  388. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_run_add_picture_anchor.py +0 -0
  389. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_run_break_inner_content.py +0 -0
  390. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_run_edges.py +0 -0
  391. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_run_formatting_wire.py +0 -0
  392. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_run_identity.py +0 -0
  393. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_run_later_runs.py +0 -0
  394. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_run_mark_inheritance.py +0 -0
  395. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_run_table_style_objects.py +0 -0
  396. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_run_text_python_docx.py +0 -0
  397. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_run_text_refreshes_paragraph.py +0 -0
  398. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_sdk_reads_served.py +0 -0
  399. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_section_read_lengths.py +0 -0
  400. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
  401. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_add_picture.py +0 -0
  402. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_add_run.py +0 -0
  403. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
  404. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
  405. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_comments_get.py +0 -0
  406. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_document_audit.py +0 -0
  407. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_document_element.py +0 -0
  408. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_enum_section.py +0 -0
  409. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_font_audit.py +0 -0
  410. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_header_footer.py +0 -0
  411. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_hyperlink.py +0 -0
  412. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_inline_shape.py +0 -0
  413. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
  414. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_misc.py +0 -0
  415. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
  416. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_paragraph_style.py +0 -0
  417. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
  418. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_parfmt.py +0 -0
  419. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_row_col_cell.py +0 -0
  420. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_run_add_break.py +0 -0
  421. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
  422. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_run_style.py +0 -0
  423. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_run_style_strict.py +0 -0
  424. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_run_text.py +0 -0
  425. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_run_underline.py +0 -0
  426. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_section_audit.py +0 -0
  427. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_section_dimensions.py +0 -0
  428. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_section_onoff.py +0 -0
  429. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_settings.py +0 -0
  430. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_shared_audit.py +0 -0
  431. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_style.py +0 -0
  432. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_styles.py +0 -0
  433. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_table_audit.py +0 -0
  434. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_table_cell.py +0 -0
  435. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_table_dimensions.py +0 -0
  436. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_silent_stub_table_layout.py +0 -0
  437. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_smoke_integration.py +0 -0
  438. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_style_acceptance.py +0 -0
  439. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_style_font.py +0 -0
  440. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_style_formatting_writes.py +0 -0
  441. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_style_metadata_writes.py +0 -0
  442. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_style_names.py +0 -0
  443. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_style_setters_contract.py +0 -0
  444. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_style_tab_stops_read.py +0 -0
  445. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_symbol_reads_python_docx_parity.py +0 -0
  446. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_table_anchors.py +0 -0
  447. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_table_direction.py +0 -0
  448. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_table_read_reuse.py +0 -0
  449. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_table_set_cell_perf.py +0 -0
  450. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_table_style_id_resolution.py +0 -0
  451. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_temporarily_unavailable.py +0 -0
  452. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_text_control_characters.py +0 -0
  453. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_tracked_revisions_offsets.py +0 -0
  454. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_uv_lock_version.py +0 -0
  455. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_validate_find_replace_asset_script.py +0 -0
  456. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_wire_contract.py +0 -0
  457. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_xml_attr_guard.py +0 -0
  458. {athena_python_docx-0.32.0 → athena_python_docx-0.33.0}/tests/test_zod_wire_contract.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: athena-python-docx
3
- Version: 0.32.0
3
+ Version: 0.33.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.32.0"
9
+ __version__ = "0.33.0"
10
10
 
11
11
  from docx.api import Document
12
12
  from docx._buffer import discard_all, flush_all
@@ -171,13 +171,16 @@ class Hyperlink:
171
171
 
172
172
  @property
173
173
  def contains_page_break(self) -> bool:
174
- """True when the text of this hyperlink contains a hard page break.
174
+ """True when a rendered page break (a page Word's last layout began)
175
+ occurs in one of the hyperlink's runs, as python-docx 1.x reads it.
175
176
 
176
- Mirrors python-docx 1.x. Detection uses DOCX Studio's ``\\f`` form-
177
- feed sentinel inside any wrapped run's text (matching
178
- ``Run.contains_page_break`` / ``Paragraph.contains_page_break``).
177
+ docx-studio reads them in the runs (``renderedPageBreaks``); the
178
+ ``\\f`` sentinel is an older docx-studio's mark.
179
179
  """
180
180
  for run_data, _ in self._segments:
181
+ breaks = run_data.get("renderedPageBreaks")
182
+ if isinstance(breaks, list) and any(isinstance(brk, dict) for brk in breaks):
183
+ return True
181
184
  text = run_data.get("text", "")
182
185
  if isinstance(text, str) and "\f" in text:
183
186
  return True
@@ -53,15 +53,45 @@ def _warn_pagebreak_fragment(prop: str) -> None:
53
53
  )
54
54
 
55
55
 
56
+ class _ParagraphFragment:
57
+ """The part of a paragraph before or after one of its rendered page breaks.
58
+
59
+ python-docx returns a ``Paragraph`` divorced from the document body: a
60
+ copy of the paragraph cut at the break, whose changes the document never
61
+ sees. This one reads what text extraction asks of it, its ``text`` and
62
+ ``style``, and changes nothing.
63
+ """
64
+
65
+ __slots__ = ("_paragraph", "_text")
66
+
67
+ def __init__(self, *, paragraph: "Paragraph", text: str) -> None:
68
+ self._paragraph = paragraph
69
+ self._text = text
70
+
71
+ @property
72
+ def text(self) -> str:
73
+ """The fragment's text, as ``Paragraph.text`` reads it."""
74
+ return self._text
75
+
76
+ @property
77
+ def style(self):
78
+ """The paragraph's style: the fragment keeps the paragraph's properties."""
79
+ return self._paragraph.style
80
+
81
+ def __repr__(self) -> str:
82
+ return f"<_ParagraphFragment text={self._text!r}>"
83
+
84
+
56
85
  class RenderedPageBreak:
57
- """A hard page break inside a paragraph, as Word would render it.
86
+ """A page break Word's last layout put in a paragraph (``w:lastRenderedPageBreak``).
58
87
 
59
- Constructed by :meth:`Paragraph.rendered_page_breaks` and yielded
60
- by :meth:`Paragraph.iter_inner_content`; not intended for direct
61
- user instantiation.
88
+ Constructed by :meth:`Paragraph.rendered_page_breaks` and yielded by
89
+ :meth:`Run.iter_inner_content`; not intended for direct user
90
+ instantiation. ``info`` is the break as docx-studio reads it in its run
91
+ (``renderedPageBreaks``): its fragments' text, or no fragment.
62
92
  """
63
93
 
64
- __slots__ = ("_paragraph", "_offset", "_preceding_text")
94
+ __slots__ = ("_paragraph", "_offset", "_preceding_text", "_info")
65
95
 
66
96
  def __init__(
67
97
  self,
@@ -69,30 +99,47 @@ class RenderedPageBreak:
69
99
  paragraph: "Paragraph",
70
100
  offset: int = 0,
71
101
  preceding_text: str = "",
102
+ info: "dict | None" = None,
72
103
  ) -> None:
73
104
  self._paragraph: "Paragraph" = paragraph
74
105
  self._offset: int = offset
75
106
  self._preceding_text: str = preceding_text
107
+ self._info: "dict | None" = info
108
+
109
+ def _fragment(self, side: str) -> "_ParagraphFragment | None":
110
+ """python-docx's fragment on `side` (``preceding``/``following``) of this break."""
111
+ info = self._info
112
+ if info is None:
113
+ _warn_pagebreak_fragment(f"{side}_paragraph_fragment")
114
+ return None
115
+ if side not in info:
116
+ raise ValueError("only defined on first rendered page-break in paragraph")
117
+ text = info[side]
118
+ return (
119
+ None if text is None else _ParagraphFragment(paragraph=self._paragraph, text=str(text))
120
+ )
76
121
 
77
122
  # ---- python-docx canonical surface ----
78
123
  @property
79
- def preceding_paragraph_fragment(self) -> "Paragraph | None":
80
- """The portion of the paragraph that flowed onto the page
81
- *before* the break.
124
+ def preceding_paragraph_fragment(self) -> "_ParagraphFragment | None":
125
+ """The part of the paragraph before this break, as a paragraph.
82
126
 
83
- DOCX Studio's flat model doesn't preserve mid-paragraph fragments;
84
- returns ``None``. Use :attr:`preceding_paragraph_text` for the
85
- plain-text equivalent or the parent paragraph for full text.
127
+ None when no content precedes it (the break precedes all of the
128
+ paragraph's content). A break in a hyperlink puts the whole hyperlink
129
+ in this fragment. Raises ``ValueError`` for a break that is not the
130
+ paragraph's first, as python-docx does.
86
131
  """
87
- _warn_pagebreak_fragment("preceding_paragraph_fragment")
88
- return None
132
+ return self._fragment("preceding")
89
133
 
90
134
  @property
91
- def following_paragraph_fragment(self) -> "Paragraph | None":
92
- """The portion of the paragraph that flowed onto the page
93
- *after* the break. See :attr:`preceding_paragraph_fragment`."""
94
- _warn_pagebreak_fragment("following_paragraph_fragment")
95
- return None
135
+ def following_paragraph_fragment(self) -> "_ParagraphFragment | None":
136
+ """The part of the paragraph after this break, as a paragraph.
137
+
138
+ None when no content follows it. A break in a hyperlink leaves the
139
+ whole hyperlink out of this fragment. Raises ``ValueError`` for a
140
+ break that is not the paragraph's first, as python-docx does.
141
+ """
142
+ return self._fragment("following")
96
143
 
97
144
  # ---- athena-specific extensions (kept for compat) ----
98
145
  @property
@@ -242,6 +242,30 @@ def _node_text(session: "Session", node_id: str, *, final: bool = False) -> str:
242
242
  return _extract_text(info, final=final)
243
243
 
244
244
 
245
+ def _rendered_page_break_reads(info: object) -> "list[tuple[dict, int]]":
246
+ """Each rendered page break docx-studio reads in the runs of the paragraph
247
+ whose node info is `info` (``renderedPageBreaks``), its links' included, in
248
+ order, with its offset in the paragraph's text-address space."""
249
+ if not isinstance(info, dict):
250
+ return []
251
+ node: object = info.get("node")
252
+ block: object = (node.get("paragraph") or node.get("heading")) if isinstance(node, dict) else None
253
+ inlines: object = block.get("inlines") if isinstance(block, dict) else None
254
+ reads: list[tuple[dict, int]] = []
255
+ offset = 0
256
+ for inline in inlines if isinstance(inlines, list) else []:
257
+ run: object = inline.get("run") if isinstance(inline, dict) else None
258
+ if not isinstance(run, dict):
259
+ continue
260
+ breaks: object = run.get("renderedPageBreaks")
261
+ for brk in breaks if isinstance(breaks, list) else []:
262
+ if isinstance(brk, dict):
263
+ reads.append((brk, offset + int(brk.get("offset", 0) or 0)))
264
+ text: object = run.get("text")
265
+ offset += len(text) if isinstance(text, str) else 0
266
+ return reads
267
+
268
+
245
269
  def _extract_text(info: object, *, final: bool = False) -> str:
246
270
  """Concatenate the text content of a paragraph, mirroring
247
271
  python-docx ``Paragraph.text`` semantics:
@@ -1129,22 +1153,28 @@ class Paragraph:
1129
1153
 
1130
1154
  @property
1131
1155
  def contains_page_break(self) -> bool:
1132
- """python-docx 1.x: True if the paragraph contains a hard page break."""
1133
- return "\f" in self.text
1156
+ """python-docx 1.x: True when a rendered page break (a page Word's last
1157
+ layout began) occurs in one of the paragraph's runs or its links' runs."""
1158
+ info = self._node_info()
1159
+ return bool(_rendered_page_break_reads(info)) or "\f" in _extract_text(info, final=True)
1134
1160
 
1135
1161
  @property
1136
1162
  def rendered_page_breaks(self) -> "list[RenderedPageBreak]":
1137
- """Return a list of :class:`RenderedPageBreak` proxies for each
1138
- hard page break in this paragraph.
1139
-
1140
- python-docx 1.x exposes this for inspecting Word's rendered
1141
- page-break positions inside a paragraph. We surface a minimal
1142
- proxy with a ``preceding_paragraph_text`` property so the
1143
- common ``len(p.rendered_page_breaks)`` pattern works. The
1144
- underlying break is detected via the ``\\f`` sentinel in the
1145
- paragraph text.
1163
+ """python-docx 1.x: a :class:`RenderedPageBreak` for each page Word's
1164
+ last layout began in the paragraph's runs and its links' runs
1165
+ (``w:lastRenderedPageBreak``), in document order.
1166
+
1167
+ Each reads python-docx's fragments: the part of the paragraph before
1168
+ and after the break (``preceding_paragraph_fragment``,
1169
+ ``following_paragraph_fragment``), defined on the first break only.
1170
+ A paragraph from an older docx-studio marks its breaks with the
1171
+ ``\\f`` sentinel in its text instead.
1146
1172
  """
1147
- text = _extract_text(self._node_info())
1173
+ node_info = self._node_info()
1174
+ reads = _rendered_page_break_reads(node_info)
1175
+ if reads:
1176
+ return [RenderedPageBreak(paragraph=self, offset=offset, info=info) for info, offset in reads]
1177
+ text = _extract_text(node_info)
1148
1178
  if "\f" not in text:
1149
1179
  return []
1150
1180
  out: list[RenderedPageBreak] = []
@@ -48,11 +48,26 @@ _RUN_MARK_TOGGLES: tuple[str, ...] = (
48
48
  "outline",
49
49
  "rtl",
50
50
  )
51
+ # The on/off properties a read reports that no mark holds: ``w:bCs``, ``w:iCs``,
52
+ # ``w:cs``, ``w:noProof``, ``w:snapToGrid``, ``w:webHidden``, ``w:specVanish``
53
+ # and ``w:oMath`` (``math``), which docx-studio keeps among the run's own
54
+ # properties. An older docx-studio reports none of them.
55
+ _RUN_UNMODELED_TOGGLES: tuple[str, ...] = (
56
+ "bCs",
57
+ "iCs",
58
+ "cs",
59
+ "noProof",
60
+ "snapToGrid",
61
+ "webHidden",
62
+ "specVanish",
63
+ "math",
64
+ )
51
65
  # Every formatting key of such a read: the toggles, and the value keys
52
66
  # (``fontSize`` in points, ``vertAlign``, the character style id ``rStyle``).
53
67
  _RUN_MARK_KEYS: frozenset[str] = frozenset(
54
68
  (
55
69
  *_RUN_MARK_TOGGLES,
70
+ *_RUN_UNMODELED_TOGGLES,
56
71
  "color",
57
72
  "themeColor",
58
73
  "highlight",
@@ -735,6 +750,37 @@ class Run:
735
750
  return
736
751
  text = _without_ruby(self._raw_text(), self._inline_snapshot)
737
752
  font = self._symbol_font() if _SYMBOL_CHARACTERS.search(text) else None
753
+ rendered = self._rendered_page_breaks()
754
+ if rendered and "\f" not in text:
755
+ # Word's rendered page breaks stand between the run's text, each where
756
+ # docx-studio reads it: python-docx yields the text before it, the
757
+ # break, then the text after. A page or column break holding no text
758
+ # reads as "" before them, as the page it begins starts after it.
759
+ from docx.text.paragraph import Paragraph
760
+
761
+ if not text and self._inline_snapshot.get("break") in ("page", "column"):
762
+ yield ""
763
+ paragraph = getattr(self, "_paragraph", None) or Paragraph(session=self._session, node_id=self._block_id)
764
+ # The offsets count the run's text with its ruby base text, which
765
+ # each piece leaves out after it is cut.
766
+ raw = self._raw_text()
767
+ spans = self._inline_snapshot.get("ruby")
768
+ ruby = [s for s in spans if isinstance(s, list) and len(s) == 2] if isinstance(spans, list) else []
769
+
770
+ def piece(start: int, end: int) -> str:
771
+ inside = [[a - start, b - start] for a, b in ruby if isinstance(a, int) and isinstance(b, int) and a >= start and b <= end]
772
+ return _word_text(_without_ruby(raw[start:end], {"ruby": inside}), font)
773
+
774
+ cut = 0
775
+ for info in rendered:
776
+ at = min(max(int(info.get("offset", 0) or 0), cut), len(raw))
777
+ if piece(cut, at):
778
+ yield piece(cut, at)
779
+ yield RenderedPageBreak(paragraph=paragraph, offset=self._range[0] + at, info=info)
780
+ cut = at
781
+ if piece(cut, len(raw)):
782
+ yield piece(cut, len(raw))
783
+ return
738
784
  # Fast path — no page breaks, yield the whole run as one string.
739
785
  if "\f" not in text:
740
786
  if _word_text(text, font):
@@ -785,6 +831,8 @@ class Run:
785
831
  f"{value!r}",
786
832
  )
787
833
  if self._field_part() is not None:
834
+ # python-docx replaces all of the run's content, its rendered page breaks too.
835
+ self._inline_snapshot.pop("renderedPageBreaks", None)
788
836
  self._set_field_text(value)
789
837
  return
790
838
  if self._check_cell_inner("text"):
@@ -792,6 +840,7 @@ class Run:
792
840
  # (when present) still reflects reality — keep it.
793
841
  return
794
842
  self._fastpath_text = None # type: ignore[attr-defined]
843
+ self._inline_snapshot.pop("renderedPageBreaks", None)
795
844
  drawing = self._inline_snapshot.get("drawing")
796
845
  picture = drawing.get("imageId") if isinstance(drawing, dict) else None
797
846
  if isinstance(picture, str) and picture:
@@ -961,14 +1010,19 @@ class Run:
961
1010
 
962
1011
  @property
963
1012
  def contains_page_break(self) -> bool:
964
- """python-docx 1.x: True if the run contains a hard page break.
1013
+ """python-docx 1.x: True when a rendered page break (a page Word's last
1014
+ layout began, ``w:lastRenderedPageBreak``) occurs in this run.
965
1015
 
966
- A run contains a page break iff its text segment includes the
967
- Form Feed sentinel ``\\f`` — that's how DOCX Studio marks a hard
968
- page break inside an inline run, mirroring Word's `<w:br
969
- w:type="page"/>`.
1016
+ An author's hard page break (``w:br w:type="page"``) is not one, as in
1017
+ python-docx. docx-studio reads them in the run (``renderedPageBreaks``);
1018
+ the ``\\f`` sentinel is an older docx-studio's mark.
970
1019
  """
971
- return "\f" in self.text
1020
+ return bool(self._rendered_page_breaks()) or "\f" in self.text
1021
+
1022
+ def _rendered_page_breaks(self) -> "list[dict]":
1023
+ """The rendered page breaks docx-studio reads in this run, in order (``renderedPageBreaks``)."""
1024
+ breaks = self._inline_snapshot.get("renderedPageBreaks")
1025
+ return [info for info in breaks if isinstance(info, dict)] if isinstance(breaks, list) else []
972
1026
 
973
1027
  def _continues_own_formatting(self) -> bool:
974
1028
  """True when content appended to this run takes its formatting from
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "athena-python-docx"
7
- version = "0.32.0"
7
+ version = "0.33.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"
@@ -52,6 +52,7 @@
52
52
  "fieldRun": {
53
53
  "text": "1",
54
54
  "part": "result",
55
+ "noProof": true,
55
56
  "instruction": "SEQ Figure \\* ARABIC",
56
57
  "fieldIndex": 0
57
58
  }
@@ -270,19 +270,8 @@
270
270
  "rw20_header_table_merges"
271
271
  ],
272
272
  "gaps": [
273
- "38_font_all_properties::export::runs.font.snap_to_grid",
274
- "38_font_all_properties::reads::runs.font.snap_to_grid",
275
273
  "abp46_comment_rich_text::export::comments.ids",
276
274
  "abp46_comment_rich_text::reads::comments.ids",
277
- "ex07_every_font_boolean::export::runs.font.complex_script",
278
- "ex07_every_font_boolean::export::runs.font.cs_bold",
279
- "ex07_every_font_boolean::export::runs.font.cs_italic",
280
- "ex07_every_font_boolean::export::runs.font.snap_to_grid",
281
- "ex07_every_font_boolean::reads::runs.font.complex_script",
282
- "ex07_every_font_boolean::reads::runs.font.cs_bold",
283
- "ex07_every_font_boolean::reads::runs.font.cs_italic",
284
- "ex07_every_font_boolean::reads::runs.font.snap_to_grid",
285
- "fixture-bidi-rtl::reads::runs.font.cs_bold",
286
275
  "fixture-bidi-wrappers::reads::hyperlinks",
287
276
  "fixture-bidi-wrappers::reads::hyperlinks.runs",
288
277
  "fixture-bidi-wrappers::reads::paragraphs.inner_content",
@@ -327,12 +316,7 @@
327
316
  "fixture-comments-links::reads::comments.ids",
328
317
  "fixture-comments-point::reads::comments.ids",
329
318
  "fixture-comments-replies::reads::comments.ids",
330
- "fixture-fields-ref-seq-toc::reads::runs.font.no_proof",
331
- "fixture-fields-toc-entries::reads::hyperlinks.runs",
332
- "fixture-form-fields::reads::runs.font.no_proof",
333
319
  "fixture-hyperlinks-empty-comment-drawing::reads::comments.ids",
334
- "fixture-index-captions::reads::hyperlinks.runs",
335
- "fixture-index-captions::reads::runs.font.no_proof",
336
320
  "fixture-inline-tags::reads::hyperlinks",
337
321
  "fixture-inline-tags::reads::hyperlinks.runs",
338
322
  "fixture-inline-tags::reads::paragraphs.inner_content",
@@ -373,17 +357,7 @@
373
357
  "fixture-inline-tags::reads::sections.inner_content",
374
358
  "fixture-inline-tags::reads::tables.cell_format",
375
359
  "fixture-inline-tags::reads::tables.cell_paragraph_content",
376
- "fixture-inline-tags::reads::tables.cells",
377
- "fixture-notes-fields-bookmarks::reads::runs.font.no_proof",
378
- "fixture-permissions-proofing::reads::paragraphs.contains_page_break",
379
- "fixture-permissions-proofing::reads::paragraphs.rendered_page_breaks",
380
- "fixture-permissions-proofing::reads::runs.contains_page_break",
381
- "fixture-permissions-proofing::reads::runs.inner_content",
382
- "fixture-run-properties-unmodeled::reads::runs.font.snap_to_grid",
383
- "fixture-smartart-diagram::reads::runs.font.no_proof",
384
- "fixture-smartart-diagram::reads::runs.inner_content",
385
- "fixture-text-effects-w14::reads::runs.font.no_proof",
386
- "fixture-textbox-page-break::reads::runs.font.no_proof"
360
+ "fixture-inline-tags::reads::tables.cells"
387
361
  ],
388
362
  "reasons": [
389
363
  {
@@ -393,43 +367,11 @@
393
367
  ],
394
368
  "reason": "Owner question 47 (open): runs inside an inline wrapper (w:dir, w:bdo, w:smartTag, w:customXml) read as the paragraph's runs, where python-docx reads only a w:p's direct w:r and w:hyperlink children, so the runs, their fonts and inner content, the links' runs, the paragraph's text and inner content, and the cells holding them read with the wrapped runs."
395
369
  },
396
- {
397
- "match": [
398
- "*::*::runs.font.no_proof",
399
- "*::*::runs.font.snap_to_grid",
400
- "*::*::runs.font.cs_bold",
401
- "*::*::runs.font.cs_italic",
402
- "*::*::runs.font.complex_script"
403
- ],
404
- "reason": "R4 (to fix): a run's w:noProof, w:snapToGrid, w:bCs, w:iCs and w:cs are carried by no model mark, so Font.no_proof, snap_to_grid, cs_bold, cs_italic and complex_script read None, and setting snap_to_grid, cs_bold, cs_italic or complex_script does not reach the export."
405
- },
406
- {
407
- "match": [
408
- "fixture-permissions-proofing::reads::paragraphs.contains_page_break",
409
- "fixture-permissions-proofing::reads::paragraphs.rendered_page_breaks",
410
- "fixture-permissions-proofing::reads::runs.contains_page_break",
411
- "fixture-permissions-proofing::reads::runs.inner_content"
412
- ],
413
- "reason": "R5 (to fix): w:lastRenderedPageBreak is neither kept nor read, so contains_page_break, rendered_page_breaks and a run's inner content leave out the page break Word last laid out there."
414
- },
415
370
  {
416
371
  "match": [
417
372
  "*::*::comments.ids"
418
373
  ],
419
374
  "reason": "R6 (to fix): Comment.comment_id returns a str where python-docx returns an int, and the first comment a script adds is numbered 1 where python-docx numbers it 0."
420
- },
421
- {
422
- "match": [
423
- "fixture-smartart-diagram::reads::runs.inner_content"
424
- ],
425
- "reason": "R11 (to fix): a SmartArt drawing is left out of its run's inner content: the import moves the diagram into text boxes after the paragraph, and the run's record keeps no trace of the drawing for the read to yield a Drawing (has_picture False) from."
426
- },
427
- {
428
- "match": [
429
- "fixture-fields-toc-entries::reads::hyperlinks.runs",
430
- "fixture-index-captions::reads::hyperlinks.runs"
431
- ],
432
- "reason": "R13 (to fix): a link's runs read no w:webHidden (the tab and page number runs Word writes in a table of contents or a table of figures entry), where python-docx reads web_hidden True; index-captions' entry runs also read no w:noProof (R4)."
433
375
  }
434
376
  ]
435
377
  }
@@ -0,0 +1,196 @@
1
+ """Rendered page breaks (``w:lastRenderedPageBreak``) as python-docx 1.2.0 reads them.
2
+
3
+ docx-studio reads each one in its run (``renderedPageBreaks``): its place in
4
+ the run's text, and the text of python-docx's fragments around the
5
+ paragraph's first break (``preceding`` / ``following``, null for no fragment,
6
+ absent for a break python-docx raises for). ``Run.contains_page_break`` and
7
+ ``Run.iter_inner_content`` read them in the run, and
8
+ ``Paragraph.rendered_page_breaks`` in the paragraph's runs and its links' runs.
9
+ An author's hard page break is not one.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from typing import Any
15
+
16
+ import pytest
17
+
18
+
19
+ @pytest.fixture
20
+ def paragraph_of(mock_session: Any, fake_sd_methods: Any) -> Any:
21
+ """A paragraph whose read (``get_node_by_id``) holds `inlines`."""
22
+ from docx.text.paragraph import Paragraph
23
+
24
+ def make(inlines: list[dict[str, Any]]) -> Any:
25
+ text = "".join(inline.get("run", {}).get("text", "") for inline in inlines)
26
+ fake_sd_methods.scripted_returns["get_node_by_id"] = {
27
+ "node": {
28
+ "nodeId": "p-1",
29
+ "nodeType": "paragraph",
30
+ "text": text,
31
+ "paragraph": {"inlines": inlines, "runMarks": True},
32
+ }
33
+ }
34
+ return Paragraph(session=mock_session, node_id="p-1", text_len=len(text))
35
+
36
+ return make
37
+
38
+
39
+ def _kinds(items: list[Any]) -> list[Any]:
40
+ return [item if isinstance(item, str) else type(item).__name__ for item in items]
41
+
42
+
43
+ FIRST = {"offset": 0, "preceding": "Before, ", "following": "after the break"}
44
+
45
+
46
+ def test_a_run_starting_on_a_new_page_yields_the_break_then_its_text(paragraph_of: Any) -> None:
47
+ paragraph = paragraph_of(
48
+ [
49
+ {"run": {"text": "Before, "}},
50
+ {"run": {"text": "after the break", "renderedPageBreaks": [FIRST]}},
51
+ ],
52
+ )
53
+
54
+ runs = paragraph.runs
55
+
56
+ assert [run.contains_page_break for run in runs] == [False, True]
57
+ assert _kinds(list(runs[0].iter_inner_content())) == ["Before, "]
58
+ assert _kinds(list(runs[1].iter_inner_content())) == ["RenderedPageBreak", "after the break"]
59
+ assert runs[1].text == "after the break"
60
+
61
+
62
+ def test_a_break_inside_a_run_yields_its_text_on_each_side(paragraph_of: Any) -> None:
63
+ paragraph = paragraph_of(
64
+ [
65
+ {
66
+ "run": {
67
+ "text": "abcdef",
68
+ "renderedPageBreaks": [{"offset": 3, "preceding": "abc", "following": "def"}],
69
+ }
70
+ }
71
+ ],
72
+ )
73
+
74
+ assert _kinds(list(paragraph.runs[0].iter_inner_content())) == [
75
+ "abc",
76
+ "RenderedPageBreak",
77
+ "def",
78
+ ]
79
+
80
+
81
+ def test_the_paragraph_lists_its_breaks_and_their_fragments(paragraph_of: Any) -> None:
82
+ paragraph = paragraph_of(
83
+ [
84
+ {"run": {"text": "Before, "}},
85
+ {"run": {"text": "after the break", "renderedPageBreaks": [FIRST]}},
86
+ ],
87
+ )
88
+
89
+ breaks = paragraph.rendered_page_breaks
90
+
91
+ assert paragraph.contains_page_break is True
92
+ assert len(breaks) == 1
93
+ assert breaks[0].preceding_paragraph_fragment.text == "Before, "
94
+ assert breaks[0].following_paragraph_fragment.text == "after the break"
95
+ # A run's break reads the same fragments.
96
+ from_run = list(paragraph.runs[1].iter_inner_content())[0]
97
+ assert from_run.preceding_paragraph_fragment.text == "Before, "
98
+
99
+
100
+ def test_a_fragment_python_docx_has_none_of_is_none(paragraph_of: Any) -> None:
101
+ paragraph = paragraph_of(
102
+ [
103
+ {
104
+ "run": {
105
+ "text": "All on the new page",
106
+ "renderedPageBreaks": [
107
+ {"offset": 0, "preceding": None, "following": "All on the new page"}
108
+ ],
109
+ }
110
+ }
111
+ ],
112
+ )
113
+
114
+ [brk] = paragraph.rendered_page_breaks
115
+
116
+ assert brk.preceding_paragraph_fragment is None
117
+ assert brk.following_paragraph_fragment.text == "All on the new page"
118
+
119
+
120
+ def test_a_later_break_raises_for_its_fragments_as_python_docx_does(paragraph_of: Any) -> None:
121
+ paragraph = paragraph_of(
122
+ [
123
+ {
124
+ "run": {
125
+ "text": "one ",
126
+ "renderedPageBreaks": [{"offset": 2, "preceding": "on", "following": "e two"}],
127
+ }
128
+ },
129
+ {"run": {"text": "two", "renderedPageBreaks": [{"offset": 1}]}},
130
+ ],
131
+ )
132
+
133
+ first, second = paragraph.rendered_page_breaks
134
+
135
+ assert first.preceding_paragraph_fragment.text == "on"
136
+ with pytest.raises(ValueError, match="first rendered page-break"):
137
+ _ = second.preceding_paragraph_fragment
138
+ with pytest.raises(ValueError, match="first rendered page-break"):
139
+ _ = second.following_paragraph_fragment
140
+
141
+
142
+ def test_a_link_run_s_break_is_the_link_s_and_the_paragraph_s(paragraph_of: Any) -> None:
143
+ link_break = {"offset": 2, "preceding": "See link", "following": " after"}
144
+ paragraph = paragraph_of(
145
+ [
146
+ {"run": {"text": "See "}},
147
+ {
148
+ "run": {
149
+ "text": "link",
150
+ "hyperlink": "https://example.com/",
151
+ "renderedPageBreaks": [link_break],
152
+ }
153
+ },
154
+ {"run": {"text": " after"}},
155
+ ],
156
+ )
157
+
158
+ [link] = paragraph.hyperlinks
159
+
160
+ assert link.contains_page_break is True
161
+ assert [brk.following_paragraph_fragment.text for brk in paragraph.rendered_page_breaks] == [
162
+ " after"
163
+ ]
164
+
165
+
166
+ def test_a_paragraph_without_breaks_reads_none(paragraph_of: Any) -> None:
167
+ paragraph = paragraph_of([{"run": {"text": "Plain"}}, {"run": {"text": "", "break": "page"}}])
168
+
169
+ assert paragraph.contains_page_break is False
170
+ assert paragraph.rendered_page_breaks == []
171
+ assert [run.contains_page_break for run in paragraph.runs] == [False, False]
172
+
173
+
174
+ def test_a_run_whose_text_is_replaced_holds_its_break_no_more(paragraph_of: Any) -> None:
175
+ """python-docx's ``run.text = ...`` replaces all of the run's content, its
176
+ rendered page break too (``Run.clear()`` does the same through it)."""
177
+ paragraph = paragraph_of(
178
+ [{"run": {"text": "abcdef", "renderedPageBreaks": [{"offset": 3, "preceding": "abc", "following": "def"}]}}],
179
+ )
180
+ run = paragraph.runs[0]
181
+
182
+ run.text = "new"
183
+
184
+ assert run.contains_page_break is False
185
+ assert "RenderedPageBreak" not in _kinds(list(run.iter_inner_content()))
186
+
187
+
188
+ def test_a_break_after_ruby_base_text_counts_it(paragraph_of: Any) -> None:
189
+ """The break's offset counts the run's text with its ruby base text, which
190
+ python-docx's run text leaves out."""
191
+ paragraph = paragraph_of(
192
+ [{"run": {"text": "A\u6f22BC", "ruby": [[1, 2]], "renderedPageBreaks": [{"offset": 2, "preceding": "A", "following": "BC"}]}}],
193
+ )
194
+
195
+ assert _kinds(list(paragraph.runs[0].iter_inner_content())) == ["A", "RenderedPageBreak", "BC"]
196
+