athena-python-docx 0.26.0__tar.gz → 0.27.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 (409) hide show
  1. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/AGENTS.md +43 -0
  2. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/PKG-INFO +1 -1
  3. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/__init__.py +1 -1
  4. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/errors.py +10 -0
  5. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/text/paragraph.py +94 -10
  6. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/text/run.py +142 -1
  7. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/pyproject.toml +1 -1
  8. athena_python_docx-0.27.0/tests/fixtures/field_reads.json +863 -0
  9. athena_python_docx-0.27.0/tests/test_field_reads_python_docx_parity.py +105 -0
  10. athena_python_docx-0.27.0/tests/test_field_runs.py +386 -0
  11. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_revisions.py +38 -0
  12. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/uv.lock +1 -1
  13. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/.gitignore +0 -0
  14. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/CLAUDE.md +0 -0
  15. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/DOCX_EXEC_LAB.md +0 -0
  16. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/README.md +0 -0
  17. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_athena_extension.py +0 -0
  18. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_batching.py +0 -0
  19. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_buffer.py +0 -0
  20. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_citations.py +0 -0
  21. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_execution.py +0 -0
  22. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_http.py +0 -0
  23. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_http_doc.py +0 -0
  24. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_image_utils.py +0 -0
  25. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_postproc.py +0 -0
  26. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_ptc.py +0 -0
  27. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_references.py +0 -0
  28. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_run_order.py +0 -0
  29. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_table_styles.py +0 -0
  30. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/_timeouts.py +0 -0
  31. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/api.py +0 -0
  32. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/blkcntnr.py +0 -0
  33. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/bookmarks.py +0 -0
  34. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/charts.py +0 -0
  35. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/client.py +0 -0
  36. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/commands.py +0 -0
  37. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/comments.py +0 -0
  38. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/document.py +0 -0
  39. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/drawing/__init__.py +0 -0
  40. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/enum/__init__.py +0 -0
  41. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/enum/base.py +0 -0
  42. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/enum/dml.py +0 -0
  43. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/enum/section.py +0 -0
  44. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/enum/shape.py +0 -0
  45. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/enum/style.py +0 -0
  46. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/enum/table.py +0 -0
  47. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/enum/text.py +0 -0
  48. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/exceptions.py +0 -0
  49. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/fields.py +0 -0
  50. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/footnotes.py +0 -0
  51. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/image/__init__.py +0 -0
  52. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/image/exceptions.py +0 -0
  53. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/image/image.py +0 -0
  54. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/math.py +0 -0
  55. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/opc/__init__.py +0 -0
  56. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/opc/coreprops.py +0 -0
  57. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/oxml/__init__.py +0 -0
  58. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/revisions.py +0 -0
  59. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/sdt.py +0 -0
  60. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/section.py +0 -0
  61. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/session.py +0 -0
  62. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/settings.py +0 -0
  63. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/shape.py +0 -0
  64. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/shared.py +0 -0
  65. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/styles/__init__.py +0 -0
  66. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/styles/_table.py +0 -0
  67. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/styles/latent.py +0 -0
  68. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/styles/style.py +0 -0
  69. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/styles/styles.py +0 -0
  70. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/table.py +0 -0
  71. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/text/__init__.py +0 -0
  72. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/text/font.py +0 -0
  73. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/text/hyperlink.py +0 -0
  74. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/text/pagebreak.py +0 -0
  75. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/text/parfmt.py +0 -0
  76. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/text/tabstops.py +0 -0
  77. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/toc.py +0 -0
  78. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/docx/typing.py +0 -0
  79. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/scripts/docx_exec_lab.py +0 -0
  80. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
  81. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
  82. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/scripts/docx_exec_lab_server.py +0 -0
  83. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/scripts/dump_wire_fixtures.py +0 -0
  84. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/scripts/release.sh +0 -0
  85. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/scripts/round_trip_smoke.py +0 -0
  86. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/scripts/smoke_test_block_not_found.py +0 -0
  87. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/scripts/validate_find_replace_asset.py +0 -0
  88. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/__init__.py +0 -0
  89. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/conftest.py +0 -0
  90. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/METHODOLOGY.md +0 -0
  91. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/README.md +0 -0
  92. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/__init__.py +0 -0
  93. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/ab_probe_cases.py +0 -0
  94. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/ab_probe_runner.py +0 -0
  95. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/auto_gen_cases.py +0 -0
  96. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/binary_round_trip.py +0 -0
  97. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/cases.py +0 -0
  98. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/complex_cases.py +0 -0
  99. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/coverage_report.py +0 -0
  100. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/extract.py +0 -0
  101. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/extreme_cases.py +0 -0
  102. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/fake_session.py +0 -0
  103. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/firm_templates/README.md +0 -0
  104. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/firm_templates/__init__.py +0 -0
  105. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/firm_templates/_runner.py +0 -0
  106. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/firm_templates/extractor.py +0 -0
  107. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
  108. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
  109. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/local_runner.py +0 -0
  110. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/mega_cases.py +0 -0
  111. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshot.py +0 -0
  112. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
  113. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
  114. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
  115. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
  116. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
  117. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
  118. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
  119. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
  120. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
  121. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
  122. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
  123. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
  124. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
  125. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
  126. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
  127. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
  128. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
  129. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
  130. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
  131. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
  132. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
  133. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
  134. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
  135. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
  136. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
  137. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
  138. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
  139. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
  140. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
  141. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
  142. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
  143. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
  144. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
  145. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
  146. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
  147. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
  148. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
  149. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
  150. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
  151. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
  152. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
  153. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
  154. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
  155. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
  156. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
  157. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
  158. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
  159. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
  160. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
  161. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
  162. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
  163. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
  164. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
  165. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
  166. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
  167. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
  168. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
  169. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
  170. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
  171. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
  172. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
  173. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
  174. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
  175. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
  176. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
  177. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
  178. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
  179. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
  180. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
  181. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
  182. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
  183. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
  184. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
  185. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
  186. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
  187. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
  188. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
  189. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
  190. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
  191. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
  192. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
  193. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
  194. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
  195. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
  196. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
  197. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
  198. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
  199. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
  200. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
  201. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
  202. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
  203. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
  204. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
  205. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
  206. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
  207. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
  208. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
  209. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
  210. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
  211. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
  212. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
  213. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
  214. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
  215. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
  216. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
  217. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
  218. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
  219. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
  220. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
  221. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
  222. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
  223. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
  224. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
  225. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
  226. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
  227. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
  228. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
  229. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
  230. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
  231. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
  232. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
  233. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
  234. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
  235. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
  236. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
  237. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
  238. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
  239. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
  240. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
  241. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
  242. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
  243. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
  244. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
  245. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
  246. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
  247. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
  248. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
  249. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
  250. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
  251. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
  252. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
  253. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
  254. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
  255. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
  256. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
  257. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
  258. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
  259. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/ours_spec.json +0 -0
  260. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/parity_crawl.py +0 -0
  261. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/parity_diff.json +0 -0
  262. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/real_world_cases.py +0 -0
  263. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/round_trip_tests.py +0 -0
  264. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/stock_spec.json +0 -0
  265. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
  266. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/README.md +0 -0
  267. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/__init__.py +0 -0
  268. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/baseline_gaps.json +0 -0
  269. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/compare.py +0 -0
  270. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/intentional_deviations.json +0 -0
  271. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/introspect.py +0 -0
  272. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
  273. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/reports/gap_report.json +0 -0
  274. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/run_parity.py +0 -0
  275. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/snapshots/athena_latest.json +0 -0
  276. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
  277. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/parity/test_parity_gap.py +0 -0
  278. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_add_section.py +0 -0
  279. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_add_section_extract_items.py +0 -0
  280. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_athena_extension_creates_session_open.py +0 -0
  281. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_athena_extensions_contract.py +0 -0
  282. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_athena_extensions_registry.py +0 -0
  283. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_batching_perf.py +0 -0
  284. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_block_not_found_error.py +0 -0
  285. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_buffer.py +0 -0
  286. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
  287. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_add_table_not_supported.py +0 -0
  288. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_borders_spec.py +0 -0
  289. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_fastpath_runs.py +0 -0
  290. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
  291. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
  292. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_inner_format_stash.py +0 -0
  293. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_inner_run_format_stash.py +0 -0
  294. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_inner_run_guard.py +0 -0
  295. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_merge.py +0 -0
  296. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_paragraphs_listed.py +0 -0
  297. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_text_plain_fastpath.py +0 -0
  298. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_cell_text_replace_semantics.py +0 -0
  299. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_citations.py +0 -0
  300. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_collapsed_range_format.py +0 -0
  301. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_color_format_theme.py +0 -0
  302. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_command_dataclasses.py +0 -0
  303. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_commands.py +0 -0
  304. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_comments.py +0 -0
  305. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_core_properties.py +0 -0
  306. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_document_asset_id_property.py +0 -0
  307. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_document_clear.py +0 -0
  308. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_document_create.py +0 -0
  309. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_document_create_from_template.py +0 -0
  310. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_document_factory_validation.py +0 -0
  311. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_docx_exec_lab.py +0 -0
  312. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_docx_exec_lab_server.py +0 -0
  313. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_drawing_image.py +0 -0
  314. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
  315. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_enum_parity.py +0 -0
  316. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_execution_scope.py +0 -0
  317. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_find_replace_session_open.py +0 -0
  318. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_http_transport.py +0 -0
  319. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_hyperlink_coalescing.py +0 -0
  320. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_image_url_data_uri.py +0 -0
  321. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_inline_shape_alt_text.py +0 -0
  322. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_inline_shape_size.py +0 -0
  323. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_insert_deferred.py +0 -0
  324. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_iter_inner_content.py +0 -0
  325. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_list_styles.py +0 -0
  326. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_merged_cell_secondary_slot.py +0 -0
  327. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_merged_cells.py +0 -0
  328. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_missing_modules.py +0 -0
  329. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_oxml_shim.py +0 -0
  330. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_page_break.py +0 -0
  331. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_paragraph_runs_hyperlinks.py +0 -0
  332. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_paragraph_text_len_cache.py +0 -0
  333. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_parity_misc.py +0 -0
  334. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_parity_round2.py +0 -0
  335. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_partial_failure_cascade.py +0 -0
  336. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_pdf_export_and_page_renders.py +0 -0
  337. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_phase_a_behavior.py +0 -0
  338. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_phase_b_headers_footers.py +0 -0
  339. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_phase_c_tables.py +0 -0
  340. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_positional_cell_id.py +0 -0
  341. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
  342. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
  343. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_postproc_ref_restore.py +0 -0
  344. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_pr19766_review_fixes.py +0 -0
  345. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_ptc.py +0 -0
  346. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_python_docx_api_parity.py +0 -0
  347. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_restart_numbering.py +0 -0
  348. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_run_add_picture_anchor.py +0 -0
  349. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_run_formatting_wire.py +0 -0
  350. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_run_identity.py +0 -0
  351. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_run_mark_inheritance.py +0 -0
  352. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_run_table_style_objects.py +0 -0
  353. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_run_text_python_docx.py +0 -0
  354. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_run_text_refreshes_paragraph.py +0 -0
  355. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_sdk_reads_served.py +0 -0
  356. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_section_read_lengths.py +0 -0
  357. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
  358. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_add_picture.py +0 -0
  359. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_add_run.py +0 -0
  360. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
  361. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
  362. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_comments_get.py +0 -0
  363. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_document_audit.py +0 -0
  364. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_document_element.py +0 -0
  365. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_enum_section.py +0 -0
  366. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_font_audit.py +0 -0
  367. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_header_footer.py +0 -0
  368. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_hyperlink.py +0 -0
  369. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_inline_shape.py +0 -0
  370. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
  371. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_misc.py +0 -0
  372. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
  373. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_paragraph_style.py +0 -0
  374. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
  375. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_parfmt.py +0 -0
  376. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_row_col_cell.py +0 -0
  377. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_run_add_break.py +0 -0
  378. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
  379. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_run_style.py +0 -0
  380. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_run_style_strict.py +0 -0
  381. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_run_text.py +0 -0
  382. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_run_underline.py +0 -0
  383. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_section_audit.py +0 -0
  384. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_section_dimensions.py +0 -0
  385. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_section_onoff.py +0 -0
  386. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_settings.py +0 -0
  387. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_shared_audit.py +0 -0
  388. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_style.py +0 -0
  389. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_styles.py +0 -0
  390. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_table_audit.py +0 -0
  391. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_table_cell.py +0 -0
  392. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_table_dimensions.py +0 -0
  393. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_silent_stub_table_layout.py +0 -0
  394. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_smoke_integration.py +0 -0
  395. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_style_acceptance.py +0 -0
  396. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_style_font.py +0 -0
  397. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_style_formatting_writes.py +0 -0
  398. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_style_metadata_writes.py +0 -0
  399. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_style_names.py +0 -0
  400. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_style_setters_contract.py +0 -0
  401. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_table_set_cell_perf.py +0 -0
  402. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_table_style_id_resolution.py +0 -0
  403. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_temporarily_unavailable.py +0 -0
  404. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_text_control_characters.py +0 -0
  405. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_uv_lock_version.py +0 -0
  406. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_validate_find_replace_asset_script.py +0 -0
  407. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_wire_contract.py +0 -0
  408. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_xml_attr_guard.py +0 -0
  409. {athena_python_docx-0.26.0 → athena_python_docx-0.27.0}/tests/test_zod_wire_contract.py +0 -0
