athena-python-docx 0.42.0__tar.gz → 0.43.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 (499) hide show
  1. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/PKG-INFO +1 -1
  2. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/__init__.py +1 -1
  3. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_buffer.py +71 -7
  4. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_http.py +52 -34
  5. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_http_doc.py +18 -1
  6. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/document.py +9 -2
  7. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/pyproject.toml +1 -1
  8. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/live/fixture_scripts.py +38 -0
  9. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_block_not_found_error.py +24 -0
  10. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_buffer.py +86 -0
  11. athena_python_docx-0.43.0/tests/test_create_workspace.py +82 -0
  12. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_document_create.py +74 -15
  13. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_document_create_from_template.py +93 -0
  14. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/uv.lock +1 -1
  15. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/.gitignore +0 -0
  16. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/AGENTS.md +0 -0
  17. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/CLAUDE.md +0 -0
  18. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/DOCX_EXEC_LAB.md +0 -0
  19. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/README.md +0 -0
  20. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_athena_extension.py +0 -0
  21. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_batching.py +0 -0
  22. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_capabilities.py +0 -0
  23. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_citations.py +0 -0
  24. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_execution.py +0 -0
  25. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_image_utils.py +0 -0
  26. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_postproc.py +0 -0
  27. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_proxy.py +0 -0
  28. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_ptc.py +0 -0
  29. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_references.py +0 -0
  30. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_run_order.py +0 -0
  31. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_table_styles.py +0 -0
  32. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_timeouts.py +0 -0
  33. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_tracked.py +0 -0
  34. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/_xml_text.py +0 -0
  35. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/api.py +0 -0
  36. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/blkcntnr.py +0 -0
  37. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/bookmarks.py +0 -0
  38. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/charts.py +0 -0
  39. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/client.py +0 -0
  40. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/commands.py +0 -0
  41. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/comments.py +0 -0
  42. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/drawing/__init__.py +0 -0
  43. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/enum/__init__.py +0 -0
  44. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/enum/base.py +0 -0
  45. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/enum/dml.py +0 -0
  46. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/enum/section.py +0 -0
  47. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/enum/shape.py +0 -0
  48. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/enum/style.py +0 -0
  49. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/enum/table.py +0 -0
  50. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/enum/text.py +0 -0
  51. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/errors.py +0 -0
  52. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/exceptions.py +0 -0
  53. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/fields.py +0 -0
  54. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/footnotes.py +0 -0
  55. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/image/__init__.py +0 -0
  56. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/image/exceptions.py +0 -0
  57. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/image/image.py +0 -0
  58. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/math.py +0 -0
  59. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/opc/__init__.py +0 -0
  60. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/opc/coreprops.py +0 -0
  61. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/oxml/__init__.py +0 -0
  62. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/revisions.py +0 -0
  63. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/sdt.py +0 -0
  64. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/section.py +0 -0
  65. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/session.py +0 -0
  66. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/settings.py +0 -0
  67. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/shape.py +0 -0
  68. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/shared.py +0 -0
  69. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/styles/__init__.py +0 -0
  70. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/styles/_table.py +0 -0
  71. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/styles/latent.py +0 -0
  72. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/styles/style.py +0 -0
  73. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/styles/styles.py +0 -0
  74. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/table.py +0 -0
  75. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/text/__init__.py +0 -0
  76. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/text/font.py +0 -0
  77. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/text/hyperlink.py +0 -0
  78. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/text/pagebreak.py +0 -0
  79. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/text/paragraph.py +0 -0
  80. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/text/parfmt.py +0 -0
  81. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/text/run.py +0 -0
  82. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/text/tabstops.py +0 -0
  83. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/toc.py +0 -0
  84. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/docx/typing.py +0 -0
  85. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/scripts/docx_exec_lab.py +0 -0
  86. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
  87. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
  88. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/scripts/docx_exec_lab_server.py +0 -0
  89. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/scripts/dump_wire_fixtures.py +0 -0
  90. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/scripts/release.sh +0 -0
  91. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/scripts/round_trip_smoke.py +0 -0
  92. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/scripts/smoke_test_block_not_found.py +0 -0
  93. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/scripts/validate_find_replace_asset.py +0 -0
  94. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/__init__.py +0 -0
  95. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/conftest.py +0 -0
  96. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/METHODOLOGY.md +0 -0
  97. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/README.md +0 -0
  98. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/__init__.py +0 -0
  99. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/__init__.py +0 -0
  100. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/agent_documents.py +0 -0
  101. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/default_style_run_formatting.py +0 -0
  102. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/header_footer_pictures.py +0 -0
  103. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/less_used_writes.py +0 -0
  104. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/merged_cell_identity.py +0 -0
  105. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/paragraph_text_setter.py +0 -0
  106. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/picture_runs.py +0 -0
  107. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/proxy_equality.py +0 -0
  108. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/return_types.py +0 -0
  109. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/xml_incompatible_text.py +0 -0
  110. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe/zero_and_negative_indents.py +0 -0
  111. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe_cases.py +0 -0
  112. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ab_probe_runner.py +0 -0
  113. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/auto_gen_cases.py +0 -0
  114. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/binary_round_trip.py +0 -0
  115. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/cases.py +0 -0
  116. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/complex_cases.py +0 -0
  117. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/coverage_report.py +0 -0
  118. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/extract.py +0 -0
  119. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/extreme_cases.py +0 -0
  120. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/fake_session.py +0 -0
  121. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/firm_templates/README.md +0 -0
  122. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/firm_templates/__init__.py +0 -0
  123. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/firm_templates/_runner.py +0 -0
  124. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/firm_templates/extractor.py +0 -0
  125. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
  126. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
  127. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/local_runner.py +0 -0
  128. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/mega_cases.py +0 -0
  129. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshot.py +0 -0
  130. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
  131. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
  132. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
  133. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
  134. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
  135. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
  136. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
  137. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
  138. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
  139. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
  140. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
  141. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
  142. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
  143. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
  144. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
  145. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
  146. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
  147. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
  148. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
  149. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
  150. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
  151. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
  152. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
  153. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
  154. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
  155. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
  156. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
  157. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
  158. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
  159. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
  160. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
  161. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
  162. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
  163. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
  164. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
  165. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
  166. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
  167. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
  168. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
  169. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
  170. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
  171. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
  172. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
  173. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
  174. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
  175. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
  176. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
  177. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
  178. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
  179. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
  180. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
  181. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
  182. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
  183. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
  184. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
  185. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
  186. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
  187. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
  188. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
  189. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
  190. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
  191. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
  192. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
  193. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
  194. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
  195. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
  196. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
  197. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
  198. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
  199. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
  200. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
  201. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
  202. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
  203. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
  204. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
  205. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
  206. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
  207. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
  208. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
  209. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
  210. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
  211. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
  212. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
  213. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
  214. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
  215. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
  216. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
  217. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
  218. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
  219. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
  220. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
  221. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
  222. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
  223. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
  224. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
  225. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
  226. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
  227. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
  228. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
  229. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
  230. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
  231. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
  232. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
  233. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
  234. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
  235. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
  236. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
  237. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
  238. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
  239. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
  240. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
  241. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
  242. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
  243. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
  244. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
  245. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
  246. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
  247. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
  248. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
  249. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
  250. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
  251. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
  252. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
  253. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
  254. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
  255. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
  256. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
  257. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
  258. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
  259. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
  260. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
  261. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
  262. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
  263. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
  264. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
  265. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
  266. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
  267. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
  268. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
  269. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
  270. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
  271. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
  272. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
  273. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
  274. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
  275. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
  276. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
  277. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw16_header_footer_tables.json +0 -0
  278. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw17_header_letterhead_filled.json +0 -0
  279. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw18_header_table_properties.json +0 -0
  280. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw19_header_table_rows_columns.json +0 -0
  281. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/op_snapshots/rw20_header_table_merges.json +0 -0
  282. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/ours_spec.json +0 -0
  283. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/parity_crawl.py +0 -0
  284. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/parity_diff.json +0 -0
  285. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/real_world_cases.py +0 -0
  286. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/round_trip_tests.py +0 -0
  287. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/stock_spec.json +0 -0
  288. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
  289. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fixtures/field_reads.json +0 -0
  290. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fixtures/note_reference_reads.json +0 -0
  291. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fixtures/ruby_reads.json +0 -0
  292. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/fixtures/symbol_reads.json +0 -0
  293. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/live/__init__.py +0 -0
  294. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/live/baseline.json +0 -0
  295. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/live/reader.py +0 -0
  296. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/live/runner.py +0 -0
  297. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/live/sdk_driver.py +0 -0
  298. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/live/stock_driver.py +0 -0
  299. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/live/test_live_runner.py +0 -0
  300. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/README.md +0 -0
  301. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/__init__.py +0 -0
  302. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/baseline_gaps.json +0 -0
  303. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/compare.py +0 -0
  304. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/intentional_deviations.json +0 -0
  305. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/introspect.py +0 -0
  306. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
  307. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/reports/gap_report.json +0 -0
  308. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/run_parity.py +0 -0
  309. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/snapshots/athena_latest.json +0 -0
  310. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
  311. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/parity/test_parity_gap.py +0 -0
  312. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_add_section.py +0 -0
  313. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_add_section_extract_items.py +0 -0
  314. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_alignment_values.py +0 -0
  315. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_athena_extension_creates_session_open.py +0 -0
  316. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_athena_extensions_contract.py +0 -0
  317. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_athena_extensions_registry.py +0 -0
  318. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_batch_chunking.py +0 -0
  319. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_batching_perf.py +0 -0
  320. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_battery_cases.py +0 -0
  321. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
  322. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_add_table_not_supported.py +0 -0
  323. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_borders_spec.py +0 -0
  324. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_fastpath_runs.py +0 -0
  325. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
  326. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
  327. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_inner_format_stash.py +0 -0
  328. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_inner_run_format_stash.py +0 -0
  329. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_inner_run_guard.py +0 -0
  330. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_merge.py +0 -0
  331. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_nested_tables.py +0 -0
  332. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_paragraphs_listed.py +0 -0
  333. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_text_plain_fastpath.py +0 -0
  334. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_cell_text_replace_semantics.py +0 -0
  335. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_citations.py +0 -0
  336. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_collapsed_range_format.py +0 -0
  337. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_color_format_theme.py +0 -0
  338. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_command_dataclasses.py +0 -0
  339. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_commands.py +0 -0
  340. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_comment_paragraphs.py +0 -0
  341. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_comments.py +0 -0
  342. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_comments_first_read.py +0 -0
  343. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_content_control_value.py +0 -0
  344. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_core_properties.py +0 -0
  345. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_document_asset_id_property.py +0 -0
  346. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_document_clear.py +0 -0
  347. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_document_factory_validation.py +0 -0
  348. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_document_settings_session_open.py +0 -0
  349. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_docx_exec_lab.py +0 -0
  350. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_docx_exec_lab_server.py +0 -0
  351. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_drawing_image.py +0 -0
  352. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
  353. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_empty_hyperlinks.py +0 -0
  354. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_enum_parity.py +0 -0
  355. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_execution_scope.py +0 -0
  356. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_export_docx_clean.py +0 -0
  357. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_export_rescue_anchors.py +0 -0
  358. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_field_reads_python_docx_parity.py +0 -0
  359. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_field_refresh.py +0 -0
  360. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_field_runs.py +0 -0
  361. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_find_replace_session_open.py +0 -0
  362. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_flushed_write_failures.py +0 -0
  363. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_http_transport.py +0 -0
  364. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_hyperlink_coalescing.py +0 -0
  365. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_image_url_data_uri.py +0 -0
  366. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_inline_page_break.py +0 -0
  367. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_inline_shape_alt_text.py +0 -0
  368. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_inline_shape_size.py +0 -0
  369. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_inline_shape_type.py +0 -0
  370. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_inline_shapes_floating.py +0 -0
  371. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_insert_deferred.py +0 -0
  372. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_iter_inner_content.py +0 -0
  373. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_latent_style_writes.py +0 -0
  374. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_line_clear_break.py +0 -0
  375. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_list_styles.py +0 -0
  376. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_listed_paragraph_reads.py +0 -0
  377. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_literal_no_break_hyphens.py +0 -0
  378. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_literal_soft_hyphens.py +0 -0
  379. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_merged_cell_identity.py +0 -0
  380. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_merged_cell_secondary_slot.py +0 -0
  381. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_merged_cells.py +0 -0
  382. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_missing_modules.py +0 -0
  383. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_nested_table_refused.py +0 -0
  384. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_note_reference_reads_python_docx_parity.py +0 -0
  385. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_oxml_shim.py +0 -0
  386. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_page_break.py +0 -0
  387. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_paragraph_delete.py +0 -0
  388. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_paragraph_indent_reads.py +0 -0
  389. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_paragraph_runs_hyperlinks.py +0 -0
  390. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_paragraph_text_empty_run.py +0 -0
  391. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_paragraph_text_len_cache.py +0 -0
  392. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_parfmt_clears.py +0 -0
  393. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_parity_misc.py +0 -0
  394. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_parity_round2.py +0 -0
  395. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_partial_failure_cascade.py +0 -0
  396. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_pdf_export_and_page_renders.py +0 -0
  397. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_phase_a_behavior.py +0 -0
  398. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_phase_b_headers_footers.py +0 -0
  399. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_phase_c_tables.py +0 -0
  400. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_positional_cell_id.py +0 -0
  401. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
  402. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
  403. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_postproc_ref_restore.py +0 -0
  404. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_pr19766_review_fixes.py +0 -0
  405. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_proxy_equality.py +0 -0
  406. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_ptc.py +0 -0
  407. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_python_docx_api_parity.py +0 -0
  408. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_read_run_ranges.py +0 -0
  409. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_rendered_page_breaks.py +0 -0
  410. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_repeated_node_read.py +0 -0
  411. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_restart_numbering.py +0 -0
  412. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_revisions.py +0 -0
  413. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_round_trip_smoke_script.py +0 -0
  414. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_ruby_reads_python_docx_parity.py +0 -0
  415. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_add_picture_anchor.py +0 -0
  416. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_break_inner_content.py +0 -0
  417. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_edges.py +0 -0
  418. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_formatting_wire.py +0 -0
  419. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_identity.py +0 -0
  420. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_later_runs.py +0 -0
  421. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_mark_inheritance.py +0 -0
  422. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_page_number_blocks.py +0 -0
  423. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_table_style_objects.py +0 -0
  424. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_text_python_docx.py +0 -0
  425. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_text_refreshes_paragraph.py +0 -0
  426. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_run_unmodeled_toggles.py +0 -0
  427. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_sdk_reads_served.py +0 -0
  428. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_section_read_lengths.py +0 -0
  429. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_sections_empty_list.py +0 -0
  430. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
  431. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_add_picture.py +0 -0
  432. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_add_run.py +0 -0
  433. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
  434. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
  435. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_comments_get.py +0 -0
  436. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_document_audit.py +0 -0
  437. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_document_element.py +0 -0
  438. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_enum_section.py +0 -0
  439. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_font_audit.py +0 -0
  440. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_header_footer.py +0 -0
  441. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_hyperlink.py +0 -0
  442. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_inline_shape.py +0 -0
  443. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
  444. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_misc.py +0 -0
  445. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
  446. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_paragraph_style.py +0 -0
  447. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
  448. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_parfmt.py +0 -0
  449. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_row_col_cell.py +0 -0
  450. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_run_add_break.py +0 -0
  451. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
  452. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_run_style.py +0 -0
  453. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_run_style_strict.py +0 -0
  454. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_run_text.py +0 -0
  455. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_run_underline.py +0 -0
  456. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_section_audit.py +0 -0
  457. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_section_dimensions.py +0 -0
  458. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_section_onoff.py +0 -0
  459. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_settings.py +0 -0
  460. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_shared_audit.py +0 -0
  461. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_style.py +0 -0
  462. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_styles.py +0 -0
  463. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_table_audit.py +0 -0
  464. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_table_cell.py +0 -0
  465. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_table_dimensions.py +0 -0
  466. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_silent_stub_table_layout.py +0 -0
  467. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_smoke_integration.py +0 -0
  468. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_acceptance.py +0 -0
  469. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_delete.py +0 -0
  470. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_font.py +0 -0
  471. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_formatting_writes.py +0 -0
  472. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_metadata_writes.py +0 -0
  473. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_names.py +0 -0
  474. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_resolution.py +0 -0
  475. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_setters_contract.py +0 -0
  476. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_tab_stops.py +0 -0
  477. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_style_tab_stops_read.py +0 -0
  478. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_symbol_reads_python_docx_parity.py +0 -0
  479. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_tab_stop_writes.py +0 -0
  480. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_table_add_row_grid_row.py +0 -0
  481. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_table_anchors.py +0 -0
  482. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_table_clears.py +0 -0
  483. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_table_direction.py +0 -0
  484. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_table_length_reads.py +0 -0
  485. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_table_listing_reads.py +0 -0
  486. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_table_read_reuse.py +0 -0
  487. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_table_set_cell_perf.py +0 -0
  488. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_table_style_id_resolution.py +0 -0
  489. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_temporarily_unavailable.py +0 -0
  490. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_text_control_characters.py +0 -0
  491. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_tracked_revisions_offsets.py +0 -0
  492. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_unmapped_alignment_reads.py +0 -0
  493. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_uv_lock_version.py +0 -0
  494. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_validate_find_replace_asset_script.py +0 -0
  495. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_versioned_read_reuse.py +0 -0
  496. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_wire_contract.py +0 -0
  497. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_xml_attr_guard.py +0 -0
  498. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_xml_incompatible_text.py +0 -0
  499. {athena_python_docx-0.42.0 → athena_python_docx-0.43.0}/tests/test_zod_wire_contract.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: athena-python-docx
