athena-python-docx 0.35.0__tar.gz → 0.36.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 (489) hide show
  1. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/AGENTS.md +15 -0
  2. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/PKG-INFO +1 -1
  3. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/__init__.py +1 -1
  4. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/_proxy.py +26 -2
  5. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/commands.py +4 -0
  6. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/oxml/__init__.py +1 -0
  7. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/styles/latent.py +21 -4
  8. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/styles/style.py +29 -10
  9. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/table.py +7 -3
  10. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/text/paragraph.py +9 -3
  11. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/text/run.py +147 -46
  12. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/text/tabstops.py +26 -3
  13. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/pyproject.toml +1 -1
  14. athena_python_docx-0.36.0/tests/fidelity/ab_probe/agent_documents.py +133 -0
  15. athena_python_docx-0.36.0/tests/fidelity/ab_probe/picture_runs.py +101 -0
  16. athena_python_docx-0.36.0/tests/fidelity/ab_probe/return_types.py +44 -0
  17. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +1 -0
  18. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_cell_inner_run_guard.py +5 -7
  19. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_drawing_image.py +179 -0
  20. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_enum_parity.py +3 -1
  21. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_latent_style_writes.py +21 -0
  22. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_run_identity.py +13 -11
  23. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_style_tab_stops.py +35 -0
  24. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_tab_stop_writes.py +21 -0
  25. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/uv.lock +1 -1
  26. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/.gitignore +0 -0
  27. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/CLAUDE.md +0 -0
  28. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/DOCX_EXEC_LAB.md +0 -0
  29. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/README.md +0 -0
  30. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/_athena_extension.py +0 -0
  31. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/_batching.py +0 -0
  32. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/_buffer.py +0 -0
  33. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/_capabilities.py +0 -0
  34. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/_citations.py +0 -0
  35. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/_execution.py +0 -0
  36. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/_http.py +0 -0
  37. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/_http_doc.py +0 -0
  38. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/_image_utils.py +0 -0
  39. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/_postproc.py +0 -0
  40. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/_ptc.py +0 -0
  41. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/_references.py +0 -0
  42. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/_run_order.py +0 -0
  43. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/_table_styles.py +0 -0
  44. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/_timeouts.py +0 -0
  45. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/_tracked.py +0 -0
  46. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/_xml_text.py +0 -0
  47. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/api.py +0 -0
  48. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/blkcntnr.py +0 -0
  49. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/bookmarks.py +0 -0
  50. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/charts.py +0 -0
  51. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/client.py +0 -0
  52. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/comments.py +0 -0
  53. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/document.py +0 -0
  54. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/drawing/__init__.py +0 -0
  55. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/enum/__init__.py +0 -0
  56. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/enum/base.py +0 -0
  57. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/enum/dml.py +0 -0
  58. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/enum/section.py +0 -0
  59. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/enum/shape.py +0 -0
  60. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/enum/style.py +0 -0
  61. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/enum/table.py +0 -0
  62. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/enum/text.py +0 -0
  63. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/errors.py +0 -0
  64. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/exceptions.py +0 -0
  65. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/fields.py +0 -0
  66. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/footnotes.py +0 -0
  67. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/image/__init__.py +0 -0
  68. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/image/exceptions.py +0 -0
  69. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/image/image.py +0 -0
  70. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/math.py +0 -0
  71. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/opc/__init__.py +0 -0
  72. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/opc/coreprops.py +0 -0
  73. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/revisions.py +0 -0
  74. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/sdt.py +0 -0
  75. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/section.py +0 -0
  76. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/session.py +0 -0
  77. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/settings.py +0 -0
  78. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/shape.py +0 -0
  79. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/shared.py +0 -0
  80. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/styles/__init__.py +0 -0
  81. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/styles/_table.py +0 -0
  82. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/styles/styles.py +0 -0
  83. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/text/__init__.py +0 -0
  84. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/text/font.py +0 -0
  85. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/text/hyperlink.py +0 -0
  86. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/text/pagebreak.py +0 -0
  87. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/text/parfmt.py +0 -0
  88. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/toc.py +0 -0
  89. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/docx/typing.py +0 -0
  90. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/scripts/docx_exec_lab.py +0 -0
  91. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
  92. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
  93. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/scripts/docx_exec_lab_server.py +0 -0
  94. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/scripts/dump_wire_fixtures.py +0 -0
  95. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/scripts/release.sh +0 -0
  96. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/scripts/round_trip_smoke.py +0 -0
  97. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/scripts/smoke_test_block_not_found.py +0 -0
  98. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/scripts/validate_find_replace_asset.py +0 -0
  99. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/__init__.py +0 -0
  100. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/conftest.py +0 -0
  101. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/METHODOLOGY.md +0 -0
  102. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/README.md +0 -0
  103. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/__init__.py +0 -0
  104. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/ab_probe/__init__.py +0 -0
  105. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/ab_probe/default_style_run_formatting.py +0 -0
  106. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/ab_probe/header_footer_pictures.py +0 -0
  107. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/ab_probe/less_used_writes.py +0 -0
  108. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/ab_probe/merged_cell_identity.py +0 -0
  109. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/ab_probe/paragraph_text_setter.py +0 -0
  110. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/ab_probe/proxy_equality.py +0 -0
  111. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/ab_probe/xml_incompatible_text.py +0 -0
  112. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/ab_probe/zero_and_negative_indents.py +0 -0
  113. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/ab_probe_cases.py +0 -0
  114. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/ab_probe_runner.py +0 -0
  115. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/auto_gen_cases.py +0 -0
  116. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/binary_round_trip.py +0 -0
  117. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/cases.py +0 -0
  118. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/complex_cases.py +0 -0
  119. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/coverage_report.py +0 -0
  120. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/extract.py +0 -0
  121. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/extreme_cases.py +0 -0
  122. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/fake_session.py +0 -0
  123. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/firm_templates/README.md +0 -0
  124. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/firm_templates/__init__.py +0 -0
  125. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/firm_templates/_runner.py +0 -0
  126. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/firm_templates/extractor.py +0 -0
  127. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
  128. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
  129. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/local_runner.py +0 -0
  130. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/mega_cases.py +0 -0
  131. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshot.py +0 -0
  132. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
  133. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
  134. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
  135. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
  136. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
  137. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
  138. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
  139. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
  140. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
  141. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
  142. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
  143. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
  144. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
  145. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
  146. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
  147. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
  148. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
  149. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
  150. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
  151. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
  152. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
  153. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
  154. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
  155. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
  156. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
  157. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
  158. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
  159. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
  160. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
  161. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
  162. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
  163. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
  164. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
  165. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
  166. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
  167. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
  168. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
  169. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
  170. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
  171. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
  172. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
  173. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
  174. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
  175. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
  176. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
  177. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
  178. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
  179. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
  180. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
  181. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
  182. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
  183. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
  184. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
  185. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
  186. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
  187. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
  188. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
  189. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
  190. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
  191. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
  192. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
  193. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
  194. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
  195. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
  196. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
  197. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
  198. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
  199. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
  200. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
  201. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
  202. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
  203. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
  204. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
  205. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
  206. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
  207. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
  208. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
  209. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
  210. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
  211. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
  212. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
  213. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
  214. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
  215. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
  216. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
  217. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
  218. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
  219. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
  220. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
  221. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
  222. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
  223. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
  224. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
  225. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
  226. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
  227. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
  228. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
  229. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
  230. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
  231. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
  232. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
  233. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
  234. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
  235. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
  236. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
  237. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
  238. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
  239. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
  240. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
  241. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
  242. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
  243. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
  244. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
  245. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
  246. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
  247. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
  248. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
  249. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
  250. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
  251. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
  252. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
  253. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
  254. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
  255. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
  256. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
  257. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
  258. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
  259. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
  260. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
  261. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
  262. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
  263. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
  264. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
  265. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
  266. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
  267. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
  268. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
  269. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
  270. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
  271. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
  272. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
  273. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
  274. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
  275. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
  276. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
  277. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
  278. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/rw16_header_footer_tables.json +0 -0
  279. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/rw17_header_letterhead_filled.json +0 -0
  280. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/rw18_header_table_properties.json +0 -0
  281. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/rw19_header_table_rows_columns.json +0 -0
  282. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/op_snapshots/rw20_header_table_merges.json +0 -0
  283. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/ours_spec.json +0 -0
  284. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/parity_crawl.py +0 -0
  285. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/parity_diff.json +0 -0
  286. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/real_world_cases.py +0 -0
  287. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/round_trip_tests.py +0 -0
  288. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/stock_spec.json +0 -0
  289. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
  290. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fixtures/field_reads.json +0 -0
  291. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fixtures/note_reference_reads.json +0 -0
  292. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fixtures/ruby_reads.json +0 -0
  293. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/fixtures/symbol_reads.json +0 -0
  294. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/live/__init__.py +0 -0
  295. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/live/baseline.json +0 -0
  296. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/live/fixture_scripts.py +0 -0
  297. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/live/reader.py +0 -0
  298. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/live/runner.py +0 -0
  299. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/live/sdk_driver.py +0 -0
  300. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/live/stock_driver.py +0 -0
  301. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/live/test_live_runner.py +0 -0
  302. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/parity/README.md +0 -0
  303. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/parity/__init__.py +0 -0
  304. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/parity/baseline_gaps.json +0 -0
  305. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/parity/compare.py +0 -0
  306. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/parity/intentional_deviations.json +0 -0
  307. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/parity/introspect.py +0 -0
  308. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
  309. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/parity/reports/gap_report.json +0 -0
  310. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/parity/run_parity.py +0 -0
  311. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/parity/snapshots/athena_latest.json +0 -0
  312. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
  313. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/parity/test_parity_gap.py +0 -0
  314. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_add_section.py +0 -0
  315. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_add_section_extract_items.py +0 -0
  316. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_alignment_values.py +0 -0
  317. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_athena_extension_creates_session_open.py +0 -0
  318. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_athena_extensions_contract.py +0 -0
  319. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_athena_extensions_registry.py +0 -0
  320. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_batch_chunking.py +0 -0
  321. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_batching_perf.py +0 -0
  322. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_battery_cases.py +0 -0
  323. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_block_not_found_error.py +0 -0
  324. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_buffer.py +0 -0
  325. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
  326. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_cell_add_table_not_supported.py +0 -0
  327. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_cell_borders_spec.py +0 -0
  328. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_cell_fastpath_runs.py +0 -0
  329. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
  330. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
  331. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_cell_inner_format_stash.py +0 -0
  332. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_cell_inner_run_format_stash.py +0 -0
  333. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_cell_merge.py +0 -0
  334. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_cell_nested_tables.py +0 -0
  335. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_cell_paragraphs_listed.py +0 -0
  336. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_cell_text_plain_fastpath.py +0 -0
  337. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_cell_text_replace_semantics.py +0 -0
  338. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_citations.py +0 -0
  339. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_collapsed_range_format.py +0 -0
  340. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_color_format_theme.py +0 -0
  341. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_command_dataclasses.py +0 -0
  342. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_commands.py +0 -0
  343. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_comment_paragraphs.py +0 -0
  344. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_comments.py +0 -0
  345. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_comments_first_read.py +0 -0
  346. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_content_control_value.py +0 -0
  347. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_core_properties.py +0 -0
  348. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_document_asset_id_property.py +0 -0
  349. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_document_clear.py +0 -0
  350. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_document_create.py +0 -0
  351. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_document_create_from_template.py +0 -0
  352. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_document_factory_validation.py +0 -0
  353. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_document_settings_session_open.py +0 -0
  354. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_docx_exec_lab.py +0 -0
  355. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_docx_exec_lab_server.py +0 -0
  356. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
  357. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_empty_hyperlinks.py +0 -0
  358. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_execution_scope.py +0 -0
  359. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_export_docx_clean.py +0 -0
  360. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_export_rescue_anchors.py +0 -0
  361. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_field_reads_python_docx_parity.py +0 -0
  362. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_field_refresh.py +0 -0
  363. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_field_runs.py +0 -0
  364. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_find_replace_session_open.py +0 -0
  365. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_flushed_write_failures.py +0 -0
  366. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_http_transport.py +0 -0
  367. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_hyperlink_coalescing.py +0 -0
  368. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_image_url_data_uri.py +0 -0
  369. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_inline_page_break.py +0 -0
  370. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_inline_shape_alt_text.py +0 -0
  371. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_inline_shape_size.py +0 -0
  372. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_inline_shape_type.py +0 -0
  373. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_inline_shapes_floating.py +0 -0
  374. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_insert_deferred.py +0 -0
  375. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_iter_inner_content.py +0 -0
  376. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_line_clear_break.py +0 -0
  377. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_list_styles.py +0 -0
  378. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_merged_cell_identity.py +0 -0
  379. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_merged_cell_secondary_slot.py +0 -0
  380. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_merged_cells.py +0 -0
  381. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_missing_modules.py +0 -0
  382. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_nested_table_refused.py +0 -0
  383. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_note_reference_reads_python_docx_parity.py +0 -0
  384. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_oxml_shim.py +0 -0
  385. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_page_break.py +0 -0
  386. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_paragraph_delete.py +0 -0
  387. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_paragraph_indent_reads.py +0 -0
  388. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_paragraph_runs_hyperlinks.py +0 -0
  389. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_paragraph_text_empty_run.py +0 -0
  390. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_paragraph_text_len_cache.py +0 -0
  391. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_parfmt_clears.py +0 -0
  392. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_parity_misc.py +0 -0
  393. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_parity_round2.py +0 -0
  394. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_partial_failure_cascade.py +0 -0
  395. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_pdf_export_and_page_renders.py +0 -0
  396. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_phase_a_behavior.py +0 -0
  397. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_phase_b_headers_footers.py +0 -0
  398. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_phase_c_tables.py +0 -0
  399. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_positional_cell_id.py +0 -0
  400. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
  401. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
  402. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_postproc_ref_restore.py +0 -0
  403. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_pr19766_review_fixes.py +0 -0
  404. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_proxy_equality.py +0 -0
  405. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_ptc.py +0 -0
  406. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_python_docx_api_parity.py +0 -0
  407. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_read_run_ranges.py +0 -0
  408. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_rendered_page_breaks.py +0 -0
  409. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_repeated_node_read.py +0 -0
  410. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_restart_numbering.py +0 -0
  411. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_revisions.py +0 -0
  412. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_round_trip_smoke_script.py +0 -0
  413. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_ruby_reads_python_docx_parity.py +0 -0
  414. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_run_add_picture_anchor.py +0 -0
  415. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_run_break_inner_content.py +0 -0
  416. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_run_edges.py +0 -0
  417. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_run_formatting_wire.py +0 -0
  418. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_run_later_runs.py +0 -0
  419. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_run_mark_inheritance.py +0 -0
  420. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_run_page_number_blocks.py +0 -0
  421. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_run_table_style_objects.py +0 -0
  422. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_run_text_python_docx.py +0 -0
  423. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_run_text_refreshes_paragraph.py +0 -0
  424. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_run_unmodeled_toggles.py +0 -0
  425. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_sdk_reads_served.py +0 -0
  426. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_section_read_lengths.py +0 -0
  427. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
  428. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_add_picture.py +0 -0
  429. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_add_run.py +0 -0
  430. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
  431. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
  432. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_comments_get.py +0 -0
  433. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_document_audit.py +0 -0
  434. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_document_element.py +0 -0
  435. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_enum_section.py +0 -0
  436. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_font_audit.py +0 -0
  437. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_header_footer.py +0 -0
  438. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_hyperlink.py +0 -0
  439. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_inline_shape.py +0 -0
  440. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
  441. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_misc.py +0 -0
  442. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
  443. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_paragraph_style.py +0 -0
  444. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
  445. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_parfmt.py +0 -0
  446. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_row_col_cell.py +0 -0
  447. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_run_add_break.py +0 -0
  448. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
  449. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_run_style.py +0 -0
  450. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_run_style_strict.py +0 -0
  451. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_run_text.py +0 -0
  452. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_run_underline.py +0 -0
  453. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_section_audit.py +0 -0
  454. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_section_dimensions.py +0 -0
  455. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_section_onoff.py +0 -0
  456. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_settings.py +0 -0
  457. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_shared_audit.py +0 -0
  458. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_style.py +0 -0
  459. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_styles.py +0 -0
  460. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_table_audit.py +0 -0
  461. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_table_cell.py +0 -0
  462. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_table_dimensions.py +0 -0
  463. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_silent_stub_table_layout.py +0 -0
  464. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_smoke_integration.py +0 -0
  465. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_style_acceptance.py +0 -0
  466. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_style_delete.py +0 -0
  467. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_style_font.py +0 -0
  468. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_style_formatting_writes.py +0 -0
  469. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_style_metadata_writes.py +0 -0
  470. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_style_names.py +0 -0
  471. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_style_setters_contract.py +0 -0
  472. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_style_tab_stops_read.py +0 -0
  473. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_symbol_reads_python_docx_parity.py +0 -0
  474. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_table_anchors.py +0 -0
  475. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_table_clears.py +0 -0
  476. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_table_direction.py +0 -0
  477. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_table_read_reuse.py +0 -0
  478. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_table_set_cell_perf.py +0 -0
  479. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_table_style_id_resolution.py +0 -0
  480. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_temporarily_unavailable.py +0 -0
  481. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_text_control_characters.py +0 -0
  482. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_tracked_revisions_offsets.py +0 -0
  483. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_unmapped_alignment_reads.py +0 -0
  484. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_uv_lock_version.py +0 -0
  485. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_validate_find_replace_asset_script.py +0 -0
  486. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_wire_contract.py +0 -0
  487. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_xml_attr_guard.py +0 -0
  488. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_xml_incompatible_text.py +0 -0
  489. {athena_python_docx-0.35.0 → athena_python_docx-0.36.0}/tests/test_zod_wire_contract.py +0 -0