@@ -224,6 +224,11 @@ not file-backed. Each is documented in the relevant docstring.
224
224
  `Revision.reject()` resolve a single change. python-docx upstream
225
225
  has zero track-changes API, so this entire surface is an Athena
226
226
  extension wired to DOCX Studio's `doc.trackChanges.{list,get,decide}`.
227
+ A run's tracked formatting change (`w:rPrChange`) and a paragraph's
228
+ (`w:pPrChange`) are `"format"` revisions; `reject()` gives the run or
229
+ paragraph exactly the earlier properties the change recorded
230
+ (`tests/test_revisions.py`, docx-studio's
231
+ `tracked-run-property-changes.test.ts`).
227
232
 
228
233
  - **Style writes refuse what would write a broken document.** python-docx
229
234
  writes these into `styles.xml`; the SDK raises before anything is sent,
@@ -968,6 +973,44 @@ architecture or a DOCX Studio constraint:
968
973
  Markup) show insertions and hide deletions, as these reads do.
969
974
  `doc.revisions` lists every change (`tests/test_run_text_python_docx.py`).
970
975
 
976
+ - **A field's runs read as python-docx reads them; some writes to them
977
+ differ.** `Paragraph.text` includes a complex field's displayed result and
978
+ `Paragraph.runs` holds its begin, field code, separate, result and end runs
979
+ (each with its own formatting), as python-docx reads the `w:r` children of
980
+ a `w:p`; a simple field (`w:fldSimple`) reads as nothing, and a field in a
981
+ link is the link's, as in python-docx. Each paragraph of a field that spans
982
+ paragraphs (a table of contents) holds the field character runs it has
983
+ (`tests/test_field_reads_python_docx_parity.py`, against stock python-docx
984
+ on the same `.docx`; `DOCX_STUDIO_LIMITATIONS.md` § 27). A field takes no
985
+ room in the text-address space, so its runs stand at one offset. Writes:
986
+ - setting a result run's `.text` changes the field's displayed result,
987
+ as in python-docx (`Replace` with a `field` target), but the export
988
+ writes a changed result of several runs as one run, in the first run's
989
+ formatting, and a result run read before the field's result changed
990
+ (through another read's run, or by a collaborator) raises
991
+ `FieldRunWriteError` rather than write over that change, where
992
+ python-docx's run is live;
993
+ - formatting a result run formats the field's whole result (`FormatApply`
994
+ with a `field` target), from its source formatting, where python-docx
995
+ formats that run alone;
996
+ - `.text` (and `clear()`) on a field character or field code run raises
997
+ `docx.errors.FieldRunWriteError` naming the field: python-docx replaces
998
+ the run's `w:fldChar` or `w:instrText` with the text, which breaks the
999
+ field;
1000
+ - formatting set on a field character or field code run is not applied and
1001
+ emits `PendingFieldRunFormatWarning` (those runs show nothing; docx-studio
1002
+ has no command that writes their own `w:rPr`), where python-docx writes
1003
+ it, so `for run in p.runs: run.bold = True` formats the text and the
1004
+ field's result and warns for the rest;
1005
+ - `add_text`, `add_tab`, `add_break`, `add_picture`, `add_field`,
1006
+ `add_footnote`, `add_endnote`, `add_math` and `add_cross_reference` on
1007
+ any of a field's runs raise `FieldRunWriteError`; python-docx appends to
1008
+ that `w:r`.
1009
+ A field that `Run.add_field` adds is a simple field, so it reads as no
1010
+ runs and no text, though docx-studio's export writes it as a complex field.
1011
+ Against a docx-studio that predates `fieldRun` reads, a field reads as no
1012
+ runs and no text, as before (`tests/test_field_runs.py`).
1013
+
971
1014
  ### If you need a deviation
