athena-python-docx 0.18.3__tar.gz → 0.19.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 (363) hide show
  1. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/CLAUDE.md +16 -0
  2. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/PKG-INFO +2 -1
  3. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/__init__.py +1 -1
  4. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_buffer.py +40 -6
  5. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_http_doc.py +88 -17
  6. athena_python_docx-0.19.0/docx/_references.py +82 -0
  7. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/commands.py +48 -0
  8. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/document.py +17 -0
  9. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/errors.py +34 -0
  10. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/text/paragraph.py +92 -0
  11. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/pyproject.toml +7 -1
  12. athena_python_docx-0.19.0/scripts/dump_wire_fixtures.py +59 -0
  13. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/scripts/release.sh +9 -12
  14. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_athena_extensions_registry.py +3 -0
  15. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_batching_perf.py +4 -8
  16. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_buffer.py +119 -4
  17. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_http_transport.py +159 -6
  18. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_ptc.py +2 -2
  19. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_revisions.py +18 -47
  20. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/uv.lock +11 -0
  21. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/.gitignore +0 -0
  22. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/DOCX_EXEC_LAB.md +0 -0
  23. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/README.md +0 -0
  24. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_athena_extension.py +0 -0
  25. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_batching.py +0 -0
  26. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_execution.py +0 -0
  27. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_http.py +0 -0
  28. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_image_utils.py +0 -0
  29. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_postproc.py +0 -0
  30. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_ptc.py +0 -0
  31. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_table_styles.py +0 -0
  32. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/_timeouts.py +0 -0
  33. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/api.py +0 -0
  34. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/bookmarks.py +0 -0
  35. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/charts.py +0 -0
  36. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/client.py +0 -0
  37. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/comments.py +0 -0
  38. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/enum/__init__.py +0 -0
  39. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/enum/section.py +0 -0
  40. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/enum/style.py +0 -0
  41. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/enum/table.py +0 -0
  42. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/enum/text.py +0 -0
  43. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/exceptions.py +0 -0
  44. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/fields.py +0 -0
  45. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/footnotes.py +0 -0
  46. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/math.py +0 -0
  47. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/opc/__init__.py +0 -0
  48. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/opc/coreprops.py +0 -0
  49. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/oxml/__init__.py +0 -0
  50. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/revisions.py +0 -0
  51. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/sdt.py +0 -0
  52. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/section.py +0 -0
  53. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/session.py +0 -0
  54. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/settings.py +0 -0
  55. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/shape.py +0 -0
  56. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/shared.py +0 -0
  57. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/styles/__init__.py +0 -0
  58. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/styles/style.py +0 -0
  59. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/styles/styles.py +0 -0
  60. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/table.py +0 -0
  61. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/text/__init__.py +0 -0
  62. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/text/font.py +0 -0
  63. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/text/hyperlink.py +0 -0
  64. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/text/pagebreak.py +0 -0
  65. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/text/parfmt.py +0 -0
  66. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/text/run.py +0 -0
  67. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/text/tabstops.py +0 -0
  68. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/toc.py +0 -0
  69. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/docx/typing.py +0 -0
  70. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/scripts/docx_exec_lab.py +0 -0
  71. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
  72. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
  73. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/scripts/docx_exec_lab_server.py +0 -0
  74. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/scripts/publish.sh +0 -0
  75. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/scripts/round_trip_smoke.py +0 -0
  76. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/scripts/smoke_test_block_not_found.py +0 -0
  77. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/scripts/validate_find_replace_asset.py +0 -0
  78. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/__init__.py +0 -0
  79. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/conftest.py +0 -0
  80. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/METHODOLOGY.md +0 -0
  81. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/README.md +0 -0
  82. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/__init__.py +0 -0
  83. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/ab_probe_cases.py +0 -0
  84. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/ab_probe_runner.py +0 -0
  85. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/auto_gen_cases.py +0 -0
  86. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/binary_round_trip.py +0 -0
  87. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/cases.py +0 -0
  88. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/complex_cases.py +0 -0
  89. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/coverage_report.py +0 -0
  90. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/extract.py +0 -0
  91. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/extreme_cases.py +0 -0
  92. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/fake_session.py +0 -0
  93. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/firm_templates/README.md +0 -0
  94. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/firm_templates/__init__.py +0 -0
  95. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/firm_templates/_runner.py +0 -0
  96. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/firm_templates/extractor.py +0 -0
  97. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
  98. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
  99. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/local_runner.py +0 -0
  100. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/mega_cases.py +0 -0
  101. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshot.py +0 -0
  102. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
  103. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
  104. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
  105. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
  106. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
  107. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
  108. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
  109. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
  110. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
  111. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
  112. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
  113. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
  114. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
  115. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
  116. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
  117. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
  118. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
  119. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
  120. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
  121. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
  122. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
  123. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
  124. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
  125. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
  126. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
  127. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
  128. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
  129. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
  130. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
  131. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
  132. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
  133. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
  134. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
  135. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
  136. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
  137. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
  138. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
  139. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
  140. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
  141. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
  142. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
  143. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
  144. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
  145. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
  146. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
  147. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
  148. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
  149. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
  150. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
  151. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
  152. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
  153. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
  154. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
  155. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
  156. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
  157. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
  158. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
  159. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
  160. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
  161. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
  162. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
  163. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
  164. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
  165. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
  166. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
  167. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
  168. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
  169. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
  170. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
  171. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
  172. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
  173. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
  174. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
  175. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
  176. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
  177. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
  178. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
  179. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
  180. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
  181. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
  182. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
  183. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
  184. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
  185. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
  186. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
  187. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
  188. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
  189. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
  190. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
  191. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
  192. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
  193. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
  194. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
  195. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
  196. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
  197. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
  198. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
  199. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
  200. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
  201. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
  202. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
  203. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
  204. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
  205. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
  206. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
  207. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
  208. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
  209. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
  210. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
  211. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
  212. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
  213. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
  214. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
  215. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
  216. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
  217. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
  218. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
  219. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
  220. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
  221. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
  222. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
  223. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
  224. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
  225. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
  226. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
  227. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
  228. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
  229. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
  230. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
  231. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
  232. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
  233. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
  234. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
  235. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
  236. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
  237. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
  238. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
  239. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
  240. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
  241. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
  242. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
  243. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
  244. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
  245. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
  246. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
  247. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
  248. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
  249. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/ours_spec.json +0 -0
  250. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/parity_crawl.py +0 -0
  251. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/parity_diff.json +0 -0
  252. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/real_world_cases.py +0 -0
  253. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/round_trip_tests.py +0 -0
  254. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/runner.py +0 -0
  255. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/stock_spec.json +0 -0
  256. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
  257. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/README.md +0 -0
  258. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/__init__.py +0 -0
  259. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/baseline_gaps.json +0 -0
  260. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/compare.py +0 -0
  261. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/intentional_deviations.json +0 -0
  262. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/introspect.py +0 -0
  263. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
  264. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/reports/gap_report.json +0 -0
  265. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/run_parity.py +0 -0
  266. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/snapshots/athena_latest.json +0 -0
  267. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
  268. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/parity/test_parity_gap.py +0 -0
  269. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_add_section_extract_items.py +0 -0
  270. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_athena_extensions_contract.py +0 -0
  271. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_block_not_found_error.py +0 -0
  272. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
  273. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_cell_add_table_not_supported.py +0 -0
  274. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
  275. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
  276. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_cell_inner_format_stash.py +0 -0
  277. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_cell_inner_run_format_stash.py +0 -0
  278. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_cell_inner_run_guard.py +0 -0
  279. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_cell_text_plain_fastpath.py +0 -0
  280. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_cell_text_replace_semantics.py +0 -0
  281. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_collapsed_range_format.py +0 -0
  282. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_command_dataclasses.py +0 -0
  283. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_commands.py +0 -0
  284. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_comments.py +0 -0
  285. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_document_asset_id_property.py +0 -0
  286. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_document_clear.py +0 -0
  287. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_document_create.py +0 -0
  288. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_document_create_from_template.py +0 -0
  289. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_document_factory_validation.py +0 -0
  290. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_docx_exec_lab.py +0 -0
  291. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_docx_exec_lab_server.py +0 -0
  292. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
  293. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_execution_scope.py +0 -0
  294. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_find_replace_session_open.py +0 -0
  295. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_hyperlink_coalescing.py +0 -0
  296. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_image_url_data_uri.py +0 -0
  297. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_insert_deferred.py +0 -0
  298. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_iter_inner_content.py +0 -0
  299. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_list_styles.py +0 -0
  300. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_merged_cell_secondary_slot.py +0 -0
  301. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_merged_cells.py +0 -0
  302. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_oxml_shim.py +0 -0
  303. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_paragraph_text_len_cache.py +0 -0
  304. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_parity_misc.py +0 -0
  305. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_parity_round2.py +0 -0
  306. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_partial_failure_cascade.py +0 -0
  307. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_phase_a_behavior.py +0 -0
  308. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_phase_b_headers_footers.py +0 -0
  309. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_phase_c_tables.py +0 -0
  310. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_positional_cell_id.py +0 -0
  311. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
  312. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
  313. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_postproc_ref_restore.py +0 -0
  314. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_pr19766_review_fixes.py +0 -0
  315. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_python_docx_api_parity.py +0 -0
  316. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
  317. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_add_picture.py +0 -0
  318. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_add_run.py +0 -0
  319. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
  320. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
  321. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_comments_get.py +0 -0
  322. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_document_audit.py +0 -0
  323. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_document_element.py +0 -0
  324. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_enum_section.py +0 -0
  325. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_font_audit.py +0 -0
  326. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_header_footer.py +0 -0
  327. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_hyperlink.py +0 -0
  328. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_inline_shape.py +0 -0
  329. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
  330. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_misc.py +0 -0
  331. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
  332. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_paragraph_style.py +0 -0
  333. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
  334. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_parfmt.py +0 -0
  335. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_row_col_cell.py +0 -0
  336. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_run_add_break.py +0 -0
  337. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
  338. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_run_style.py +0 -0
  339. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_run_style_strict.py +0 -0
  340. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_run_text.py +0 -0
  341. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_run_underline.py +0 -0
  342. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_section_audit.py +0 -0
  343. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_section_dimensions.py +0 -0
  344. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_section_onoff.py +0 -0
  345. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_settings.py +0 -0
  346. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_shared_audit.py +0 -0
  347. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_style.py +0 -0
  348. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_styles.py +0 -0
  349. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_table_audit.py +0 -0
  350. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_table_cell.py +0 -0
  351. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_table_dimensions.py +0 -0
  352. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_silent_stub_table_layout.py +0 -0
  353. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_smoke_integration.py +0 -0
  354. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_style_acceptance.py +0 -0
  355. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_style_font.py +0 -0
  356. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_style_setters_contract.py +0 -0
  357. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_table_set_cell_perf.py +0 -0
  358. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_table_style_id_resolution.py +0 -0
  359. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_temporarily_unavailable.py +0 -0
  360. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_validate_find_replace_asset_script.py +0 -0
  361. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_wire_contract.py +0 -0
  362. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_xml_attr_guard.py +0 -0
  363. {athena_python_docx-0.18.3 → athena_python_docx-0.19.0}/tests/test_zod_wire_contract.py +0 -0