3
- Version: 0.42.0
3
+ Version: 0.43.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.42.0"
9
+ __version__ = "0.43.0"
10
10
 
11
11
  from docx.api import Document
12
12
  from docx._buffer import discard_all, flush_all
@@ -29,6 +29,7 @@ alive.
29
29
  from __future__ import annotations
30
30
 
31
31
  import copy
32
+ import dataclasses
32
33
  import json
33
34
  import sys
34
35
  import threading
@@ -58,6 +59,7 @@ from docx.errors import DocxError, DocxUnsupportedCommandError, FlushAllError
58
59
  def _apply_proxy_id_rewrites(
59
60
  results: list[Any],
60
61
  proxy_id_refs: "dict[str, list[tuple[object, str]]]",
62
+ resolved: "dict[str, str] | None" = None,
61
63
  ) -> None:
62
64
  """Walk ``results`` and rewrite registered proxy ids from client UUIDs
63
65
  to the real DOCX Studio ids the applier echoed back.
@@ -79,7 +81,7 @@ def _apply_proxy_id_rewrites(
79
81
  ``clientIdMap`` rewrite still resolves them server-side on the
80
82
  *next* call.
81
83
  """
82
- if not proxy_id_refs:
84
+ if not proxy_id_refs and resolved is None:
83
85
  return