972
1015
 
973
1016
  If there is a genuine technical reason why a deviation from python-docx is necessary:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: athena-python-docx
3
- Version: 0.26.0
3
+ Version: 0.27.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.26.0"
9
+ __version__ = "0.27.0"
10
10
 
11
11
  from docx.api import Document
12
12
  from docx._buffer import discard_all, flush_all
@@ -129,6 +129,16 @@ class NotNumberedListItemError(DocxError, ValueError):
129
129
  """
130
130
 
131
131
 
132
+ class FieldRunWriteError(DocxError, ValueError):
133
+ """Raised for a write to a run of a Word field that the SDK refuses:
134
+ text written to a field character or field code run (python-docx's write
135
+ replaces the run's ``w:fldChar`` or ``w:instrText`` and breaks the field),
136
+ and content appended to any of a field's runs. The message names the
137
+ field. Subclasses :class:`ValueError` so ``except ValueError`` also
138
+ catches it.
139
+ """
140
+
141
+
132
142
  class SessionError(DocxError):
133
143
  """Raised when the DOCX Studio SDK session cannot be established or used."""
134
144
 
@@ -199,8 +199,10 @@ def _extract_text(info: object, *, final: bool = False) -> str:
199
199
  returns: tracked changes as Word's final view shows them, a deletion's
200
200
  runs left out and an insertion's read (python-docx leaves out both, as
201
201
  it reads only a ``w:p``'s direct ``w:r`` and ``w:hyperlink`` children),
202
- and each run's text read as python-docx's ``Run.text`` reads it
203
- (:func:`_word_text`).
202
+ each run's text read as python-docx's ``Run.text`` reads it
203
+ (:func:`_word_text`), and a complex field's displayed result (the text of
204
+ its result runs, never its field code), which takes no room in the
205
+ text-address space.
204
206
  """
205
207
  if not isinstance(info, dict):
206
208
  return ""
@@ -217,6 +219,12 @@ def _extract_text(info: object, *, final: bool = False) -> str:
217
219
  for inline in inlines:
218
220
  if not isinstance(inline, dict):
219
221
  continue
222
+ field_run: object = inline.get("fieldRun")
223
+ if isinstance(field_run, dict):
224
+ shown: object = field_run.get("text")
225
+ if final and isinstance(shown, str) and not _deleted(field_run):
226
+ parts.append(_word_text(shown, _symbol_font(field_run)))
227
+ continue
220
228
  run: object = inline.get("run")
221
229
  if isinstance(run, dict):
222
230
  text: object = run.get("text")
@@ -273,6 +281,61 @@ def _walk_inlines(info: object) -> list[dict]:
273
281
  return []
274
282
 
275
283
 
284
+ def _inline_width(inline: dict) -> int:
285
+ """The room an inline takes in the text-address space (see ``runs``)."""
286
+ run: object = inline.get("run")
287
+ if isinstance(run, dict):
288
+ text: object = run.get("text")
289
+ return len(text) if isinstance(text, str) else 0
290
+ kind: object = inline.get("type")
291
+ if kind == "text":
292
+ flat: object = inline.get("text")
293
+ return len(flat) if isinstance(flat, str) else 0
294
+ return 1 if kind in ("tab", "lineBreak") else 0
295
+
296
+
297
+ def _field_run_proxies(
298
+ paragraph: "Paragraph", inlines: list[dict], *, marks_known: bool
299
+ ) -> dict[int, "Run"]:
300
+ """The :class:`Run` of each field run of ``inlines`` (a ``fieldRun``
301
+ read), by its index there: a field's begin, code, separate, result or end
302
+ run, which python-docx reads as a run of the paragraph. A field takes no
303
+ room in the text-address space, so each stands at one offset. All are built
304
+ before any is handed out, so each result run of a field holds all of them
305
+ (``_field_results``) and setting one's text keeps the others'. A run of a
306
+ tracked deletion has none, as Word's final view shows it.
307
+ """
308
+ from docx.text.run import Run
309
+
310
+ proxies: dict[int, Run] = {}
311
+ results: dict[int, list[Run]] = {}
312
+ offset = 0
313
+ for position, inline in enumerate(inlines):
314
+ field_run: object = inline.get("fieldRun")
315
+ if not isinstance(field_run, dict):
316
+ offset += _inline_width(inline)
317
+ continue
318
+ if _deleted(field_run):
319
+ continue
320
+ run = Run(
321
+ session=paragraph._session,
322
+ block_id=paragraph._node_id,
323
+ range_=(offset, offset),
324
+ inline=field_run,
325
+ paragraph=paragraph,
326
+ in_cell=paragraph._in_cell,
327
+ cell_ooxml_address=paragraph._cell_ooxml_address,
328
+ )
329
+ run._marks_known = marks_known
330
+ index: object = field_run.get("fieldIndex")
331
+ if field_run.get("part") == "result" and isinstance(index, int):
332
+ siblings = results.setdefault(index, [])
333
+ siblings.append(run)
334
+ run._field_results = siblings # type: ignore[attr-defined]
335
+ proxies[position] = run
336
+ return proxies
337
+
338
+
276
339
  def _create_page_break_paragraph(session: Any, client_node_id: str, *, at: dict) -> None:
277
340
  """Create, at ``at``, the paragraph python-docx writes for a page break:
278
341
  ``<w:p><w:r><w:br w:type="page"/></w:r></w:p>``.
@@ -505,9 +568,10 @@ class Paragraph:
505
568
  def text(self) -> str:
506
569
  """The paragraph's text: its runs' and hyperlinks' text as
507
570
  python-docx's ``Paragraph.text`` reads it (a non-breaking hyphen as
508
- ``-``, a soft hyphen and a symbol left out), with tracked changes as
509
- Word's final view shows them: an insertion's text in, a deletion's
510
- out."""
571
+ ``-``, a soft hyphen and a symbol left out, a complex field's displayed
572
+ result in and its field code out, a simple field's result out), with
573
+ tracked changes as Word's final view shows them: an insertion's text
574
+ in, a deletion's out."""
511
575
  return _extract_text(self._node_info(), final=True)