@@ -222,6 +222,22 @@ Issue numbers reference `python-openxml/python-docx`.
222
222
  the same `Hyperlink` class the read path already exposes. Also
223
223
  available as `Run.add_hyperlink` for convenience.
224
224
 
225
+ - **`Paragraph.add_citation(*, source, text=None, anchor=None,
226
+ display_value=None, label=None) -> str`** + **`Document.remove_citation
227
+ (*, citation_id, soft=True)`** — source-citation markers (Athena
228
+ studio-linking; no python-docx analog, `issue=None`). Attaches a
229
+ Word-style superscript citation marker pointing at an Athena asset
230
+ (`source`, an `AssetReference`), optionally narrowed by `anchor` (a
231
+ sheet range, PDF page, …). Renders in Olympus as a clickable superscript
232
+ with a hover preview + Citations-panel entry — the same unified
233
+ citation system as sheets / PPTX / native docs; clicking opens the
234
+ source in a new tab. Routes through `CreateCitation` / `RemoveCitation`;
235
+ the studio server derives the openable `citation_string` from
236
+ `source_ref` + `source_anchor` via `@athenaintel/references` (single
237
+ canonical serializer). `docx/_references.py` mirrors the pptx
238
+ `AssetReference` + anchor re-exports off the shared `athena-references`
239
+ package.
240
+
225
241
  - **`Run.add_field(kind, *, args=None, cached_result=None) -> Field`**