84
86
  for result in results:
85
87
  if not isinstance(result, dict):
@@ -92,6 +94,8 @@ def _apply_proxy_id_rewrites(
92
94
  real = result.get(real_key)
93
95
  if not (isinstance(cli, str) and isinstance(real, str)):
94
96
  continue
97
+ if resolved is not None:
98
+ resolved[cli] = real
95
99
  refs = proxy_id_refs.pop(cli, [])
96
100
  for proxy, attr in refs:
97
101
  try:
@@ -104,6 +108,44 @@ def _apply_proxy_id_rewrites(
104
108
  pass
105
109
 
106
110
 
111
+ _MINTED_ID_FIELDS: frozenset[str] = frozenset({"client_node_id", "client_entity_id"})
112
+
113
+
114
+ def _with_resolved_ids(value: Any, resolved: "dict[str, str]") -> Any:
115
+ """``value`` with every string that is a client id in ``resolved`` swapped
116
+ for its real id; the same object when nothing changed."""
117
+ if isinstance(value, str):
118
+ return resolved.get(value, value)
119
+ if isinstance(value, dict):
120
+ out = {k: _with_resolved_ids(v, resolved) for k, v in value.items()}
121
+ return value if all(out[k] is value[k] for k in value) else out
122
+ if isinstance(value, list):
123
+ items = [_with_resolved_ids(v, resolved) for v in value]
124
+ return value if all(a is b for a, b in zip(items, value)) else items
125
+ return value
126
+
127
+
128
+ def _resolve_client_ids(cmd: Command, resolved: "dict[str, str]") -> None:
129
+ """Point ``cmd`` at the real ids of the creates an earlier batch landed.
130
+
131
+ A proxy's own id is rewritten when its create's batch returns, but a copy
132
+ of the client id taken before then (a ``Run``'s paragraph id, the id
133
+ ``add_paragraph`` promotes to a list item) still names the client id. Only
134
+ the docx-studio pod that applied the create can resolve that id when the
135
+ replicas share no client-id map, so a later batch that reaches another pod
136
+ fails with ``block "p_…" not found``.
137
+ """
138
+ if not resolved or not dataclasses.is_dataclass(cmd):
139
+ return
140
+ for f in dataclasses.fields(cmd):
141
+ if f.name in _MINTED_ID_FIELDS:
142
+ continue
143
+ value = getattr(cmd, f.name)
144
+ new = _with_resolved_ids(value, resolved)
145
+ if new is not value:
146
+ setattr(cmd, f.name, new)
147
+
148
+
107
149
  _WRITES_UNAPPLIED = "_docx_writes_unapplied"
108
150
 
109
151
 
@@ -551,6 +593,10 @@ class CommandBuffer:
551
593
  # ``add_heading`` / etc. when they queue a Create with a
552
594
  # client-assigned id. Drained on every flush.
553
595
  self._proxy_id_refs: dict[str, list[tuple[object, str]]] = {}
596
+ # client id → real id of every create a batch landed, so a command
597
+ # that still names a client id is sent with the real one
598
+ # (:func:`_resolve_client_ids`).
599
+ self._resolved_client_ids: dict[str, str] = {}
554
600
  # The last command's ``GetNodeById`` key, result and time, while
555
601
  # nothing has been sent or buffered after it (see :meth:`call`), for a
556
602
  # docx-studio that gives no version.
@@ -866,6 +912,7 @@ class CommandBuffer:
866
912
  user = None if self._user is None else dict(self._user)
867
913
  version = self._version_request()
868
914
  sent_at = _now()
915
+ self._resolve_drained(cmds)
869
916
  try:
870
917
  outcome = self._client.execute_batch(
871
918
  self._asset_id,
@@ -944,6 +991,7 @@ class CommandBuffer:
944
991
  proxy's ``_node_id`` in-place.
945
992
  """
946
993
  self.ensure_usable()
994
+ _resolve_client_ids(cmd, self._resolved_client_ids)
947
995
  # A write whose text holds a character XML does not allow raises
948
996
  # python-docx's ValueError here, before anything is sent: every command
949
997
  # comes through, a path-proxy op's and a direct ``send_command``'s.
@@ -1073,6 +1121,7 @@ class CommandBuffer:
1073
1121
  self._proxy_id_refs = {}
1074
1122
  if not pending:
1075
1123
  return []
1124
+ self._resolve_drained(pending)
1076
1125
  try:
1077
1126
  results, _skipped_by_index = self._client.execute_batch(
1078
1127
  self._asset_id,
@@ -1088,7 +1137,9 @@ class CommandBuffer:
1088
1137
  # batch ships REAL ids for the survivors (vs. dead client
1089
1138
  # UUIDs that would cascade into another "Block not found").
1090
1139
  # See ``_apply_proxy_id_rewrites`` for the full rationale.
1091
- _apply_proxy_id_rewrites(_extract_partial_results(exc), proxy_id_refs)
1140
+ _apply_proxy_id_rewrites(
1141
+ _extract_partial_results(exc), proxy_id_refs, self._resolved_client_ids
1142
+ )
1092
1143
  _ptc_emit_end_batch(pending, is_error=True)
1093
1144
  _unapplied_writes(exc)
1094
1145
  raise
@@ -1099,7 +1150,7 @@ class CommandBuffer:
1099
1150
  finally:
1100
1151
  _forget_reads_of_others(self)
1101
1152
  _ptc_emit_end_batch(pending, is_error=False)
1102
- _apply_proxy_id_rewrites(results, proxy_id_refs)
1153
+ _apply_proxy_id_rewrites(results, proxy_id_refs, self._resolved_client_ids)
1103
1154
  return results
1104
1155
 
1105
1156
  def close(self) -> None:
@@ -1126,6 +1177,14 @@ class CommandBuffer:
1126
1177
 
1127
1178
  # ----- internals -----
1128
1179
 
1180
+ def _resolve_drained(self, cmds: "list[Command]") -> None:
1181
+ """Point ``cmds``, about to be sent under ``_post_lock``, at the real
1182
+ ids every earlier batch returned: one queued while the idle timer's
1183
+ batch with its create was still in flight missed that id in
1184
+ :meth:`call`."""
1185
+ for queued in cmds:
1186
+ _resolve_client_ids(queued, self._resolved_client_ids)
1187
+
1129
1188
  def _eager_flush_with(self, cmd: Command) -> Any:
1130
1189
  """Drain pending + run ``cmd`` in one batch; return cmd's result.
1131
1190
 
@@ -1151,6 +1210,7 @@ class CommandBuffer:
1151
1210
  self._proxy_id_refs = {}
1152
1211
  writes = self._writes
1153
1212
  all_cmds: list[Command] = [*pending, cmd]
1213
+ self._resolve_drained(all_cmds)
1154
1214
  trailing_index = len(all_cmds) - 1
1155
1215
  # A batch that answers a read asks for the document's version (a
1156
1216
  # docx-studio that predates it ignores the question).
@@ -1175,7 +1235,9 @@ class CommandBuffer:
1175
1235
  # ``table.cell(0, 0)`` → ``tables.get``), so a failure on
1176
1236
  # the trailing ``cmd`` would otherwise abandon every
1177
1237
  # buffered Create's rewrite at once.
1178
- _apply_proxy_id_rewrites(_extract_partial_results(exc), proxy_id_refs)
1238
+ _apply_proxy_id_rewrites(
1239
+ _extract_partial_results(exc), proxy_id_refs, self._resolved_client_ids
1240
+ )
1179
1241
  _ptc_emit_end_batch([cmd], is_error=True)
1180
1242
  if pending:
1181
1243
  self._settle_writes_of_failed_read(
@@ -1198,7 +1260,7 @@ class CommandBuffer:
1198
1260
  # Proxy-id rewrites still run for the buffered prefix that DID apply —
1199
1261
  # the trailing skip (if any) doesn't strand the supported Creates that
1200
1262
  # flushed alongside it.
1201
- _apply_proxy_id_rewrites(results, proxy_id_refs)
1263
+ _apply_proxy_id_rewrites(results, proxy_id_refs, self._resolved_client_ids)
1202
1264
  # This is the AWAITED-result path: the caller is about to read the
1203
1265
  # trailing command's result. If the server skipped that command
1204
1266
  # (capability gap), there is no result to return — returning some
@@ -1260,11 +1322,13 @@ class CommandBuffer:
1260
1322
  # ``MAX_BATCH_COMMANDS``: those a failed request's earlier ones
1261
1323
  # applied keep their ids, as a flush's do, and only the rest are
1262
1324
  # the failure's unapplied writes.
1263
- _apply_proxy_id_rewrites(_extract_partial_results(again), proxy_id_refs)
1325
+ _apply_proxy_id_rewrites(
1326
+ _extract_partial_results(again), proxy_id_refs, self._resolved_client_ids
1327
+ )
1264
1328
  _ptc_emit_end_batch(pending, is_error=True)
1265
1329
  raise _unapplied_writes(again) from exc
1266
1330
  _ptc_emit_end_batch(pending, is_error=False)
1267
- _apply_proxy_id_rewrites(results, proxy_id_refs)
1331
+ _apply_proxy_id_rewrites(results, proxy_id_refs, self._resolved_client_ids)
1268
1332
  _as_the_read_s_own_failure(exc, failed)
1269
1333
 
1270
1334
  def _reset_timer_locked(self) -> None:
@@ -38,6 +38,20 @@ _API_KEY_ENV = "ATHENA_DOCX_API_KEY" # noqa: S105
38
38
  _AGORA_BASE_URL_ENV = "ATHENA_AGORA_BASE_URL"
39
39
 
40
40
 
41
+ def _resolve_api_key(api_key: str | None) -> str | None:
42
+ """The key a create or upload sends: ``api_key``, else the studio key.
43
+
44
+ ``ATHENA_DOCX_API_KEY`` comes first, as on every other call this SDK
45
+ makes. Both create paths land outside Athena's public API (docx-studio's
46
+ ``/docs/empty``, agora's ``/api/docx-studio/assets``), and inside an Athena
47
+ session VM or computer ``ATHENA_API_KEY`` is a credential only that public
48
+ API (``/api/v0``) accepts. ``ATHENA_API_KEY`` stays the fallback for an
49
+ environment that sets no studio key (local use with one personal key).
50
+ The new document is owned by the user the key belongs to.
51
+ """
52
+ return api_key or os.environ.get(_API_KEY_ENV) or os.environ.get("ATHENA_API_KEY")
53
+
54
+
41
55
  def _derive_agora_base_url(docx_base_url: str) -> str | None:
42
56
  """Best-effort derive an Agora base URL from a docx-studio base URL.
43
57
 
@@ -99,11 +113,11 @@ def create_empty_document(
99
113
  Falls back to ``$ATHENA_DOCX_BASE_URL``.
100
114
  api_key: Athena API key (PropelAuth user API key) or PropelAuth
101
115
  access token. Sent as ``Authorization: Bearer <api_key>``.
102
- Falls back to ``$ATHENA_DOCX_API_KEY``.
116
+ Falls back to ``$ATHENA_DOCX_API_KEY``, then ``$ATHENA_API_KEY``.
103
117
  name: Optional display title.
104
118
  parent_folder_id: Optional parent folder; defaults to workspace root.
105
- workspace_id: Optional workspace UUID; defaults to caller's
106
- current workspace.
119
+ workspace_id: Optional workspace UUID; defaults to the sandbox run's
120
+ ATHENA_WORKSPACE_ID, then the caller's current workspace.
107
121
  timeout: Request timeout in seconds.
108
122
 
109
123
  Returns:
@@ -115,17 +129,7 @@ def create_empty_document(
115
129
  DocxError: any other 4xx/5xx from the server.
116
130
  """
117
131
  resolved_base: str | None = base_url or os.environ.get(_BASE_URL_ENV)
118
- # Create must authenticate as the REAL user so the new asset's ``created_by``
119
- # is the user, not a service identity. ``ATHENA_API_KEY`` is the canonical
120
- # per-user Athena key (a local API key in BYO/SSO mode that agora resolves to
121
- # the real user); the docx-studio studio key (``ATHENA_DOCX_API_KEY``) is a
122
- # deployment service credential used only for studio access-checks on
123
- # EXISTING assets, and agora's create gate rejects it (no backing user). So
124
- # prefer the user key here; only fall back to the studio key for callers that
125
- # set just that.
126
- resolved_key: str | None = (
127
- api_key or os.environ.get("ATHENA_API_KEY") or os.environ.get(_API_KEY_ENV)
128
- )
132
+ resolved_key: str | None = _resolve_api_key(api_key)
129
133
 
130
134
  if not resolved_base:
131
135
  raise SessionError(
@@ -141,17 +145,22 @@ def create_empty_document(
141
145
  )
142
146
 
143
147
  url: str = resolved_base.rstrip("/") + "/docs/empty"
144
- body: dict[str, str] = {}
148
+ # The document is born as its creator's: its first write is signed as the
149
+ # caller, with the agent run's attributions when the script runs in one
150
+ # (``X-Custom-Attributions``), so the run's turn replay starts with it.
151
+ body: dict[str, str] = {"seedAuthor": "requester"}
145
152
  if name is not None:
146
153
  body["name"] = name
147
154
  if parent_folder_id is not None:
148
155
  body["parentFolderId"] = parent_folder_id
149
- if workspace_id is not None:
150
- body["workspaceId"] = workspace_id
156
+ resolved_workspace = workspace_id or os.environ.get("ATHENA_WORKSPACE_ID")
157
+ if resolved_workspace:
158
+ body["workspaceId"] = resolved_workspace
151
159
 
152
160
  # Lazy import — _http is loaded lazily by Document.create(), so the
153
161
  # package is fully initialized by the time we reach this code.
154
162
  from docx import __version__
163
+ from docx._http_doc import _delegation_headers
155
164
 
156
165
  payload: bytes = json.dumps(body).encode("utf-8")
157
166
  req = urllib.request.Request( # noqa: S310
@@ -163,6 +172,7 @@ def create_empty_document(
163
172
  "Authorization": f"Bearer {resolved_key}",
164
173
  "Accept": "application/json",
165
174
  "User-Agent": f"athena-python-docx/{__version__}",
175
+ **_delegation_headers(),
166
176
  },
167
177
  )
168
178
 
@@ -227,6 +237,7 @@ def upload_document(
227
237
  name: str | None = None,
228
238
  parent_folder_id: str | None = None,
229
239
  workspace_id: str | None = None,
240
+ source_asset_id: str | None = None,
230
241
  timeout: float = 120.0,
231
242
  ) -> UploadAssetResult:
232
243
  """Upload a local ``.docx`` to agora and create a new Word document asset.
@@ -242,11 +253,12 @@ def upload_document(
242
253
  agora_base_url: Explicit Agora base URL. Falls back to
243
254
  ``$ATHENA_AGORA_BASE_URL`` and finally to a host substitution
244
255
  of ``docx-studio`` → ``agora`` against ``docx_base_url``.
245
- api_key: Athena API key. Falls back to ``$ATHENA_DOCX_API_KEY``.
256
+ api_key: Athena API key. Falls back to ``$ATHENA_DOCX_API_KEY``, then
257
+ ``$ATHENA_API_KEY``.
246
258
  name: Optional asset title (defaults to the file's stem).
247
259
  parent_folder_id: Optional parent folder for the new asset.
248
- workspace_id: Optional workspace UUID; defaults to the caller's
249
- current workspace as resolved by Agora.
260
+ workspace_id: Optional workspace UUID; defaults to the sandbox run's
261
+ ATHENA_WORKSPACE_ID, then the caller's current workspace in Agora.
250
262
  timeout: Request timeout in seconds (uploads can be slow for big
251
263
  decks/templates).
252
264
 
@@ -262,17 +274,7 @@ def upload_document(
262
274
  import os
263
275
 
264
276
  resolved_docx_base: str | None = docx_base_url or os.environ.get(_BASE_URL_ENV)
265
- # Create must authenticate as the REAL user so the new asset's ``created_by``
266
- # is the user, not a service identity. ``ATHENA_API_KEY`` is the canonical
267
- # per-user Athena key (a local API key in BYO/SSO mode that agora resolves to
268
- # the real user); the docx-studio studio key (``ATHENA_DOCX_API_KEY``) is a
269
- # deployment service credential used only for studio access-checks on
270
- # EXISTING assets, and agora's create gate rejects it (no backing user). So
271
- # prefer the user key here; only fall back to the studio key for callers that
272
- # set just that.
273
- resolved_key: str | None = (
274
- api_key or os.environ.get("ATHENA_API_KEY") or os.environ.get(_API_KEY_ENV)
275
- )
277
+ resolved_key: str | None = _resolve_api_key(api_key)
276
278
  resolved_agora_base: str | None = (
277
279
  agora_base_url
278
280
  or os.environ.get(_AGORA_BASE_URL_ENV)
@@ -308,15 +310,28 @@ def upload_document(
308
310
  with open(docx_path, "rb") as f:
309
311
  file_bytes: bytes = f.read()
310
312
 
311
- create_body: dict[str, str] = {"name": title}
313
+ # The document is born holding the uploaded file: the create seeds no
314
+ # template (``seed_template``), and the import below is its first write,
315
+ # signed as the caller with the agent run's attributions when the script
316
+ # runs in one (``seed_author``, ``X-Custom-Attributions``). An agora that
317
+ # predates both seeds the template, which the overwrite import replaces.
318
+ create_body: dict[str, str | bool] = {
319
+ "name": title,
320
+ "seed_author": "requester",
321
+ "seed_template": False,
322
+ }
323
+ if source_asset_id is not None:
324
+ create_body["source_asset_id"] = source_asset_id
312
325
  if parent_folder_id is not None:
313
326
  create_body["parent_folder_id"] = parent_folder_id
314
- if workspace_id is not None:
315
- create_body["workspace_id"] = workspace_id
327
+ resolved_workspace = workspace_id or os.environ.get("ATHENA_WORKSPACE_ID")
328
+ if resolved_workspace:
329
+ create_body["workspace_id"] = resolved_workspace
316
330
 
317
331
  create_url: str = resolved_agora_base.rstrip("/") + "/api/docx-studio/assets"
318
332
 
319
333
  from docx import __version__
334
+ from docx._http_doc import _delegation_headers
320
335
 
321
336
  req = urllib.request.Request( # noqa: S310
322
337
  create_url,
@@ -327,6 +342,7 @@ def upload_document(
327
342
  "Authorization": f"Bearer {resolved_key}",
328
343
  "Accept": "application/json",
329
344
  "User-Agent": f"athena-python-docx/{__version__}",
345
+ **_delegation_headers(),
330
346
  },
331
347
  )
332
348
 
@@ -374,6 +390,7 @@ def upload_document(
374
390
  import_body = {
375
391
  "docx_base64": base64.b64encode(file_bytes).decode("ascii"),
376
392
  "overwrite": True,
393
+ "seed_author": "requester",
377
394
  }
378
395
  import_req = urllib.request.Request( # noqa: S310
379
396
  import_url,
@@ -384,6 +401,7 @@ def upload_document(
384
401
  "Authorization": f"Bearer {resolved_key}",
385
402
  "Accept": "application/json",
386
403
  "User-Agent": f"athena-python-docx/{__version__}",
404
+ **_delegation_headers(),
387
405
  },
388
406
  )
389
407
 
@@ -33,6 +33,7 @@ from __future__ import annotations
33
33
  import dataclasses
34
34
  import json
35
35
  import os
36
+ import re
36
37
  import urllib.parse
37
38
  import warnings
38
39
  from typing import Any, TypedDict
@@ -477,6 +478,9 @@ _PARAGRAPH_TARGETING_COMMANDS: frozenset[str] = frozenset(
477
478
  )
478
479
 
479
480
 
481
+ # The id ``Document._mint_client_node_id`` gives a node it creates.
482
+ _SDK_MINTED_ID = re.compile(r"[a-z]+_[0-9a-f]{12}")
483
+
480
484
  _CELL_PARAGRAPH_HINT: str = (
481
485
  "\n\nHint: DOCX Studio 1.8.1 cannot format paragraphs nested inside "
482
486
  "table cells via SetParagraphAlignment / SetParagraphStyle / "
@@ -534,6 +538,15 @@ _TABLE_QUERY_COMMANDS: frozenset[str] = frozenset(
534
538
  )
535
539
 
536
540
 
541
+ _SDK_CLIENT_ID_HINT: str = (
542
+ "\n\nHint: this id is one the SDK minted for a block it created "
543
+ "(``p_xxxxxxxxxxxx``), not one docx-studio assigned. The create that "
544
+ "minted it failed or was discarded in an earlier batch, or it belongs "
545
+ "to a previous execution. This is not a table-cell problem: re-read "
546
+ "the block (``doc.paragraphs`` / ``doc.tables``) and use the proxy it "
547
+ "returns, or create the block again."
548
+ )
549
+
537
550
  _TABLE_CLIENT_ID_HINT: str = (
538
551
  "\n\nHint: this looks like a stale client-side table UUID "
539
552
  "(``t_xxxxxxxxxxxx``). Either an earlier batch in this execution "
@@ -882,8 +895,12 @@ def _http_post_json(
882
895
  isinstance(cmd_type, str) and cmd_type in _PARAGRAPH_TARGETING_COMMANDS
883
896
  )
884
897
  table_query = isinstance(cmd_type, str) and cmd_type in _TABLE_QUERY_COMMANDS
885
- if _missing_block_id(err_obj.get("message")) in header_footer_block_ids:
898
+ missing = _missing_block_id(err_obj.get("message"))
899
+ if missing in header_footer_block_ids:
886
900
  hint = _HEADER_FOOTER_PARAGRAPH_HINT
901
+ elif missing is not None and _SDK_MINTED_ID.fullmatch(missing):
902
+ # A block this SDK created, not a cell's own paragraph.
903
+ hint = _SDK_CLIENT_ID_HINT
887
904
  elif paragraph_targeting:
888
905
  hint = _CELL_PARAGRAPH_HINT
889
906
  elif table_query:
@@ -259,6 +259,7 @@ class Document:
259
259
  track_revisions: bool = False,
260
260
  docx: str | None = None,
261
261
  agora_base_url: str | None = None,
262
+ source_asset_id: str | None = None,
262
263
  ) -> "Document":
263
264
  """Create a new Word document asset and return an opened Document.
264
265
 
@@ -279,9 +280,14 @@ class Document:
279
280
  base_url: docx-studio API base URL. Falls back to
280
281
  ``$ATHENA_DOCX_BASE_URL``.
281
282
  api_key: Athena API key (PropelAuth user API key) or
282
- access token. Falls back to ``$ATHENA_DOCX_API_KEY``.
283
+ access token. Falls back to ``$ATHENA_DOCX_API_KEY``, then
284
+ ``$ATHENA_API_KEY``, as every other call does; the upload path
285
+ (``docx=``) sends the same key to agora.
283
286
  parent_folder_id: Optional parent folder; defaults to
284
- workspace root.
287
+ the source document's folder when source_asset_id is set,
288
+ otherwise workspace root.
289
+ source_asset_id: Original asset when creating a copy from an export.
290
+ Agora checks source access and destination edit permission.
285
291
  workspace_id: Optional workspace UUID; defaults to the
286
292
  caller's current workspace as resolved by Agora.
287
293
  docx: Optional path to a local ``.docx`` file. When provided,
@@ -325,6 +331,7 @@ class Document:
325
331
  name=name,
326
332
  parent_folder_id=parent_folder_id,
327
333
  workspace_id=workspace_id,
334
+ source_asset_id=source_asset_id,
328
335
  )
329
336
  asset_id = upload_result["asset_id"]
330
337
  else:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "athena-python-docx"
7
- version = "0.42.0"
7
+ version = "0.43.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"
@@ -752,6 +752,44 @@ FIXTURE_SCRIPTS: list[FixtureScript] = [
752
752
  'row.cells[0].text = "Delta"\n'
753
753
  ),
754
754
  ),
755
+ FixtureScript(
756
+ # Writes to a table merged the legacy way (w:hMerge), which python-docx
757
+ # reads as written, each w:tc its own cell: text added after the
758
+ # nested table of a merge's first cell, a merge's first cell and a
759
+ # cell that continues it rewritten, bold on a row's runs, and a row
760
+ # added. The export writes each cell's w:hMerge back, as python-docx
761
+ # keeps it.
762
+ name="fixture-tables-horizontal-merges-writes",
763
+ fixture="tables-horizontal-merges",
764
+ script=(
765
+ "table = doc.tables[0]\n"
766
+ 'table.cell(0, 0).paragraphs[-1].add_run("Issued")\n'
767
+ 'table.cell(2, 0).text = "Merged text rewritten"\n'
768
+ 'table.cell(2, 1).text = "Continued"\n'
769
+ "for run in table.cell(1, 3).paragraphs[0].runs:\n"
770
+ " run.bold = True\n"
771
+ "row = table.add_row()\n"
772
+ 'row.cells[0].text = "Beta"\n'
773
+ ),
774
+ ),
775
+ FixtureScript(
776
+ # Writes to tables whose rows of one cell set no span in a grid of two
777
+ # columns: python-docx reads such a row's one cell as written, and
778
+ # writes it so; a row added after one, and a column added, as
779
+ # python-docx adds them (a w:tc in every row).
780
+ name="fixture-tables-implied-span-writes",
781
+ fixture="tables-implied-span",
782
+ script=(
783
+ "form = doc.tables[0]\n"
784
+ 'form.rows[2].cells[0].text = "Reviewed"\n'
785
+ 'form.rows[4].cells[0].paragraphs[0].add_run(" (internal)")\n'
786
+ 'form.rows[5].cells[1].text = "Closed"\n'
787
+ "closing = doc.tables[1]\n"
788
+ "row = closing.add_row()\n"
789
+ 'row.cells[0].text = "Total"\n'
790
+ "closing.add_column(Pt(72))\n"
791
+ ),
792
+ ),
755
793
  FixtureScript(
756
794
  # Writes to a footer part written with a header's root element and to
757
795
  # a header part written with a footer's: python-docx edits the part's
@@ -123,6 +123,30 @@ def test_insert_block_not_found_carries_cell_hint() -> None:
123
123
  )
124
124
 
125
125
 
126
+ def test_sdk_minted_id_block_not_found_names_the_stale_client_id() -> None:
127
+ """A miss on an id the SDK minted (``p_<12 hex>``) is a create that never
128
+ landed, not a cell paragraph: the hint says so instead of the cell one."""
129
+
130
+ def _capture(url: str, **kwargs: Any) -> _FakeResponse: # noqa: ARG001
131
+ return _FakeResponse(
132
+ status_code=207,
133
+ body=_partial_failure_body(
134
+ cmd_type="Insert",
135
+ message='Block "p_7b3a2a6afbf3" not found.',
136
+ ),
137
+ )
138
+
139
+ client = HttpClient(base_url="https://example.com", api_key="bg_key")
140
+
141
+ with patch.object(client._session, "post", side_effect=_capture):
142
+ with pytest.raises(BlockNotFoundError) as exc_info:
143
+ _execute_setalignment(client)
144
+
145
+ msg = str(exc_info.value)
146
+ assert "the SDK minted" in msg
147
+ assert "cell.add_paragraph" not in msg
148
+
149
+
126
150
  def test_non_paragraph_command_block_not_found_skips_hint() -> None:
127
151
  """A ``Block ... not found`` miss on a non-paragraph-targeting
128
152
  command (a stale image id from ``SetImageSize``, a deleted list
@@ -670,3 +670,89 @@ def test_discard_all_skips_stale_buffers() -> None:
670
670
  buf.close()
671
671
  finally:
672
672
  begin_execution(None)
673
+
674
+
675
+ class _EchoingClient(_FakeClient):
676
+ """Answers each create with its real id, as docx-studio echoes it."""
677
+
678
+ def execute_batch(
679
+ self, asset_id: str, commands: list[Command], **kw: Any
680
+ ) -> tuple[list[Any], dict[int, dict[str, Any]]]:
681
+ super().execute_batch(asset_id, commands, **kw)
682
+ results: list[Any] = []
683
+ for cmd in commands:
684
+ cli = getattr(cmd, "client_node_id", None)
685
+ results.append({"client_node_id": cli, "real_node_id": f"REAL-{cli}"} if cli else {})
686
+ return results, {}
687
+
688
+
689
+ def test_a_client_id_copied_before_its_create_landed_is_sent_as_the_real_id() -> None:
690
+ """``add_paragraph(style="List Bullet")`` and ``p.add_run().add_break()``
691
+ keep the paragraph's client id; when a read flushed the create first, the
692
+ next batch may reach a docx-studio pod that never minted it."""
693
+ fake = _EchoingClient()
694
+ buf = CommandBuffer(fake, "asset_1", auto_flush_seconds=0.0)
695
+ buf.call(CreateParagraph(text="one", at={"kind": "documentEnd"}, client_node_id="p_abc"))
696
+ buf.flush()
697
+ buf.call(
698
+ Replace(
699
+ target={
700
+ "kind": "selection",
701
+ "start": {"kind": "text", "blockId": "p_abc", "offset": 0},
702
+ },
703
+ text="y",
704
+ )
705
+ )
706
+ buf.call(
707
+ CreateParagraph(
708
+ text="two", at={"kind": "after", "target": {"nodeId": "p_abc"}}, client_node_id="p_def"
709
+ )
710
+ )
711
+ buf.flush()
712
+ replace, create = fake.batches[1]
713
+ assert replace.target == {
714
+ "kind": "selection",
715
+ "start": {"kind": "text", "blockId": "REAL-p_abc", "offset": 0},
716
+ }
717
+ assert create.at == {"kind": "after", "target": {"nodeId": "REAL-p_abc"}}
718
+ assert create.client_node_id == "p_def"
719
+ buf.close()
720
+
721
+
722
+ class _HeldEchoingClient(_EchoingClient):
723
+ """An echoing client whose first batch waits for ``release``."""
724
+
725
+ def __init__(self) -> None:
726
+ super().__init__()
727
+ self.sent = threading.Event()
728
+ self.release = threading.Event()
729
+
730
+ def execute_batch(
731
+ self, asset_id: str, commands: list[Command], **kw: Any
732
+ ) -> tuple[list[Any], dict[int, dict[str, Any]]]:
733
+ if not self.batches:
734
+ self.sent.set()
735
+ assert self.release.wait(5)
736
+ return super().execute_batch(asset_id, commands, **kw)
737
+
738
+
739
+ def test_a_command_queued_while_its_create_is_in_flight_is_sent_as_the_real_id() -> None:
740
+ """The idle timer's batch carries the create; an edit queued before that
741
+ batch answers still goes out naming the paragraph by its real id."""
742
+ fake = _HeldEchoingClient()
743
+ buf = CommandBuffer(fake, "asset_1", auto_flush_seconds=0.0)
744
+ buf.call(CreateParagraph(text="one", at={"kind": "documentEnd"}, client_node_id="p_abc"))
745
+ in_flight = threading.Thread(target=buf.flush)
746
+ in_flight.start()
747
+ assert fake.sent.wait(5)
748
+ buf.call(
749
+ CreateParagraph(
750
+ text="two", at={"kind": "after", "target": {"nodeId": "p_abc"}}, client_node_id="p_def"
751
+ )
752
+ )
753
+ fake.release.set()
754
+ in_flight.join(5)
755
+ buf.flush()
756
+ (create,) = fake.batches[1]
757
+ assert create.at == {"kind": "after", "target": {"nodeId": "REAL-p_abc"}}
758
+ buf.close()