512
576
 
513
577
  @text.setter
@@ -565,13 +629,19 @@ class Paragraph:
565
629
  As in python-docx, these are the paragraph's own runs: the runs of a
566
630
  hyperlink are the hyperlink's (:attr:`hyperlinks`,
567
631
  :meth:`iter_inner_content`), while :attr:`text` includes its text.
632
+ A complex field's runs are the paragraph's runs, in order: its begin,
633
+ field code and separate runs (``""``), its result runs with their
634
+ text, and its end run (``""``), each with its own formatting; so are
635
+ the field character runs of a field that begins or ends in another
636
+ paragraph (a table of contents). A simple field (``w:fldSimple``) has
637
+ no runs here, as in python-docx.
568
638
 
569
639
  Run offsets are computed against DOCX Studio's text-address space,
570
640
  which counts tab and lineBreak inlines as 1 character each
571
641
  (so ``insert_tab({offset})`` and the run-range arithmetic stay
572
- consistent). A run of a tracked deletion is no run of the
573
- paragraph's, as Word's final view shows it, but keeps its offsets;
574
- a tracked insertion's runs are runs.
642
+ consistent). A field's runs take no room in it. A run of a tracked
643
+ deletion is no run of the paragraph's, as Word's final view shows it,
644
+ but keeps its offsets; a tracked insertion's runs are runs.
575
645
  """
576
646
  from docx.text.run import Run
577
647
 
@@ -602,8 +672,13 @@ class Paragraph:
602
672
  inlines: list[dict] = _walk_inlines(info)
603
673
  marks_known: bool = _reports_run_marks(info)
604
674
  runs: list["Run"] = []
675
+ field_runs = _field_run_proxies(self, inlines, marks_known=marks_known)
605
676
  offset: int = 0
606
- for inline in inlines:
677
+ for position, inline in enumerate(inlines):
678
+ if isinstance(inline.get("fieldRun"), dict):
679
+ if position in field_runs:
680
+ runs.append(field_runs[position])
681
+ continue
607
682
  drawing: object = inline.get("drawing")
608
683
  if isinstance(drawing, dict):
609
684
  # A picture: python-docx's run holding a ``w:drawing``. It
@@ -1012,6 +1087,7 @@ class Paragraph:
1012
1087
  self._session.doc.get_node_by_id({"id": self._node_id}),
1013
1088
  )
1014
1089
  inlines = _walk_inlines(info)
1090
+ field_runs = _field_run_proxies(self, inlines, marks_known=_reports_run_marks(info))
1015
1091
  offset: int = 0
1016
1092
  # Accumulator for the in-flight hyperlink span. Mirrors the
1017
1093
  # coalesce logic in ``Paragraph.hyperlinks`` — yields a single
@@ -1032,9 +1108,17 @@ class Paragraph:
1032
1108
  segments=segments,
1033
1109
  )
1034
1110
 
1035
- for inline in inlines:
1111
+ for position, inline in enumerate(inlines):
1036
1112
  if not isinstance(inline, dict):
1037
1113
  continue
1114
+ if isinstance(inline.get("fieldRun"), dict):
1115
+ # A field's ``w:r`` is a run of the paragraph, never of a link.
1116
+ flushed = _flush_current()
1117
+ if flushed is not None:
1118
+ yield flushed
1119
+ if position in field_runs:
1120
+ yield field_runs[position]
1121
+ continue
1038
1122
  # Two DOCX Studio inline shapes are accepted by the rest of the
1039
1123
  # paragraph code (``_extract_text``, ``runs``):
1040
1124
  # 1. typed-wrapper: {"run": {"text": "...", ...}}
@@ -70,6 +70,20 @@ class PendingColorTypeWarning(UserWarning):
70
70
  ``theme_color`` without a warning."""