226
242
  + **`Document.fields: Fields`** — generic Word field insertion.
227
243
  Supports `"PAGE"`, `"NUMPAGES"`, `"DATE"`, `"TIME"`, `"AUTHOR"`,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: athena-python-docx
3
- Version: 0.18.3
3
+ Version: 0.19.0
4
4
  Summary: Drop-in replacement for python-docx that connects to Athena's Superdoc/Keryx collaborative document stack
5
5
  Project-URL: Homepage, https://athenaintelligence.ai
6
6
  Author-email: Athena Intelligence <engineering@athenaintelligence.ai>
@@ -11,6 +11,7 @@ Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.11
12
12
  Classifier: Programming Language :: Python :: 3.12
13
13
  Requires-Python: >=3.11
14
+ Requires-Dist: athena-references>=0.1.0
14
15
  Requires-Dist: requests>=2.28
15
16
  Requires-Dist: urllib3>=1.26.6
16
17
  Provides-Extra: dev
@@ -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.18.3"
9
+ __version__ = "0.19.0"
10
10
 
11
11
  from docx.api import Document
12
12
  from docx._buffer import flush_all
@@ -41,7 +41,7 @@ from docx._execution import (
41
41
  )
42
42
  from docx._timeouts import timeout_for_commands
43
43
  from docx.commands import Command, is_response_bearing, must_flush_immediately
44
- from docx.errors import DocxError, FlushAllError
44
+ from docx.errors import DocxError, DocxUnsupportedCommandError, FlushAllError
45
45
 
46
46
 