@@ -121,6 +121,21 @@ pinned by focused tests and documented in
121
121
  `horz_dpi`, `width`, `scaled_dimensions`, …). A picture in a table cell or
122
122
  a header has no id to read its image by, so `.image` raises `ValueError`;
123
123
  an older docx-studio does not report pictures, so they are not runs there.
124
+ `Run.add_picture` puts the picture in that run, as python-docx appends the
125
+ `w:drawing` to its `w:r`: after what the run holds, and text `add_text`
126
+ adds goes after it. Against a docx-studio that reports
127
+ `capabilities.pictureRuns`, `CreateImage` carries `runEdge: "append"` (the
128
+ run's identity as `add_text` names it), the run's record holds the picture
129
+ (`pictureRun`), and the run reads as one run whose pictures stand at their
130
+ places in its text (`pictures`), so `iter_inner_content()` yields its text
131
+ and each `Drawing` in order (with Word's rendered page breaks where the read
132
+ places them among the pictures, `afterPictures`), a whole-run format takes
133
+ its pictures too, and `run.text = ...` takes every one of them out
134
+ (`wholeRun`). A picture's run (no text) formats its picture at once, named
135
+ by its identity (`laterRuns`), so `r = p.add_run(); r.bold = True;
136
+ r.add_picture(img)` writes the bold `w:rPr` beside the `w:drawing`. An older
137
+ docx-studio puts the picture in a run of its own (`tests/test_drawing_image.py`,
138
+ battery `ab_probe/picture_runs.py`).
124
139
  - **A run holding only a page or column break yields `""` from
125
140
  `Run.iter_inner_content()`,** as python-docx reads a `w:br` of type `page`
126
141
  or `column` as empty text; an empty run (a comment reference's) yields
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: athena-python-docx
3
- Version: 0.35.0
3
+ Version: 0.36.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.35.0"
9
+ __version__ = "0.36.0"
10
10
 
11
11
  from docx.api import Document
12
12
  from docx._buffer import discard_all, flush_all
@@ -1,11 +1,35 @@
1
1
  """python-docx's ``ElementProxy`` equality, for the SDK's proxies of the
2
2
  objects python-docx wraps in one: styles, the styles part, settings, a
3
- paragraph's or a style's paragraph format, a run's or a style's font."""
3
+ paragraph's or a style's paragraph format, a run's or a style's font, tab
4
+ stops and the latent styles."""
4
5
 
5
6
  from __future__ import annotations
6
7
 
7
8
  import weakref
8
- from typing import Any
9
+ from collections.abc import Iterator
10
+ from typing import Any, Generic, TypeVar
11
+
12
+ _T = TypeVar("_T")
13
+
14
+
15
+ class IdentityWeakSet(Generic[_T]):
16
+ """A set of objects held weakly, by identity: a ``weakref.WeakSet`` hashes
17
+ its members, and a proxy that compares by its element
18
+ (:class:`ElementProxyEquality`) is not hashable, as in python-docx."""
19
+
20
+ __slots__ = ("_members",)
21
+
22
+ def __init__(self) -> None:
23
+ self._members: "weakref.WeakValueDictionary[int, _T]" = weakref.WeakValueDictionary()
24
+
25
+ def add(self, item: _T) -> None:
26
+ self._members[id(item)] = item
27
+
28
+ def __iter__(self) -> Iterator[_T]:
29
+ return iter(list(self._members.values()))
30
+
31
+ def __len__(self) -> int:
32
+ return len(self._members)
9
33
 
10
34
 
11
35
  class ElementProxyEquality:
@@ -140,6 +140,10 @@ class CreateImage(Command):
140
140
  at: dict[str, Any]
141
141
  client_node_id: str | None = None
142
142
  run: dict[str, Any] | None = None
143
+ # ``"append"`` (``Run.add_picture`` against a docx-studio that reports
144
+ # ``capabilities.pictureRuns``): the picture ends the run that ends at the
145
+ # anchor's offset, in that run's ``w:r``, as python-docx appends it.
146
+ run_edge: str | None = None
143
147
 
144
148
 
145
149
  @dataclass
@@ -92,6 +92,7 @@ _XML_ELEMENT_ATTRS: frozenset[str] = frozenset(
92
92
  "_tc", # _Cell._tc
93
93
  "_tbl", # Table._tbl
94
94
  "_tr", # _Row._tr
95
+ "_t", # _Text._t (the ``w:t`` Run.add_text added)
95
96
  "element", # Run.element / Paragraph.element / Document.element
96
97
  "_element", # private alias used by some python-docx forks
97
98
  },
@@ -23,6 +23,7 @@ import warnings
23
23
  import weakref
24
24
  from typing import TYPE_CHECKING, Any, Iterator
25
25
 
26
+ from docx._proxy import ElementProxyEquality, IdentityWeakSet
26
27
  from docx.styles import BabelFish
27
28
 
28
29
  if TYPE_CHECKING:
@@ -52,12 +53,12 @@ _XML_INCOMPATIBLE_MESSAGE = (
52
53
  # Session -> every live ``_LatentStyle``, read or added: deleting one moves the
53
54
  # others of its name after it up one place (:func:`_removed`), as python-docx's
54
55
  # are elements a deletion elsewhere does not move. The sets hold them weakly.
55
- _LIVE: "weakref.WeakKeyDictionary[Any, weakref.WeakSet[_LatentStyle]]" = weakref.WeakKeyDictionary()
56
+ _LIVE: "weakref.WeakKeyDictionary[Any, IdentityWeakSet[_LatentStyle]]" = weakref.WeakKeyDictionary()
56
57
 
57
58
 
58
59
  def _track(latent_style: "_LatentStyle") -> None:
59
60
  try:
60
- _LIVE.setdefault(latent_style._session, weakref.WeakSet()).add(latent_style)
61
+ _LIVE.setdefault(latent_style._session, IdentityWeakSet()).add(latent_style)
61
62
  except TypeError: # no session, or one that takes no weak reference
62
63
  return
63
64
 
@@ -67,6 +68,7 @@ def _removed(deleted: "_LatentStyle", name: Any) -> None:
67
68
  that name after it move up one place, and another object of the same
68
69
  exception is out of the document, writing nothing to it, as python-docx's
69
70
  removed element is."""
71
+ deleted._element = removed = object()
70
72
  try:
71
73
  live = _LIVE.get(deleted._session)
72
74
  except TypeError:
@@ -78,6 +80,7 @@ def _removed(deleted: "_LatentStyle", name: Any) -> None:
78
80
  continue
79
81
  if other._occurrence == deleted._occurrence:
80
82
  other._detached = True
83
+ other._element = removed
81
84
  elif other._occurrence > deleted._occurrence:
82
85
  other._occurrence -= 1
83
86
 
@@ -140,13 +143,17 @@ def _int(text: object) -> int | None:
140
143
  return None
141
144
 
142
145
 
143
- class LatentStyles:
146
+ class LatentStyles(ElementProxyEquality):
144
147
  """The default behaviors for latent styles in this document, and the
145
148
  |_LatentStyle| objects that override them for a named latent style."""
146
149
 
147
150
  def __init__(self, *, session: "Session | None" = None) -> None:
148
151
  self._session: "Session | None" = session
149
152
 
153
+ def _proxy_element(self) -> tuple[object, ...]:
154
+ # python-docx's LatentStyles wraps the styles part's w:latentStyles.
155
+ return ("w:latentStyles", self._session)
156
+
150
157
  def _read(self, prop: str | None) -> dict[str, Any]:
151
158
  """The latent styles docx-studio reports; a read of ``prop`` from one
152
159
  that reports none warns (iterating, an empty collection, does not)."""
@@ -246,7 +253,7 @@ class LatentStyles:
246
253
  self._set_default("load_count", "count", None if value is None else int(value))
247
254
 
248
255
 
249
- class _LatentStyle:
256
+ class _LatentStyle(ElementProxyEquality):
250
257
  """A ``w:lsdException``: display behaviors for a built-in style the
251
258
  document does not define yet, overriding the |LatentStyles| defaults."""
252
259
 
@@ -260,8 +267,18 @@ class _LatentStyle:
260
267
  #: Removed through another object of the same exception: like
261
268
  #: python-docx's element out of the part, it writes nothing to the document.
262
269
  self._detached = False
270
+ #: The exception it held once removed, which its other objects hold too.
271
+ self._element: object | None = None
263
272
  _track(self)
264
273
 
274
+ def _proxy_element(self) -> tuple[object, ...]:
275
+ # python-docx's _LatentStyle wraps its w:lsdException: the exception
276
+ # of its name at its place among them, or, removed, that exception, as
277
+ # its other objects are.
278
+ if self._detached or self._exception is None:
279
+ return ("w:lsdException", self._element if self._element is not None else id(self))
280
+ return ("w:lsdException", self._session, self._exception.get("name"), self._occurrence)
281
+
265
282
  def _target(self) -> dict[str, Any]:
266
283
  """The exception's address in ``EditLatentStyles``."""
267
284
  name = self._exception.get("name")
@@ -35,6 +35,7 @@ from docx._batching import run_sync
35
35
  from docx._buffer import writes_unapplied
36
36
  from docx._proxy import (
37
37
  ElementProxyEquality,
38
+ IdentityWeakSet,
38
39
  style_definition,
39
40
  style_deleted,
40
41
  style_element,
@@ -910,11 +911,13 @@ class _StyleParagraphFormat(ElementProxyEquality):
910
911
 
911
912
  _TAB_STOPS_REASON = "does not write this style's tab stops: it predates styleTabStops, or does not write the style's formatting"
912
913
 
913
- # Session -> style id -> every live tab stop of that style, read or added: a
914
- # write moves the others to their new places in the style's list
915
- # (``_StyleTabStops._moved``), as python-docx's are elements that a change to
916
- # another does not move. The sets hold them weakly.
917
- _LIVE_TAB_STOPS: "weakref.WeakKeyDictionary[Any, dict[str, weakref.WeakSet[_StyleTabStop]]]" = (
914
+ # Session -> style definition (``style_element``) -> every live tab stop of
915
+ # that style, read or added: a write moves the others to their new places in
916
+ # the style's list (``_StyleTabStops._moved``), as python-docx's are elements
917
+ # that a change to another does not move. By the definition, not the id: a
918
+ # renamed style keeps its stops, and one made after a style of its id was
919
+ # deleted holds none of the deleted one's. The sets hold them weakly.
920
+ _LIVE_TAB_STOPS: "weakref.WeakKeyDictionary[Any, dict[tuple[object, ...], IdentityWeakSet[_StyleTabStop]]]" = (
918
921
  weakref.WeakKeyDictionary()
919
922
  )
920
923
 
@@ -926,7 +929,7 @@ _REMOVED = "removed"
926
929
  _CLEARED = "cleared"
927
930
 
928
931
 
929
- class _StyleTabStop:
932
+ class _StyleTabStop(ElementProxyEquality):
930
933
  """One tab stop of a style's ``w:tabs``, as python-docx's ``TabStop``
931
934
  reads it: ``position`` a ``Length``, ``alignment`` a
932
935
  ``WD_TAB_ALIGNMENT`` (a cleared tab's is ``CLEAR``), ``leader`` a
@@ -938,7 +941,7 @@ class _StyleTabStop:
938
941
  to another tab stop moves, as python-docx's ``w:tab`` element stays itself;
939
942
  one the style no longer holds writes nothing, as a removed element."""
940
943
 
941
- __slots__ = ("__weakref__", "_detached", "_entry", "_index", "_owner")
944
+ __slots__ = ("__weakref__", "_detached", "_element", "_entry", "_index", "_owner")
942
945
 
943
946
  def __init__(
944
947
  self, entry: dict[str, Any], owner: "_StyleTabStops | None" = None, index: int | None = None
@@ -947,9 +950,19 @@ class _StyleTabStop:
947
950
  self._owner: "_StyleTabStops | None" = owner
948
951
  self._index: int | None = index
949
952
  self._detached: str | None = None
953
+ # The w:tab it held once out of the style, which its other objects hold too.
954
+ self._element: object | None = None
950
955
  if owner is not None and index is not None:
951
956
  owner._track(self)
952
957
 
958
+ def _proxy_element(self) -> tuple[object, ...]:
959
+ # python-docx's TabStop wraps its w:tab: a stop at its place in the
960
+ # style's list is that w:tab; one out of the style is the w:tab taken
961
+ # out, as are its other objects.
962
+ if self._owner is not None and self._index is not None and not self._detached:
963
+ return ("w:tab", *style_element(self._owner._style), self._index)
964
+ return ("w:tab", self._element if self._element is not None else id(self))
965
+
953
966
  def _wire(self) -> dict[str, Any]:
954
967
  """This tab stop as ``SetStyleFormatting``'s ``tabStops`` takes it."""
955
968
  leader = self._entry.get("leader")
@@ -1010,7 +1023,7 @@ class _StyleTabStop:
1010
1023
  )
1011
1024
 
1012
1025
 
1013
- class _StyleTabStops:
1026
+ class _StyleTabStops(ElementProxyEquality):
1014
1027
  """``ParagraphStyle.paragraph_format.tab_stops``: python-docx's
1015
1028
  ``TabStops`` over the style's own ``w:tabs``.
1016
1029
 
@@ -1030,6 +1043,10 @@ class _StyleTabStops:
1030
1043
  def __init__(self, style: "BaseStyle") -> None:
1031
1044
  self._style: "BaseStyle" = style
1032
1045
 
1046
+ def _proxy_element(self) -> tuple[object, ...]:
1047
+ # python-docx's TabStops wraps the style's w:pPr (its paragraph format wraps the w:style).
1048
+ return ("w:pPr", *style_element(self._style))
1049
+
1033
1050
  def _stashed(self) -> list:
1034
1051
  kept = self._style._overrides
1035
1052
  if "parfmt.tab_stops" not in kept:
@@ -1052,7 +1069,7 @@ class _StyleTabStops:
1052
1069
  styles = _LIVE_TAB_STOPS.setdefault(self._style._session, {})
1053
1070
  except TypeError: # no session, or one that takes no weak reference
1054
1071
  return
1055
- styles.setdefault(self._style.style_id, weakref.WeakSet()).add(stop)
1072
+ styles.setdefault(style_element(self._style), IdentityWeakSet()).add(stop)
1056
1073
 
1057
1074
  def _moved(
1058
1075
  self,
@@ -1066,15 +1083,17 @@ class _StyleTabStops:
1066
1083
  new place) gives one, or out of the style (``why``) where ``place``
1067
1084
  gives none."""
1068
1085
  try:
1069
- live = (_LIVE_TAB_STOPS.get(self._style._session) or {}).get(self._style.style_id)
1086
+ live = (_LIVE_TAB_STOPS.get(self._style._session) or {}).get(style_element(self._style))
1070
1087
  except TypeError:
1071
1088
  return
1089
+ gone: dict[int, object] = {}
1072
1090
  for stop in list(live or ()):
1073
1091
  if stop._detached or stop._index is None:
1074
1092
  continue
1075
1093
  new = place(stop._index)
1076
1094
  if new is None:
1077
1095
  stop._detached = why
1096
+ stop._element = gone.setdefault(stop._index, object())
1078
1097
  continue
1079
1098
  stop._index = new
1080
1099
  if entries and new in entries:
@@ -321,6 +321,7 @@ if TYPE_CHECKING:
321
321
  WD_TABLE_ALIGNMENT,
322
322
  WD_TABLE_DIRECTION,
323
323
  )
324
+ from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
324
325
  from docx.styles.style import ParagraphStyle, _TableStyle
325
326
  from docx.text.paragraph import Paragraph
326
327
 
@@ -1224,14 +1225,17 @@ class Table:
1224
1225
  run_sync(self._session.doc.tables.set_style(params))
1225
1226
 
1226
1227
  @property
1227
- def alignment(self) -> "WD_TABLE_ALIGNMENT | None":
1228
- from docx.enum.table import WD_TABLE_ALIGNMENT
1228
+ def alignment(self) -> "WD_PARAGRAPH_ALIGNMENT | None":
1229
+ """The table's ``w:jc``, as python-docx 1.2.0 reads it: through the
1230
+ paragraph ``CT_Jc``, so a member of ``WD_PARAGRAPH_ALIGNMENT``, which
1231
+ compares equal to the ``WD_TABLE_ALIGNMENT`` member of its value."""
1232
+ from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
1229
1233
 
1230
1234
  info: object = run_sync(
1231
1235
  self._session.doc.tables.get_properties({"nodeId": self._fresh_node_id()}),
1232
1236
  )
1233
1237
  if isinstance(info, dict):
1234
- return WD_TABLE_ALIGNMENT.from_docx_studio(info.get("alignment"))
1238
+ return WD_PARAGRAPH_ALIGNMENT.from_docx_studio(info.get("alignment"))
1235
1239
  return None
1236
1240
 
1237
1241
  @alignment.setter
@@ -245,9 +245,15 @@ def _node_text(session: "Session", node_id: str, *, final: bool = False) -> str:
245
245
 
246
246
  def _picture_run_data(inline: dict, drawing: dict) -> dict:
247
247
  """The run data of a picture's inline (``{drawing}``): its ``w:r`` holds the
248
- picture and no text, and the rendered page breaks docx-studio reads in it,
249
- which stand before its drawing (``renderedPageBreaks``)."""
250
- data: dict = {"text": "", "drawing": drawing}
248
+ picture and no text, the rendered page breaks docx-studio reads in it,
249
+ which stand before its drawing (``renderedPageBreaks``), and the run's
250
+ formatting it reads (its ``w:rPr``, which python-docx reads beside the
251
+ ``w:drawing``)."""
252
+ from docx.text.run import _RUN_READ_KEYS
253
+
254
+ data: dict = {key: value for key, value in inline.items() if key in _RUN_READ_KEYS}
255
+ data["text"] = ""
256
+ data["drawing"] = drawing
251
257
  breaks: object = inline.get("renderedPageBreaks")
252
258
  if isinstance(breaks, list):
253
259
  data["renderedPageBreaks"] = breaks
@@ -463,6 +463,28 @@ class Run:
463
463
  return None
464
464
  return self._later_runs()
465
465
 
466
+ def _picture_run_identity(self) -> int | None:
467
+ """This run's identity (:meth:`_later_runs`) while it holds a picture
468
+ and no text (a picture's run, ``drawing``), which a whole-run
469
+ ``FormatApply`` of its collapsed range formats, untracked, on a
470
+ docx-studio that reports ``capabilities.pictureRuns``: python-docx
471
+ formats the run's ``w:r``, and docx-studio its picture. Else None."""
472
+ if not self._is_collapsed_range or tracking(self._session):
473
+ return None
474
+ drawing = self._inline_snapshot.get("drawing")
475
+ if not isinstance(drawing, dict) or drawing.get("legacy") is True:
476
+ return None
477
+ if not server_takes(self._session, "pictureRuns"):
478
+ return None
479
+ return self._later_runs()
480
+
481
+ def _empty_run_identity(self) -> int | None:
482
+ """The identity by which a whole-run ``FormatApply`` of this run's
483
+ collapsed range formats what it holds: a page or column break's
484
+ (:meth:`_break_run_identity`) or a picture's (:meth:`_picture_run_identity`)."""
485
+ identity = self._break_run_identity()
486
+ return identity if identity is not None else self._picture_run_identity()
487
+
466
488
  def _add_run_ref(self, command: dict) -> dict:
467
489
  """Add the run identity of an insert at the end of this run.
468
490
 
@@ -707,9 +729,10 @@ class Run:
707
729
  pending = getattr(self, "_pending_inline", None)
708
730
  if not pending:
709
731
  return
710
- # An empty run that holds a page or column break: docx-studio formats
711
- # the break's run, which it tells by the run's identity.
712
- identity = self._break_run_identity()
732
+ # An empty run that holds a page or column break, or a picture:
733
+ # docx-studio formats the run's break or picture, which it tells by
734
+ # the run's identity.
735
+ identity = self._empty_run_identity()
713
736
  if self._is_collapsed_range and identity is None:
714
737
  return
715
738
  self._pending_inline = {}
@@ -724,7 +747,7 @@ class Run:
724
747
  },
725
748
  ),
726
749
  )
727
- # Its break holds the formatting now, which what it takes next continues.
750
+ # Its break or picture holds the formatting now, which what it takes next continues.
728
751
  self._fresh = False
729
752
  return
730
753
  # An emptied run's kept formatting turns off each toggle it did not
@@ -827,6 +850,43 @@ class Run:
827
850
  yield Drawing(session=self._session, info=drawing)
828
851
  if not self._raw_text():
829
852
  return
853
+ pictures = self._pictures()
854
+ if pictures:
855
+ from docx.drawing import Drawing
856
+
857
+ # A run that holds pictures beside its text (``run.add_picture``
858
+ # appends a ``w:drawing`` to the run): the text between them, each
859
+ # picture and each of Word's rendered page breaks, in order. Where
860
+ # a break and a picture stand at one place in the text, the read
861
+ # says how many of the run's pictures come before the break
862
+ # (``afterPictures``); without it the break comes first, as Word
863
+ # writes a page that opens with the picture.
864
+ raw = self._raw_text()
865
+ font = self._symbol_font() if _SYMBOL_CHARACTERS.search(raw) else None
866
+ items: list[tuple[int, float, dict, bool]] = [
867
+ (offset, index, info, True) for index, (offset, info) in enumerate(pictures)
868
+ ]
869
+ rendered = self._rendered_page_breaks()
870
+ for info in rendered:
871
+ offset = int(info.get("offset", 0) or 0)
872
+ after = info.get("afterPictures")
873
+ if not isinstance(after, int) or isinstance(after, bool):
874
+ after = sum(1 for at, _ in pictures if at < offset)
875
+ items.append((offset, after - 0.5, info, False))
876
+ items.sort(key=lambda item: (item[0], item[1]))
877
+ cut = 0
878
+ for offset, _order, info, is_picture in items:
879
+ at = min(max(offset, cut), len(raw))
880
+ if _word_text(raw[cut:at], font):
881
+ yield _word_text(raw[cut:at], font)
882
+ if not is_picture:
883
+ yield RenderedPageBreak(paragraph=self._page_break_paragraph(), offset=self._range[0] + at, info=info)
884
+ elif info.get("legacy") is not True:
885
+ yield Drawing(session=self._session, info=info)
886
+ cut = at
887
+ if _word_text(raw[cut:], font):
888
+ yield _word_text(raw[cut:], font)
889
+ return
830
890
  if _note_reference(self._inline_snapshot):
831
891
  return
832
892
  text = _without_ruby(self._raw_text(), self._inline_snapshot)
@@ -923,18 +983,18 @@ class Run:
923
983
  return
924
984
  self._fastpath_text = None # type: ignore[attr-defined]
925
985
  self._inline_snapshot.pop("renderedPageBreaks", None)
926
- drawing = self._inline_snapshot.get("drawing")
927
- picture = drawing.get("imageId") if isinstance(drawing, dict) else None
928
- if isinstance(picture, str) and picture:
929
- # python-docx replaces all of the run's content, its picture too.
986
+ pictures: list[str] = self._picture_ids()
987
+ if pictures:
988
+ # python-docx replaces all of the run's content, its pictures too.
930
989
  old_range: tuple[int, int] = self._range
931
990
  tracked: bool = tracking(self._session)
932
- run_sync(
933
- self._session.doc.replace(
934
- {"target": self._text_range, "text": value, "removePictures": [picture]},
935
- ),
936
- )
991
+ replace: dict = {"target": self._text_range, "text": value, "removePictures": pictures}
992
+ if server_takes(self._session, "pictureRuns"):
993
+ # All of this run: its record then holds the new text alone.
994
+ replace["wholeRun"] = True
995
+ run_sync(self._session.doc.replace(replace))
937
996
  self._inline_snapshot.pop("drawing", None)
997
+ self._inline_snapshot.pop("pictures", None)
938
998
  if tracked:
939
999
  self._note_tracked_replacement(old_range, value)
940
1000
  else:
@@ -1029,8 +1089,11 @@ class Run:
1029
1089
  # any pending inline formatting now that there's text to mark.
1030
1090
  self._flush_pending_inline()
1031
1091
 
1032
- def add_text(self, text: str) -> "Run":
1033
- """python-docx: append text to the run. Returns a new Run for the added text."""
1092
+ def add_text(self, text: str) -> "_Text":
1093
+ """Append ``text`` to the run, as python-docx does: its tabs and line
1094
+ breaks stay characters. Returns a |_Text| for the added text, as
1095
+ python-docx returns one for the ``w:t`` it adds: it has no public
1096
+ API."""
1034
1097
  check_xml_compatible(text)
1035
1098
  if not isinstance(text, str):
1036
1099
  raise TypeError(
@@ -1039,34 +1102,10 @@ class Run:
1039
1102
  )
1040
1103
  self._refuse_field_write("add_text")
1041
1104
  if self._check_cell_inner("add_text"):
1042
- # Return a stub Run with the same in_cell flag so chained
1043
- # ops on the result also short-circuit cleanly. Propagate
1044
- # the parent's cell_ooxml_address so any subsequent run-
1045
- # format setter (``.bold = True`` etc.) can stash to the
1046
- # right cell in ``_post_export_ops`` instead of dropping
1047
- # silently.
1048
- return Run(
1049
- session=self._session,
1050
- block_id=self._block_id,
1051
- range_=(self._range[1], self._range[1]),
1052
- paragraph=self._paragraph,
1053
- in_cell=True,
1054
- cell_ooxml_address=getattr(self, "_cell_ooxml_address", None),
1055
- )
1105
+ return _Text(self)
1056
1106
  self._fastpath_text = None # type: ignore[attr-defined]
1057
- new_range = self._append_text(text)
1058
- added = Run(
1059
- session=self._session,
1060
- block_id=self._block_id,
1061
- range_=new_range,
1062
- paragraph=self._paragraph,
1063
- in_cell=self._in_cell,
1064
- cell_ooxml_address=getattr(self, "_cell_ooxml_address", None),
1065
- )
1066
- # The added text is part of this run: when it grows, this run grows
1067
- # (also through a chain of returned runs).
1068
- added._owner_record = getattr(self, "_run_record", None) or getattr(self, "_owner_record", None)
1069
- return added
1107
+ self._append_text(text)
1108
+ return _Text(self)
1070
1109
 
1071
1110
  def _append_text(self, text: str) -> tuple[int, int]:
1072
1111
  """Insert ``text`` at the end of this run; return the new text's range."""
@@ -1440,12 +1479,57 @@ class Run:
1440
1479
  create: dict = {"src": data_uri, "size": {"width": w_px, "height": h_px}, "at": at}
1441
1480
  if at["kind"] == "selection":
1442
1481
  self._add_run_ref(create)
1482
+ if server_takes(self._session, "pictureRuns"):
1483
+ # python-docx appends the ``w:drawing`` to this run's ``w:r``.
1484
+ self._mark_append(create)
1443
1485
  result: object = run_sync(self._session.doc.create.image(create))
1444
1486
  info: dict = _build_inline_shape_info(result, width=w_pt, height=h_pt)
1445
1487
  if at["kind"] == "selection" and info.get("nodeId"):
1446
1488
  note_picture(self, info["nodeId"], self._range[1], before=_before_image(create))
1489
+ if create.get("runEdge") == "append":
1490
+ self._hold_picture({"imageId": info["nodeId"]})
1491
+ # The formatting set on the run while it was empty is its
1492
+ # ``w:rPr``, which python-docx writes beside the picture.
1493
+ self._flush_pending_inline()
1447
1494
  return InlineShape(session=self._session, info=info)
1448
1495
 
1496
+ def _pictures(self) -> list[tuple[int, dict]]:
1497
+ """The pictures this run holds beside its text (``pictures``, which a
1498
+ docx-studio reporting ``capabilities.pictureRuns`` reads): each one's
1499
+ place in the run's text, in characters, and its drawing, in order."""
1500
+ held: object = self._inline_snapshot.get("pictures")
1501
+ pictures: list[tuple[int, dict]] = []
1502
+ for entry in held if isinstance(held, list) else []:
1503
+ drawing: object = entry.get("drawing") if isinstance(entry, dict) else None
1504
+ if not isinstance(drawing, dict):
1505
+ continue
1506
+ offset: object = entry.get("offset")
1507
+ valid = isinstance(offset, int) and not isinstance(offset, bool)
1508
+ pictures.append((offset if valid else 0, drawing)) # type: ignore[arg-type]
1509
+ return pictures
1510
+
1511
+ def _picture_ids(self) -> list[str]:
1512
+ """The id of each picture this run holds: a picture run's (``drawing``)
1513
+ and those beside its text (``pictures``)."""
1514
+ drawings = [self._inline_snapshot.get("drawing"), *(info for _, info in self._pictures())]
1515
+ ids = [d.get("imageId") for d in drawings if isinstance(d, dict)]
1516
+ return [i for i in ids if isinstance(i, str) and i]
1517
+
1518
+ def _hold_picture(self, drawing: dict) -> None:
1519
+ """This run took the picture ``drawing`` at its end (``run.add_picture``),
1520
+ as the next read gives it: a run of a picture alone as a picture's run
1521
+ (``drawing``), else among its pictures (``pictures``) where its text ends."""
1522
+ snapshot = self._inline_snapshot
1523
+ held = self._pictures()
1524
+ alone = snapshot.get("drawing")
1525
+ if not held and not self._raw_text() and not isinstance(alone, dict):
1526
+ snapshot["drawing"] = drawing
1527
+ return
1528
+ if isinstance(alone, dict):
1529
+ held = [(0, snapshot.pop("drawing"))]
1530
+ held.append((len(self._raw_text()), drawing))
1531
+ snapshot["pictures"] = [{"offset": offset, "drawing": info} for offset, info in held]
1532
+
1449
1533
  @athena_extension(
1450
1534
  issue=498,
1451
1535
  description="Run.add_field — generic Word field (PAGE, DATE, REF, SEQ, TOC, ...).",
@@ -1901,10 +1985,11 @@ class Run:
1901
1985
  )
1902
1986
  return
1903
1987
  self._note_inline_applied(kwargs)
1904
- identity = self._break_run_identity()
1988
+ identity = self._empty_run_identity()
1905
1989
  if identity is not None:
1906
- # The run holds only a page or column break: python-docx formats its
1907
- # ``w:r``, and docx-studio its break, which it tells by the run's identity.
1990
+ # The run holds only a page or column break, or a picture: python-docx
1991
+ # formats its ``w:r``, and docx-studio its break or picture, which it
1992
+ # tells by the run's identity.
1908
1993
  run_sync(
1909
1994
  self._session.doc.format.apply(
1910
1995
  {
@@ -1915,7 +2000,7 @@ class Run:
1915
2000
  },
1916
2001
  ),
1917
2002
  )
1918
- # Its break holds the formatting now, which what it takes next continues.
2003
+ # Its break or picture holds the formatting now, which what it takes next continues.
1919
2004
  self._fresh = False
1920
2005
  return
1921
2006
  if self._is_collapsed_range:
@@ -2125,6 +2210,22 @@ class Run:
2125
2210
  return Font(self)
2126
2211
 
2127
2212
 
2213
+ class _Text:
2214
+ """Proxy object wrapping ``<w:t>`` element: what ``Run.add_text``
2215
+ returns, as python-docx returns one for the ``w:t`` it adds. It has no
2216
+ public API; its element (``_t``) is not available here."""
2217
+
2218
+ def __init__(self, run: "Run") -> None:
2219
+ super().__init__()
2220
+ self._run = run
2221
+
2222
+ def __getattr__(self, name: str) -> Any:
2223
+ from docx.oxml import guard_xml_attr
2224
+
2225
+ guard_xml_attr("_Text", name)
2226
+ raise AttributeError(f"'_Text' object has no attribute {name!r}")
2227
+
2228
+
2128
2229
  class Font(ElementProxyEquality):
2129
2230
  """Font properties of a Run. Accessed via `run.font`; do not instantiate."""
2130
2231