71
71
 
72
72
 
73
+ class PendingFieldRunFormatWarning(UserWarning):
74
+ """Emitted when formatting is set on a field character or field code run
75
+ (a field's begin, code, separate or end run). python-docx writes it into
76
+ that run's ``w:rPr``, which shows nothing; docx-studio keeps those runs'
77
+ own properties from the source and has no command that writes them, so
78
+ the setter changes nothing. Formatting a result run formats the field's
79
+ result.
80
+ """
81
+
82
+
83
+ #: The parts of a field whose runs ``Paragraph.runs`` reads (``fieldRun`` reads).
84
+ _FIELD_RUN_PARTS: frozenset[str] = frozenset({"begin", "code", "separate", "result", "end"})
85
+
86
+
73
87
  if TYPE_CHECKING:
74
88
  from docx.client import Session
75
89
  from docx.enum.dml import MSO_COLOR_TYPE, MSO_THEME_COLOR
@@ -399,6 +413,114 @@ class Run:
399
413
  self._note_paragraph_len_delta(delta)
400
414
  note_length_change(self, at, delta, replaced=replaced, before=_before_image(sent))
401
415
 
416
+ def _field_part(self) -> str | None:
417
+ """The part of a field this run is (``begin``, ``code``, ``separate``,
418
+ ``result`` or ``end``), for a run ``Paragraph.runs`` read from a field;
419
+ else None."""
420
+ part: object = getattr(self, "_inline_snapshot", {}).get("part")
421
+ return part if isinstance(part, str) and part in _FIELD_RUN_PARTS else None
422
+
423
+ def _field_name(self) -> str:
424
+ instruction: object = self._inline_snapshot.get("instruction")
425
+ if isinstance(instruction, str) and instruction:
426
+ return f"the field {{{instruction}}}"
427
+ return "a field that begins or ends in another paragraph"
428
+
429
+ def _refuse_field_write(self, op: str) -> None:
430
+ """Raise :class:`docx.errors.FieldRunWriteError` for ``op`` on a run of a field."""
431
+ part = self._field_part()
432
+ if part is None:
433
+ return
434
+ from docx.errors import FieldRunWriteError
435
+
436
+ if op == "text":
437
+ raise FieldRunWriteError(
438
+ f"Run.text = ... on the {part} run of {self._field_name()}: python-docx "
439
+ "replaces the run's field character or field code with the text, which "
440
+ "breaks the field. Set the text of the field's result run to change "
441
+ "what it shows.",
442
+ )
443
+ raise FieldRunWriteError(
444
+ f"Run.{op} on the {part} run of {self._field_name()}: content cannot be "
445
+ "added to a field's run here. Add a run after the field "
446
+ "(Paragraph.add_run), or set a result run's text.",
447
+ )
448
+
449
+ def _field_target(self) -> dict | None:
450
+ """The ``field`` target of this run's field node, for a result run."""
451
+ index: object = self._inline_snapshot.get("fieldIndex")
452
+ if self._field_part() != "result" or not isinstance(index, int):
453
+ return None
454
+ return {"kind": "field", "blockId": self._block_id, "field": index}
455
+
456
+ def _set_field_text(self, value: str) -> None:
457
+ """``Run.text = value`` on a result run: the field shows ``value`` in
458
+ this run's place, its other result runs' text kept, and its field code
459
+ stays, as python-docx writes it.
460
+
461
+ The field's result is written whole, so it is composed from the result
462
+ runs of this run's read only while the field still shows what they
463
+ hold: a field another read's run (or a collaborator) changed since
464
+ raises :class:`docx.errors.FieldRunWriteError` instead of losing that
465
+ change.
466
+ """
467
+ target = self._field_target()
468
+ if target is None:
469
+ self._refuse_field_write("text")
470
+ return
471
+ siblings: list[Run] = getattr(self, "_field_results", None) or [self]
472
+ live: object = run_sync(self._session.doc.get_node_by_id({"id": self._block_id}))
473
+ if self._shown_result(live) != "".join(run._raw_text() for run in siblings):
474
+ from docx.errors import FieldRunWriteError
475
+
476
+ raise FieldRunWriteError(
477
+ f"Run.text = ... on a result run of {self._field_name()}: the field's "
478
+ "result changed since this run was read. Read the paragraph's runs "
479
+ "again and set the text of a run from that read.",
480
+ )
481
+ shown = "".join(value if run is self else run._raw_text() for run in siblings)
482
+ run_sync(self._session.doc.replace({"target": target, "text": shown}))
483
+ self._inline_snapshot["text"] = value
484
+ self._note_paragraph_len_delta(0)
485
+
486
+ def _shown_result(self, info: object) -> str:
487
+ """The displayed result a paragraph read gives this run's field."""
488
+ from docx.text.paragraph import _walk_inlines
489
+
490
+ index: object = self._inline_snapshot.get("fieldIndex")
491
+ parts: list[str] = []
492
+ for inline in _walk_inlines(info):
493
+ field_run: object = inline.get("fieldRun")
494
+ if (
495
+ isinstance(field_run, dict)
496
+ and field_run.get("part") == "result"
497
+ and field_run.get("fieldIndex") == index
498
+ ):
499
+ text: object = field_run.get("text")
500
+ parts.append(text if isinstance(text, str) else "")
501
+ return "".join(parts)
502
+
503
+ def _format_field(self, kwargs: dict) -> None:
504
+ """Formatting set on a run of a field: a result run's formats the
505
+ field node, so every result run of the field takes it."""
506
+ target = self._field_target()
507
+ if target is None:
508
+ import warnings
509
+
510
+ warnings.warn(
511
+ f"Run formatting on the {self._field_part()} run of {self._field_name()} "
512
+ "was not applied: docx-studio keeps the field character and field code "
513
+ "runs' own properties from the source and cannot write them (they show "
514
+ "nothing). Format a result run to format the field's result.",
515
+ PendingFieldRunFormatWarning,
516
+ stacklevel=4,
517
+ )
518
+ return
519
+ run_sync(self._session.doc.format.apply({"target": target, "inline": kwargs}))
520
+ for run in getattr(self, "_field_results", None) or [self]:
521
+ run._note_inline_applied(kwargs)
522
+ self._note_paragraph_len_delta(0)
523
+
402
524
  @property