47
47
  def _apply_proxy_id_rewrites(
@@ -467,8 +467,17 @@ class CommandBuffer:
467
467
  def flush(self) -> list[Any]:
468
468
  """Flush pending commands as one HTTP batch.
469
469
 
470
- Returns the list of per-command result dicts. Empty list if nothing
471
- was pending. Cancels any active idle timer.
470
+ Returns the per-command result list, positionally aligned with the
471
+ flushed commands (skipped commands occupy their slot as ``None``).
472
+ Empty list if nothing was pending. Cancels any active idle timer.
473
+
474
+ This is the FIRE-AND-FORGET path: callers here don't await an
475
+ individual command's result, so an unsupported command that the
476
+ server skipped is simply warned (inside ``execute_batch``) and its
477
+ slot left ``None`` — the supported commands still applied. (A caller
478
+ that DOES need a single command's result goes through
479
+ :meth:`_eager_flush_with`, which raises on a skipped trailing command
480
+ rather than returning the wrong slot.)
472
481
 
473
482
  After the batch returns, walks per-cmd results for
474
483
  ``real_node_id`` / ``real_entity_id`` echoes and updates any
@@ -490,7 +499,7 @@ class CommandBuffer:
490
499
  if not pending:
491
500
  return []
492
501
  try:
493
- results = self._client.execute_batch(
502
+ results, _skipped_by_index = self._client.execute_batch(
494
503
  self._asset_id,
495
504
  pending,
496
505
  change_mode=change_mode,
@@ -554,8 +563,11 @@ class CommandBuffer:
554
563
  proxy_id_refs = self._proxy_id_refs
555
564
  self._proxy_id_refs = {}
556
565
  all_cmds: list[Command] = [*pending, cmd]
566
+ trailing_index = len(all_cmds) - 1
557
567
  try:
558
- results: list[Any] = self._client.execute_batch(
568
+ results: list[Any]
569
+ skipped_by_index: dict[int, dict[str, Any]]
570
+ results, skipped_by_index = self._client.execute_batch(
559
571
  self._asset_id,
560
572
  all_cmds,
561
573
  change_mode=change_mode,
@@ -576,10 +588,32 @@ class CommandBuffer:
576
588
  _ptc_emit_end_batch(all_cmds, is_error=True)
577
589
  raise
578
590
  _ptc_emit_end_batch(all_cmds, is_error=False)
591
+ # Proxy-id rewrites still run for the buffered prefix that DID apply —
592
+ # the trailing skip (if any) doesn't strand the supported Creates that
593
+ # flushed alongside it.
579
594
  _apply_proxy_id_rewrites(results, proxy_id_refs)
595
+ # This is the AWAITED-result path: the caller is about to read the
596
+ # trailing command's result. If the server skipped that command
597
+ # (capability gap), there is no result to return — returning some
598
+ # earlier applied mutation's slot would be silent WRONG DATA. Fail
599
+ # loud instead. (Fire-and-forget mutations never reach here; they go
600
+ # through :meth:`flush`, which keeps the skip+warn behavior.)
601
+ if trailing_index in skipped_by_index:
602
+ skip = skipped_by_index[trailing_index]
603
+ reason = skip.get("reason") if isinstance(skip, dict) else None
604
+ raise DocxUnsupportedCommandError(
605
+ f"docx-studio skipped command {type(cmd).__name__!r} but the "
606
+ f"SDK was awaiting its result" + (f": {reason}" if reason else ".")
607
+ )
580
608
  if not results:
581
609
  return {}
582
- return results[-1]
610
+ # Trailing command applied: its slot holds the result dict (or ``{}``
611
+ # for a non-dict result). A ``None`` here would only arise if the
612
+ # server omitted the trailing command's index from ``applied[]``
613
+ # without listing it in ``skipped[]`` — fall back to ``{}`` to match
614
+ # the legacy "no result" behavior rather than handing back ``None``.
615
+ trailing = results[-1]
616
+ return trailing if trailing is not None else {}
583
617
 
584
618
  def _reset_timer_locked(self) -> None:
585
619
  # Caller must hold self._lock.
@@ -31,6 +31,7 @@ from __future__ import annotations
31
31
 
32
32
  import json
33
33
  import os
34
+ import warnings
34
35
  from typing import Any
35
36
 
36
37
  import requests
@@ -172,6 +173,7 @@ from docx.errors import (
172
173
  BlockNotFoundError,
173
174
  DocxError,
174
175
  DocxStudioTemporarilyUnavailable,
176
+ DocxUnsupportedCommandWarning,
175
177
  NotFoundError,
176
178
  SessionError,
177
179
  )
@@ -662,6 +664,29 @@ def _http_post_json(
662
664
  )
663
665
 
664
666
 
667
+ def _warn_skipped_commands(skipped: list[Any]) -> None:
668
+ """Emit one :class:`DocxUnsupportedCommandWarning` per server-reported
669
+ skipped command.
670
+
671
+ Each entry is the wire-shape ``{index, type, reason}`` dict the applier
672
+ records when a command raises ``UnsupportedDocxEditorCommandError``. The
673
+ batch still succeeded for every supported command; warning (rather than
674
+ raising) is the graceful-degradation contract so a script that ends with
675
+ an unsupported op doesn't abort into an empty document.
676
+
677
+ Tolerant of malformed entries: anything that isn't a dict is summarized
678
+ generically rather than crashing the (otherwise successful) batch.
679
+ """
680
+ for entry in skipped:
681
+ if isinstance(entry, dict):
682
+ cmd_type = entry.get("type", "<unknown>")
683
+ reason = entry.get("reason") or "not supported by the docx-editor engine yet"
684
+ message = f"Skipped unsupported command {cmd_type!r}: {reason}"
685
+ else:
686
+ message = f"Skipped unsupported command: {entry!r}"
687
+ warnings.warn(message, DocxUnsupportedCommandWarning, stacklevel=2)
688
+
689
+
665
690
  # ---------------------------------------------------------------------------
666
691
  # HttpClient — the bare HTTP transport. Stateless wrt batching.
667
692
  # ---------------------------------------------------------------------------
@@ -706,8 +731,22 @@ class HttpClient:
706
731
  *,
707
732
  change_mode: str | None = None,
708
733
  user: dict[str, str] | None = None,
709
- ) -> list[Any]:
710
- """POST a batch of typed commands, return per-command result dicts.
734
+ ) -> tuple[list[Any], dict[int, dict[str, Any]]]:
735
+ """POST a batch of typed commands.
736
+
737
+ Returns ``(results, skipped_by_index)``:
738
+
739
+ - ``results`` is **positionally aligned** with ``commands``: one slot
740
+ per input command, in order. An applied command's slot holds its
741
+ ``result`` dict (or ``{}`` for a non-dict result); a SKIPPED
742
+ command's slot holds ``None``. Positional alignment is what lets a
743
+ caller awaiting a single command's result (``_eager_flush_with``)
744
+ map ``results[-1]`` back to *its* command and detect that the
745
+ command was skipped (``None``) rather than silently returning some
746
+ earlier applied mutation's result.
747
+ - ``skipped_by_index`` maps the input index of each skipped command to
748
+ its ``{index, type, reason}`` entry, so callers awaiting a result a
749
+ skipped command can't provide can raise a precise error.
711
750
 
712
751
  Envelope fields:
713
752
  change_mode: ``"direct"`` | ``"tracked"`` | ``None``. When
@@ -720,14 +759,22 @@ class HttpClient:
720
759
  opens the per-asset session for the first time; ignored
721
760
  on subsequent batches against the same pooled session.
722
761
 
723
- On success, length matches len(commands). On 207 partial-failure,
724
- the server returns 207 with `applied` (the successful prefix) plus
725
- an `error` for the failing command :func:`_http_post_json`
726
- translates that to a :class:`DocxError`. We don't pad partial
727
- results because the doc state is undefined.
762
+ On success, ``len(applied) + len(skipped) == len(commands)``. The
763
+ server's ``skipped[]`` array carries commands the docx-editor engine
764
+ doesn't implement yet; each is surfaced as a
765
+ :class:`DocxUnsupportedCommandWarning` (graceful degradation one
766
+ unsupported FIRE-AND-FORGET mutation no longer aborts the whole batch
767
+ into an empty document). Fire-and-forget mutations don't read their
768
+ result, so a warning + skip is correct for them. A skipped command
769
+ that a caller IS awaiting (a query / response-bearing op routed
770
+ through ``_eager_flush_with``) is detected positionally and raised by
771
+ that caller. On 207 partial-failure, the server returns 207 with
772
+ `applied` (the successful prefix) plus an `error` for the failing
773
+ command — :func:`_http_post_json` translates that to a
774
+ :class:`DocxError`.
728
775
  """