403
525
  def _text_range(self) -> dict:
404
526
  return {
@@ -449,7 +571,11 @@ class Run:
449
571
  return self._inline_snapshot.get("symbolFont") or self._read_inline_attr("fontFamily")
450
572
 
451
573
  def _raw_text(self) -> str:
452
- """The run's slice of its paragraph's text-address space."""
574
+ """The run's slice of its paragraph's text-address space; for a run of
575
+ a field, which takes none, the text its read holds (a result run's)."""
576
+ if self._field_part() is not None:
577
+ shown: object = self._inline_snapshot.get("text")
578
+ return shown if isinstance(shown, str) else ""
453
579
  # Fast-path runs (see ``Table._fastpath_cell_text``) carry their
454
580
  # known text — the whole point is zero read round-trips. Mutations
455
581
  # through this proxy keep the attr in sync or clear it.
@@ -525,6 +651,9 @@ class Run:
525
651
  f"Run.text requires str; got {type(value).__name__} "
526
652
  f"{value!r}",
527
653
  )
654
+ if self._field_part() is not None:
655
+ self._set_field_text(value)
656
+ return
528
657
  if self._check_cell_inner("text"):
529
658
  # Warn-and-skip: nothing mutated, so the fast-path text cache
530
659
  # (when present) still reflects reality — keep it.