729
776
  if not commands:
730
- return []
777
+ return [], {}
731
778
 
732
779
  url: str = f"{self._base_url}/docs/{asset_id}/commands"
733
780
  body: dict[str, Any] = {
@@ -747,6 +794,12 @@ class HttpClient:
747
794
  timeout=timeout_for_commands(commands=commands),
748
795
  )
749
796
  applied = resp.get("applied")
797
+ # Commands the engine doesn't implement yet are reported in
798
+ # ``skipped[]`` (non-fatal). Warn per skip and count them toward the
799
+ # batch total — they're a success, not a failure.
800
+ skipped_raw = resp.get("skipped")
801
+ skipped = skipped_raw if isinstance(skipped_raw, list) else []
802
+ _warn_skipped_commands(skipped)
750
803
  # An empty applied list paired with an `error` field is the
751
804
  # 'all-commands-failed' shape — surface it rather than returning
752
805
  # an empty result list to a caller that's about to index [-1].
@@ -758,19 +811,37 @@ class HttpClient:
758
811
  f"docx-studio response missing applied[]: {resp!r}",
759
812
  )
760
813
 
761
- if len(applied) != len(commands):
814
+ # Applied + skipped together must account for every command. A short
815
+ # ``applied`` is only legitimate when the remainder were skipped.
816
+ if len(applied) + len(skipped) != len(commands):
762
817
  raise DocxError(
763
- f"docx-studio applied {len(applied)} of {len(commands)} commands",
818
+ f"docx-studio applied {len(applied)} (+{len(skipped)} skipped) "
819
+ f"of {len(commands)} commands",
764
820
  )
765
821
 
766
- results: list[Any] = []
822
+ # Build a positionally-aligned result list (one slot per input
823
+ # command). Applied slots carry the result dict; skipped slots stay
824
+ # ``None``. Each entry's ``index`` field places it at the right slot —
825
+ # we don't assume ``applied[]`` is a contiguous prefix once skips can
826
+ # appear anywhere in the batch.
827
+ results: list[Any] = [None] * len(commands)
767
828
  for entry in applied:
768
- if isinstance(entry, dict):
769
- result = entry.get("result")
770
- results.append(result if isinstance(result, dict) else {})
771
- else:
772
- results.append({})
773
- return results
829
+ if not isinstance(entry, dict):
830
+ continue
831
+ idx = entry.get("index")
832
+ if not isinstance(idx, int) or not (0 <= idx < len(results)):
833
+ continue
834
+ result = entry.get("result")
835
+ results[idx] = result if isinstance(result, dict) else {}
836
+
837
+ skipped_by_index: dict[int, dict[str, Any]] = {}
838
+ for entry in skipped:
839
+ if not isinstance(entry, dict):
840
+ continue
841
+ idx = entry.get("index")
842
+ if isinstance(idx, int) and 0 <= idx < len(commands):
843
+ skipped_by_index[idx] = entry
844
+ return results, skipped_by_index
774
845
 
775
846
 
776
847
  # ---------------------------------------------------------------------------
@@ -0,0 +1,82 @@
1
+ """Thin Python mirror of the @athenaintel/references TypeScript package.
2
+
3
+ Used by the citation SDK method (``Paragraph.add_citation`` — an Athena
4
+ extension beyond the python-docx surface). Mirrors only what the SDK needs to
5
+ construct ``AssetReference`` + ``Anchor`` payloads — full reference
6
+ serialization (URI / URL / Spaces format) is server-side.
7
+
8
+ The anchor + version-policy dataclasses are re-exported from the shared
9
+ ``athena-references`` package (the single Python source of truth, kept in
10
+ lockstep with ``packages/references`` and ``agora/agora/utils/asset_references``).
11
+ ``AssetReference`` is a thin back-compat wrapper that additionally accepts a
12
+ plain ``dict`` ``meta`` and serializes it verbatim.
13
+
14
+ Note (Athena extension):
15
+ This module is NOT part of python-docx — it exists to construct
16
+ Athena-asset references for the SDK's citation method.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ __athena_extension_module__: bool = True
22
+ __athena_extension_description__: str = (
23
+ "Cross-studio asset reference types (Athena studio-linking, no upstream)."
24
+ )
25
+ __athena_extension_since__: str = "0.11.0"
26
+
27
+ from dataclasses import dataclass
28
+ from typing import Any, Optional
29
+
30
+ from athena_references import (
31
+ Anchor,
32
+ PageAnchor,
33
+ PageRangeAnchor,
34
+ PmNodeAnchor,
35
+ PmNodeRangeAnchor,
36
+ ShapeAnchor,
37
+ SheetCellAnchor,
38
+ SheetRangeAnchor,
39
+ SheetTableAnchor,
40
+ SlideAnchor,
41
+ VersionPolicy,
42
+ VersionPolicyLatest,
43
+ VersionPolicyPinned,
44
+ )
45
+
46
+
47
+ @dataclass
48
+ class AssetReference:
49
+ """Athena asset reference. ``id`` is the asset UUID.
50
+
51
+ Re-homes the canonical anchor types onto the shared ``athena-references``
52
+ package while preserving the historical SDK contract that ``meta`` may be a
53
+ plain ``dict`` (serialized verbatim) in addition to ``None``.
54
+ """
55
+
56
+ id: str
57
+ meta: Optional[dict[str, Any]] = None
58
+
59
+ def to_dict(self) -> dict[str, Any]:
60
+ """Serialize to the canonical wire shape."""
61
+ out: dict[str, Any] = {"referenced": "asset", "id": self.id}
62
+ if self.meta is not None:
63
+ out["meta"] = dict(self.meta)
64
+ return out
65
+
66
+
67
+ __all__ = [
68
+ "AssetReference",
69
+ "Anchor",
70
+ "PageAnchor",
71
+ "PageRangeAnchor",
72
+ "PmNodeAnchor",
73
+ "PmNodeRangeAnchor",
74
+ "ShapeAnchor",
75
+ "SheetCellAnchor",
76
+ "SheetRangeAnchor",
77
+ "SheetTableAnchor",
78
+ "SlideAnchor",
79
+ "VersionPolicyLatest",
80
+ "VersionPolicyPinned",
81
+ "VersionPolicy",
82
+ ]
@@ -926,6 +926,47 @@ class RemoveHyperlink(Command):
926
926
  target: dict[str, Any] = dataclasses.field(default_factory=dict)
927
927
 
928
928
 
929
+ # ---------------------------------------------------------------------------
930
+ # Citations (Athena extension — source-citation markers. No python-docx
931
+ # analog; mirrors the PPTX studio's AddSlideCitation / RemoveCitation. The
932
+ # studio server derives the openable ``citation_string`` (Spaces URL) from
933
+ # ``source_ref`` + ``source_anchor`` via @athenaintel/references.)
934
+ # ---------------------------------------------------------------------------
935
+
936
+
937
+ @dataclass
938
+ class CreateCitation(Command):
939
+ """Insert a source-citation marker.
940
+
941
+ Either ``text`` inserts a new cited span at ``at`` (a Word-style
942
+ superscript marker is appended after it), or ``target`` cites an existing
943
+ range. ``source_ref`` / ``source_anchor`` describe the cited source
944
+ (AssetReference / Anchor JSON, transited as dicts like ``SetImageAnchor``).
945
+ ``label`` is the marker glyph (defaults to a running index server-side).
946
+ ``client_entity_id`` is the client-minted citation id the applier honors
947
+ as the citation's stable id (the ``docx-editor-citations`` Y.Map key).
948
+ """
949
+
950
+ source_ref: dict[str, Any] = dataclasses.field(default_factory=dict)
951
+ source_anchor: dict[str, Any] | None = None
952
+ display_value: str | None = None
953
+ label: str | None = None
954
+ text: str | None = None
955
+ target: dict[str, Any] | None = None
956
+ at: dict[str, Any] | None = None
957
+ client_entity_id: str | None = None
958
+
959
+
960
+ @dataclass
961
+ class RemoveCitation(Command):
962
+ """Remove a citation by id — strips the marker mark and, by default,
963
+ deactivates the metadata record (``active: false``) so the removal is
964
+ undoable. ``soft=False`` splices the record out entirely."""
965
+
966
+ citation_id: str = ""
967
+ soft: bool = True
968
+
969
+
929
970
  # ---------------------------------------------------------------------------
930
971
  # Fields (Athena extension — python-docx has no field-add API. PAGE,
931
972
  # NUMPAGES, DATE, TIME, FILENAME, AUTHOR, NUMWORDS, REF, SEQ, TOC. The
@@ -1714,6 +1755,9 @@ _RESPONSE_BEARING_TYPES: frozenset[str] = frozenset(
1714
1755
  # Athena-extension response-bearing creates — each returns an
1715
1756
  # entity id the caller wraps in a proxy.
1716
1757
  "CreateHyperlink",
1758
+ # CreateCitation eager-flushes so the marker's ``at`` offset stays
1759
+ # correct relative to surrounding text inserts (mirrors CreateHyperlink).
1760
+ "CreateCitation",
1717
1761
  "CreateField",
1718
1762
  "CreateBookmark",
1719
1763
  "CreateFootnote",
@@ -1783,6 +1827,8 @@ def _mark_athena_extension_command(cls: type, issue: "str | None", description:
1783
1827
  for _cls, _issue, _desc in [
1784
1828
  (CreateHyperlink, "python-docx#74", "Hyperlink add"),
1785
1829
  (RemoveHyperlink, "python-docx#74", "Hyperlink remove"),
1830
+ (CreateCitation, None, "Source citation marker create"),
1831
+ (RemoveCitation, None, "Source citation remove"),
1786
1832
  (CreateField, "python-docx#498", "Field insert"),
1787
1833
  (FieldsRefresh, "python-docx#498", "Field refresh"),
1788
1834
  (FieldsList, "python-docx#498", "Field enumeration"),
@@ -1914,6 +1960,8 @@ __all__ = [
1914
1960
  # semantics))
1915
1961
  "CreateHyperlink",
1916
1962
  "RemoveHyperlink",
1963
+ "CreateCitation",
1964
+ "RemoveCitation",
1917
1965
  "CreateField",
1918
1966
  "FieldsRefresh",
1919
1967
  "FieldsList",
@@ -936,6 +936,23 @@ class Document:
936
936
  include_hyperlinks=include_hyperlinks,
937
937
  )
938
938
 
939
+ @athena_extension(
940
+ description="Document.remove_citation — remove a source citation by id.",
941
+ )
942
+ def remove_citation(self, *, citation_id: str, soft: bool = True) -> None:
943
+ """Remove a source citation by id (Athena extension).
944
+
945
+ Strips the citation marker and, by default (``soft=True``),
946
+ deactivates the metadata record so the removal is undoable;
947
+ ``soft=False`` splices the record out entirely. No-op if the id is
948
+ unknown. No upstream python-docx analog.
949
+ """
950
+ from docx.commands import RemoveCitation
951
+
952
+ run_sync(
953
+ self._session.send_command(RemoveCitation(citation_id=citation_id, soft=soft))
954
+ )
955
+
939
956
  @athena_extension(issue=179, description="Document.add_chart.")
940
957
  def add_chart(
941
958
  self,
@@ -68,6 +68,40 @@ class DocxError(Exception):
68
68
  return self
69
69
 
70
70
 
71
+ class DocxUnsupportedCommandWarning(UserWarning):
72
+ """Emitted (not raised) when docx-studio reports that one or more commands
73
+ in a batch were *skipped* because the docx-editor engine doesn't implement
74
+ them yet (the server's ``skipped[]`` response array).
75
+
76
+ The batch still succeeds: every supported command applied and persisted.
77
+ Surfacing each skip as a warning — instead of letting one unsupported op
78
+ abort the whole batch into an empty document — is the graceful-degradation
79
+ contract. ``warnings.warn`` lets agent code observe the skip (a
80
+ ``warnings.catch_warnings(record=True)`` block, or the executor's warning
81
+ capture) without the script blowing up. Subclasses :class:`UserWarning` so
82
+ it surfaces by default and matches the SDK's other pending/unsupported
83
+ warning classes.
84
+ """
85
+
86
+
87
+ class DocxUnsupportedCommandError(DocxError):
88
+ """Raised when a caller AWAITS the result of a command the docx-editor
89
+ engine doesn't implement (the server skipped it, reporting it in
90
+ ``skipped[]`` rather than ``applied[]``).
91
+
92
+ The graceful-degradation contract skips unsupported FIRE-AND-FORGET
93
+ mutations and merely warns (:class:`DocxUnsupportedCommandWarning`) — a
94
+ fire-and-forget mutation never reads its result, so dropping it keeps the
95
+ rest of the batch alive instead of nuking the document. But a query or
96
+ response-bearing command (``ExportPDF``, ``SectionsGet``, ``Find``,
97
+ ``CommentsGet``, …) is flushed eagerly precisely because the caller is
98
+ about to read its result. Returning some earlier applied mutation's result
99
+ in its place would be silent WRONG DATA, so the awaited-result path raises
100
+ this instead. Subclasses :class:`DocxError` so existing ``except
101
+ DocxError`` handlers still catch it.
102
+ """
103
+
104
+
71
105
  class SessionError(DocxError):
72
106
  """Raised when the Superdoc SDK session cannot be established or used."""
73
107
 
@@ -9,6 +9,7 @@ from docx._athena_extension import athena_extension
9
9
  from docx._batching import run_sync
10
10
 
11
11
  if TYPE_CHECKING:
12
+ from docx._references import Anchor, AssetReference
12
13
  from docx.bookmarks import Bookmark
13
14
  from docx.client import Session
14
15
  from docx.enum.text import WD_ALIGN_PARAGRAPH
@@ -1448,6 +1449,97 @@ class Paragraph:
1448
1449
  range_=(start, end_offset),
1449
1450
  )
1450
1451
 
1452
+ @athena_extension(
1453
+ description="Paragraph.add_citation — append a source-citation marker.",
1454
+ )
1455
+ def add_citation(
1456
+ self,
1457
+ *,
1458
+ source: "AssetReference",
1459
+ text: str | None = None,
1460
+ anchor: "Anchor | None" = None,
1461
+ display_value: str | None = None,
1462
+ label: str | None = None,
1463
+ ) -> str:
1464
+ """Attach a source citation to this paragraph (Athena extension).
1465
+
1466
+ Appends a Word-style superscript citation marker. When ``text`` is
1467
+ given it is inserted first (the cited span) and the marker follows;
1468
+ otherwise the marker attaches at the paragraph end, citing the
1469
+ preceding text. ``source`` is the cited Athena asset; ``anchor``
1470
+ optionally narrows it to a sub-location (a sheet range, PDF page, …).
1471
+ ``display_value`` is the label shown on the citations-panel card;
1472
+ ``label`` overrides the marker glyph (defaults to a bullet).
1473
+
1474
+ Renders in Olympus as a clickable superscript with a hover preview and
1475
+ a Citations-panel entry; clicking opens the source in a new tab. No
1476
+ upstream python-docx analog.
1477
+
1478
+ Returns the citation id.
1479
+ """
1480
+ import uuid
1481
+
1482
+ from docx._references import AssetReference
1483
+ from docx.commands import CreateCitation
1484
+
1485
+ if not isinstance(source, AssetReference):
1486
+ raise TypeError(
1487
+ f"Paragraph.add_citation source requires an AssetReference; "
1488
+ f"got {type(source).__name__} {source!r}",
1489
+ )
1490
+ if text is not None and not isinstance(text, str):
1491
+ raise TypeError(
1492
+ f"Paragraph.add_citation text requires str or None; got "
1493
+ f"{type(text).__name__} {text!r}",
1494
+ )
1495
+ source_anchor: dict | None = None
1496
+ if anchor is not None:
1497
+ if not hasattr(anchor, "to_dict"):
1498
+ raise TypeError(
1499
+ "Paragraph.add_citation anchor must be an Anchor dataclass "
1500
+ "(SheetRangeAnchor, PageAnchor, …); got "
1501
+ f"{type(anchor).__name__}",
1502
+ )
1503
+ source_anchor = anchor.to_dict()
1504
+
1505
+ # Cell-inner paragraphs hit the same addressing gap as add_hyperlink
1506
+ # (BlockNotFoundError on flush). Citations in table cells are a
1507
+ # follow-up; warn and skip rather than fail the whole batch.
1508
+ if self._in_cell:
1509
+ warnings.warn(
1510
+ "Paragraph.add_citation on a cell-inner paragraph is not yet "
1511
+ "supported (same addressing gap as add_hyperlink); the "
1512
+ "citation was skipped.",
1513
+ CellInnerFormatNotSupportedWarning,
1514
+ stacklevel=2,
1515
+ )
1516
+ return ""
1517
+
1518
+ citation_id = f"citation_{uuid.uuid4().hex[:12]}"
1519
+ start: int = self._resolve_text_len()
1520
+ cursor: dict = {
1521
+ "kind": "text",
1522
+ "blockId": self._node_id,
1523
+ "offset": start,
1524
+ }
1525
+ cmd = CreateCitation(
1526
+ source_ref=source.to_dict(),
1527
+ source_anchor=source_anchor,
1528
+ display_value=display_value,
1529
+ label=label,
1530
+ text=text,
1531
+ at={"kind": "selection", "start": cursor, "end": cursor},
1532
+ client_entity_id=citation_id,
1533
+ )
1534
+ run_sync(self._session.send_command(cmd))
1535
+ # The applier inserts ``text`` (if any) then a marker glyph (``label``
1536
+ # or a 1-char bullet); keep the cached text length in sync so a
1537
+ # follow-up op targets the right offset before the snapshot re-fetch.
1538
+ marker_len = len(label) if label else 1
1539
+ self._text_len = start + (len(text) if text else 0) + marker_len
1540
+ self._invalidate_node_info()
1541
+ return citation_id
1542
+
1451
1543
  @athena_extension(
1452
1544
  issue=425,
1453
1545
  description="Paragraph.add_bookmark — zero-width anchor at paragraph end.",
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "athena-python-docx"
7
- version = "0.18.3"
7
+ version = "0.19.0"
8
8
  description = "Drop-in replacement for python-docx that connects to Athena's Superdoc/Keryx collaborative document stack"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -26,6 +26,12 @@ dependencies = [
26
26
  # (urllib3 1.26.0); requests 2.28 only pins urllib3>=1.21.1, so we
27
27
  # tighten here to avoid a runtime TypeError on import.
28
28
  "urllib3>=1.26.6",
29
+ # Canonical Python source of truth for AssetReference + the Anchor union,
30
+ # re-exported by docx/_references.py for Paragraph.add_citation. Deploy
31
+ # follow-up: the document-exec sandbox vendors this wheel via
32
+ # `pip download --no-deps athena-references` (no PyPI access at build time),
33
+ # mirroring presentation-exec.
34
+ "athena-references>=0.1.0",
29
35
  ]
30
36
 
31
37
  [project.optional-dependencies]