@@ -594,6 +723,7 @@ class Run:
594
723
  f"Run.add_text requires str; got {type(text).__name__} "
595
724
  f"{text!r}",
596
725
  )
726
+ self._refuse_field_write("add_text")
597
727
  if self._check_cell_inner("add_text"):
598
728
  # Return a stub Run with the same in_cell flag so chained
599
729
  # ops on the result also short-circuit cleanly. Propagate
@@ -678,6 +808,7 @@ class Run:
678
808
 
679
809
  def add_tab(self) -> None:
680
810
  """Insert a tab character at the end of the run, in its formatting."""
811
+ self._refuse_field_write("add_tab")
681
812
  if self._check_cell_inner("add_tab"):
682
813
  return
683
814
  end: int = self._range[1]
@@ -721,6 +852,7 @@ class Run:
721
852
  """
722
853
  import warnings
723
854
 
855
+ self._refuse_field_write("add_break")
724
856
  if self._check_cell_inner("add_break"):
725
857
  return
726
858
  end: int = self._range[1]
@@ -843,6 +975,7 @@ class Run:
843
975
  from docx.shape import InlineShape
844
976
  import base64
845
977
 
978
+ self._refuse_field_write("add_picture")
846
979
  image_bytes, _fallback_path = load_image_bytes(image_path_or_stream)
847
980
  # The image's signature, as python-docx reads it: a stream with none
848
981
  # (an empty one, or one read to its end already) raises
@@ -918,6 +1051,7 @@ class Run:
918
1051
  from docx.commands import CreateField
919
1052
  from docx.fields import Field
920
1053
 
1054
+ self._refuse_field_write("add_field")
921
1055
  if self._check_cell_inner("add_field"):
922
1056
  return Field(
923
1057
  session=self._session,
@@ -1000,6 +1134,7 @@ class Run:
1000
1134
  from docx.commands import CreateFootnote
1001
1135
  from docx.footnotes import Footnote
1002
1136
 
1137
+ self._refuse_field_write("add_footnote")
1003
1138
  end: int = self._range[1]
1004
1139
  cursor: dict = {
1005
1140
  "kind": "text",
@@ -1036,6 +1171,7 @@ class Run:
1036
1171
  from docx.commands import CreateEndnote
1037
1172
  from docx.footnotes import Endnote
1038
1173
 
1174
+ self._refuse_field_write("add_endnote")
1039
1175
  end: int = self._range[1]
1040
1176
  cursor: dict = {
1041
1177
  "kind": "text",
@@ -1086,6 +1222,7 @@ class Run:
1086
1222
  from docx.commands import CreateCrossReference
1087
1223
  from docx.fields import Field
1088
1224
 
1225
+ self._refuse_field_write("add_cross_reference")
1089
1226
  end: int = self._range[1]
1090
1227
  cursor: dict = {
1091
1228
  "kind": "text",
@@ -1132,6 +1269,7 @@ class Run:
1132
1269
  from docx.commands import CreateMath
1133
1270
  from docx.math import MathEquation
1134
1271
 
1272
+ self._refuse_field_write("add_math")
1135
1273
  end: int = self._range[1]
1136
1274
  cursor: dict = {
1137
1275
  "kind": "text",
@@ -1298,6 +1436,9 @@ class Run:
1298
1436
  # target range.`` Stash the attrs and apply them once the run
1299
1437
  # has text — see ``_pending_inline`` in ``__init__`` for the
1300
1438
  # python-docx parity rationale.
1439
+ if self._field_part() is not None:
1440
+ self._format_field(kwargs)
1441
+ return
1301
1442
  if getattr(self, "_in_cell", False):
1302
1443
  # Stash for the post-export OOXML rewrite FIRST so the formatting
1303
1444
  # lands in the exported ``.docx`` regardless of engine (and is the
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "athena-python-docx"
7
- version = "0.26.0"
7
+ version = "0.27.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"