athena-python-docx 0.20.0__tar.gz → 0.21.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 (367) hide show
  1. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/CLAUDE.md +41 -17
  2. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/DOCX_EXEC_LAB.md +4 -4
  3. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/PKG-INFO +9 -9
  4. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/README.md +6 -7
  5. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/__init__.py +1 -1
  6. athena_python_docx-0.21.0/docx/_citations.py +74 -0
  7. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/_http.py +122 -91
  8. athena_python_docx-0.21.0/docx/_references.py +91 -0
  9. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/api.py +6 -6
  10. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/client.py +1 -8
  11. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/commands.py +35 -2
  12. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/document.py +68 -26
  13. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/errors.py +3 -3
  14. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/styles/styles.py +43 -13
  15. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/table.py +50 -82
  16. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/text/paragraph.py +58 -1
  17. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/pyproject.toml +6 -5
  18. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/scripts/docx_exec_lab.py +1 -1
  19. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/scripts/round_trip_smoke.py +6 -7
  20. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/scripts/smoke_test_block_not_found.py +2 -11
  21. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/scripts/validate_find_replace_asset.py +1 -1
  22. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/METHODOLOGY.md +9 -9
  23. athena_python_docx-0.21.0/tests/fidelity/README.md +50 -0
  24. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/fake_session.py +32 -33
  25. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/firm_templates/README.md +1 -1
  26. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/parity/intentional_deviations.json +2 -0
  27. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_athena_extensions_registry.py +5 -0
  28. athena_python_docx-0.21.0/tests/test_citations.py +151 -0
  29. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_document_create.py +5 -11
  30. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_document_create_from_template.py +167 -52
  31. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_document_factory_validation.py +2 -2
  32. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_docx_exec_lab.py +1 -1
  33. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_smoke_integration.py +4 -5
  34. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/uv.lock +12 -1
  35. athena_python_docx-0.20.0/tests/fidelity/README.md +0 -71
  36. athena_python_docx-0.20.0/tests/fidelity/runner.py +0 -486
  37. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/.gitignore +0 -0
  38. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/_athena_extension.py +0 -0
  39. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/_batching.py +0 -0
  40. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/_buffer.py +0 -0
  41. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/_execution.py +0 -0
  42. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/_http_doc.py +0 -0
  43. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/_image_utils.py +0 -0
  44. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/_postproc.py +0 -0
  45. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/_ptc.py +0 -0
  46. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/_table_styles.py +0 -0
  47. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/_timeouts.py +0 -0
  48. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/bookmarks.py +0 -0
  49. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/charts.py +0 -0
  50. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/comments.py +0 -0
  51. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/enum/__init__.py +0 -0
  52. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/enum/section.py +0 -0
  53. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/enum/shape.py +0 -0
  54. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/enum/style.py +0 -0
  55. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/enum/table.py +0 -0
  56. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/enum/text.py +0 -0
  57. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/exceptions.py +0 -0
  58. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/fields.py +0 -0
  59. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/footnotes.py +0 -0
  60. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/math.py +0 -0
  61. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/opc/__init__.py +0 -0
  62. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/opc/coreprops.py +0 -0
  63. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/oxml/__init__.py +0 -0
  64. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/revisions.py +0 -0
  65. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/sdt.py +0 -0
  66. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/section.py +0 -0
  67. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/session.py +0 -0
  68. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/settings.py +0 -0
  69. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/shape.py +0 -0
  70. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/shared.py +0 -0
  71. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/styles/__init__.py +0 -0
  72. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/styles/style.py +0 -0
  73. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/text/__init__.py +0 -0
  74. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/text/font.py +0 -0
  75. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/text/hyperlink.py +0 -0
  76. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/text/pagebreak.py +0 -0
  77. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/text/parfmt.py +0 -0
  78. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/text/run.py +0 -0
  79. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/text/tabstops.py +0 -0
  80. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/toc.py +0 -0
  81. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/docx/typing.py +0 -0
  82. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
  83. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
  84. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/scripts/docx_exec_lab_server.py +0 -0
  85. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/scripts/dump_wire_fixtures.py +0 -0
  86. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/scripts/publish.sh +0 -0
  87. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/scripts/release.sh +0 -0
  88. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/__init__.py +0 -0
  89. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/conftest.py +0 -0
  90. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/__init__.py +0 -0
  91. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/ab_probe_cases.py +0 -0
  92. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/ab_probe_runner.py +0 -0
  93. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/auto_gen_cases.py +0 -0
  94. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/binary_round_trip.py +0 -0
  95. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/cases.py +0 -0
  96. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/complex_cases.py +0 -0
  97. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/coverage_report.py +0 -0
  98. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/extract.py +0 -0
  99. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/extreme_cases.py +0 -0
  100. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/firm_templates/__init__.py +0 -0
  101. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/firm_templates/_runner.py +0 -0
  102. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/firm_templates/extractor.py +0 -0
  103. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
  104. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
  105. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/local_runner.py +0 -0
  106. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/mega_cases.py +0 -0
  107. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshot.py +0 -0
  108. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
  109. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
  110. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
  111. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
  112. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
  113. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
  114. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
  115. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
  116. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
  117. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
  118. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
  119. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
  120. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
  121. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
  122. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
  123. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
  124. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
  125. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
  126. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
  127. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
  128. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
  129. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
  130. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
  131. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
  132. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
  133. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
  134. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
  135. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
  136. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
  137. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
  138. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
  139. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
  140. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
  141. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
  142. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
  143. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
  144. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
  145. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
  146. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
  147. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
  148. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
  149. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
  150. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
  151. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
  152. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
  153. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
  154. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
  155. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
  156. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
  157. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
  158. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
  159. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
  160. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
  161. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
  162. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
  163. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
  164. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
  165. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
  166. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
  167. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
  168. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
  169. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
  170. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
  171. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
  172. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
  173. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
  174. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
  175. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
  176. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
  177. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
  178. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
  179. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
  180. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
  181. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
  182. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
  183. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
  184. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
  185. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
  186. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
  187. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
  188. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
  189. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
  190. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
  191. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
  192. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
  193. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
  194. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
  195. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
  196. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
  197. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
  198. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
  199. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
  200. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
  201. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
  202. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
  203. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
  204. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
  205. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
  206. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
  207. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
  208. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
  209. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
  210. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
  211. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
  212. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
  213. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
  214. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
  215. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
  216. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
  217. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
  218. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
  219. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
  220. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
  221. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
  222. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
  223. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
  224. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
  225. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
  226. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
  227. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
  228. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
  229. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
  230. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
  231. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
  232. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
  233. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
  234. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
  235. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
  236. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
  237. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
  238. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
  239. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
  240. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
  241. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
  242. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
  243. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
  244. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
  245. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
  246. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
  247. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
  248. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
  249. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
  250. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
  251. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
  252. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
  253. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
  254. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
  255. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/ours_spec.json +0 -0
  256. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/parity_crawl.py +0 -0
  257. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/parity_diff.json +0 -0
  258. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/real_world_cases.py +0 -0
  259. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/round_trip_tests.py +0 -0
  260. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/stock_spec.json +0 -0
  261. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
  262. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/parity/README.md +0 -0
  263. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/parity/__init__.py +0 -0
  264. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/parity/baseline_gaps.json +0 -0
  265. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/parity/compare.py +0 -0
  266. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/parity/introspect.py +0 -0
  267. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
  268. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/parity/reports/gap_report.json +0 -0
  269. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/parity/run_parity.py +0 -0
  270. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/parity/snapshots/athena_latest.json +0 -0
  271. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
  272. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/parity/test_parity_gap.py +0 -0
  273. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_add_section_extract_items.py +0 -0
  274. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_athena_extensions_contract.py +0 -0
  275. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_batching_perf.py +0 -0
  276. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_block_not_found_error.py +0 -0
  277. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_buffer.py +0 -0
  278. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
  279. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_cell_add_table_not_supported.py +0 -0
  280. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
  281. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
  282. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_cell_inner_format_stash.py +0 -0
  283. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_cell_inner_run_format_stash.py +0 -0
  284. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_cell_inner_run_guard.py +0 -0
  285. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_cell_text_plain_fastpath.py +0 -0
  286. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_cell_text_replace_semantics.py +0 -0
  287. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_collapsed_range_format.py +0 -0
  288. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_command_dataclasses.py +0 -0
  289. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_commands.py +0 -0
  290. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_comments.py +0 -0
  291. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_document_asset_id_property.py +0 -0
  292. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_document_clear.py +0 -0
  293. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_docx_exec_lab_server.py +0 -0
  294. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
  295. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_execution_scope.py +0 -0
  296. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_find_replace_session_open.py +0 -0
  297. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_http_transport.py +0 -0
  298. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_hyperlink_coalescing.py +0 -0
  299. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_image_url_data_uri.py +0 -0
  300. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_insert_deferred.py +0 -0
  301. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_iter_inner_content.py +0 -0
  302. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_list_styles.py +0 -0
  303. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_merged_cell_secondary_slot.py +0 -0
  304. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_merged_cells.py +0 -0
  305. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_oxml_shim.py +0 -0
  306. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_paragraph_text_len_cache.py +0 -0
  307. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_parity_misc.py +0 -0
  308. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_parity_round2.py +0 -0
  309. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_partial_failure_cascade.py +0 -0
  310. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_phase_a_behavior.py +0 -0
  311. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_phase_b_headers_footers.py +0 -0
  312. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_phase_c_tables.py +0 -0
  313. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_positional_cell_id.py +0 -0
  314. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
  315. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
  316. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_postproc_ref_restore.py +0 -0
  317. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_pr19766_review_fixes.py +0 -0
  318. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_ptc.py +0 -0
  319. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_python_docx_api_parity.py +0 -0
  320. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_revisions.py +0 -0
  321. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
  322. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_add_picture.py +0 -0
  323. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_add_run.py +0 -0
  324. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
  325. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
  326. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_comments_get.py +0 -0
  327. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_document_audit.py +0 -0
  328. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_document_element.py +0 -0
  329. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_enum_section.py +0 -0
  330. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_font_audit.py +0 -0
  331. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_header_footer.py +0 -0
  332. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_hyperlink.py +0 -0
  333. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_inline_shape.py +0 -0
  334. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
  335. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_misc.py +0 -0
  336. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
  337. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_paragraph_style.py +0 -0
  338. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
  339. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_parfmt.py +0 -0
  340. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_row_col_cell.py +0 -0
  341. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_run_add_break.py +0 -0
  342. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
  343. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_run_style.py +0 -0
  344. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_run_style_strict.py +0 -0
  345. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_run_text.py +0 -0
  346. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_run_underline.py +0 -0
  347. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_section_audit.py +0 -0
  348. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_section_dimensions.py +0 -0
  349. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_section_onoff.py +0 -0
  350. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_settings.py +0 -0
  351. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_shared_audit.py +0 -0
  352. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_style.py +0 -0
  353. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_styles.py +0 -0
  354. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_table_audit.py +0 -0
  355. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_table_cell.py +0 -0
  356. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_table_dimensions.py +0 -0
  357. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_silent_stub_table_layout.py +0 -0
  358. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_style_acceptance.py +0 -0
  359. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_style_font.py +0 -0
  360. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_style_setters_contract.py +0 -0
  361. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_table_set_cell_perf.py +0 -0
  362. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_table_style_id_resolution.py +0 -0
  363. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_temporarily_unavailable.py +0 -0
  364. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_validate_find_replace_asset_script.py +0 -0
  365. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_wire_contract.py +0 -0
  366. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_xml_attr_guard.py +0 -0
  367. {athena_python_docx-0.20.0 → athena_python_docx-0.21.0}/tests/test_zod_wire_contract.py +0 -0
@@ -67,11 +67,7 @@ These standard python-docx members don't apply to a Superdoc-backed SDK:
67
67
  ### Phase-3b upstream-blocked surface
68
68
 
69
69
  These are *shipped at the surface* but partially or fully no-op at
70
- runtime, pending SuperDoc SDK changes outside this repo. The
71
- upstream constraints have been verified against
72
- [SuperDoc's published docs](https://docs.superdoc.dev/document-api/reference/styles/apply)
73
- and the bundled TypeScript types in
74
- `node_modules/@superdoc-dev/sdk/dist/generated/client.d.ts`. See
70
+ runtime, pending DOCX Studio backend changes. See
75
71
  `docx-studio/PYTHON_DOCX_PARITY_GAPS.md` § *What unblocks the
76
72
  remaining work* for the exact wire-op shape needed to unblock each.
77
73
 
@@ -132,16 +128,15 @@ not file-backed. Each is documented in the relevant docstring.
132
128
 
133
129
  - **`Document.create(name=, base_url=, api_key=, parent_folder_id=, workspace_id=, docx=, agora_base_url=)`**
134
130
  classmethod. Stock python-docx returns a blank in-memory document
135
- for `Document(None)`; we can't fabricate a SuperDocument asset
131
+ for `Document(None)`; we can't fabricate a Word document asset
136
132
  client-side, so net-new asset creation is a separate factory that
137
133
  hits `POST {base_url}/docs/empty`. The constructor positional-arg
138
134
  shape (`Document(asset_id)`) is preserved for parity.
139
135
  - **`docx=path`** mirrors the upstream `Document(docx="template.docx")`
140
- constructor kwarg: when provided, the local `.docx` is uploaded to
141
- agora's `/api/super-docs/create-from-upload` and used as the seed
142
- for the new SuperDocument. The bytes are mirrored to S3 server-side
143
- so subsequent template-asset reuse is fast. Without `docx=`, the
144
- default Athena template is applied as today.
136
+ constructor kwarg: when provided, the SDK creates a normal Word
137
+ document asset through Agora's docx-studio bridge, then imports the
138
+ local `.docx` into that asset. Without `docx=`, the default Athena
139
+ template is applied as today.
145
140
  - **`agora_base_url=`** is a transport-detail kwarg only required when
146
141
  the SDK can't derive the agora host from `base_url` (or from
147
142
  `$ATHENA_AGORA_BASE_URL`). The default substitution rule replaces
@@ -389,6 +384,38 @@ Issue numbers reference `python-openxml/python-docx`.
389
384
  cleanup loops, which left horizontal-rule and scratch-content artifacts
390
385
  in the document during failed-run recoveries.
391
386
 
387
+ - **`Paragraph.add_citation(source, *, anchor=None, excerpt=None,
388
+ display_value=None) -> str`** + **`Paragraph.clear_citations()`** +
389
+ **`Document.add_citation(source, *, destination_anchor, anchor=None,
390
+ ...)`** + **`Document.remove_citation(citation_id)`** (0.20.0+) —
391
+ SDK-native Athena citations. This is the programmatic equivalent of
392
+ the user selecting DOCX text and choosing "Use selection as citation
393
+ destination/source" in Olympus: it writes the canonical document
394
+ citation record into the document's ``citations`` Y.Array and applies
395
+ the citation-blue highlight to the destination paragraph/range. It is
396
+ not a live link and does not overwrite destination text.
397
+
398
+ Common agent pattern after writing a supported claim:
399
+
400
+ ```python
401
+ from docx import Document
402
+ from docx._references import AssetReference, SheetRangeAnchor
403
+
404
+ doc = Document("asset_word")
405
+ p = doc.add_paragraph("The combined assessment identified 20,049 total vulnerabilities.")
406
+ citation_id = p.add_citation(
407
+ AssetReference(id="asset_spreadsheet"),
408
+ anchor=SheetRangeAnchor(sheet_id=0, range="B2:B8"),
409
+ excerpt="20,049 total vulnerabilities",
410
+ display_value="Vulnerability total",
411
+ )
412
+ doc.save()
413
+ ```
414
+
415
+ Use ``Document.add_citation(..., destination_anchor=PmNodeAnchor(...))``
416
+ only when you already have a DOCX editor anchor id. For normal writing,
417
+ cite the ``Paragraph`` proxy you just created or found.
418
+
392
419
  - **`Document.export_docx(path=None, *, include_revisions=False)
393
420
  -> bytes`** (0.11.4+) — export the document's current state as
394
421
  ``.docx`` bytes, optionally writing to ``path``. Closes the gap
@@ -397,16 +424,13 @@ Issue numbers reference `python-openxml/python-docx`.
397
424
  harnesses and CI workflows need a programmatic .docx export to
398
425
  diff against ``python-docx`` output that doesn't require a
399
426
  human-driven Olympus UI action. Routes through ``ExportDocx`` →
400
- apps/api → SuperDoc's official ``doc.save({out: <tmp>})`` (the
401
- recommended backend export path per the SuperDoc team — Document
402
- API, not the browser-side ``editor.exportDocx`` shim). apps/api
427
+ apps/api's backend export path. apps/api
403
428
  writes the session state to a temp ``.docx``, reads bytes back,
404
429
  base64-encodes for the wire, and unlinks the temp in a
405
430
  ``finally`` block. The SDK decodes and writes to ``path``.
406
431
  ``include_revisions=True`` raises ``DocxError`` pre-flight:
407
- ``@superdoc-dev/sdk@1.8.1``'s ``DocSaveParams`` has no
408
- ``includeRevisions`` field, so honoring the flag isn't possible
409
- through ``doc.save({out})``.
432
+ the current backend export path has no ``includeRevisions`` support,
433
+ so honoring the flag isn't possible yet.
410
434
 
411
435
  Template seeding: ``Document.create()`` (no ``docx=``) already
412
436
  produces an exportable asset because ``POST /docs/empty`` seeds
@@ -1,7 +1,7 @@
1
1
  # DocX Execution Lab
2
2
 
3
3
  `scripts/docx_exec_lab.py` is a headless runner for quickly testing
4
- `athena-python-docx` snippets against Athena SuperDocument assets. It
4
+ `athena-python-docx` snippets against Athena Word document assets. It
5
5
  shortens the debug loop by running local SDK code directly against a
6
6
  chosen DOCX Studio API, capturing stdout/stderr/errors/timings, and
7
7
  optionally exporting the result to `.docx` for assertions.
@@ -85,10 +85,10 @@ uv run python scripts/docx_exec_lab.py \
85
85
  --export-docx
86
86
  ```
87
87
 
88
- ## Local UI With SuperDoc Embed
88
+ ## Local UI With DOCX Editor Embed
89
89
 
90
90
  The lab also includes a local-only HTML UI that wraps the same headless
91
- runner and iframes Olympus's existing SuperDoc embed route.
91
+ runner and iframes Olympus's existing DOCX editor embed route.
92
92
 
93
93
  ```bash
94
94
  cd docx-studio/python-sdk
@@ -107,7 +107,7 @@ Defaults:
107
107
  The UI accepts an `asset_id`, Python code, and optional assertions. The
108
108
  right pane can mint an editable embed token through staging Agora and
109
109
  iframe the returned `https://staging-app.athenaintel.com/embed/{token}`
110
- URL, so changes render in the real Olympus/SuperDoc viewer.
110
+ URL, so changes render in the real Olympus DOCX editor viewer.
111
111
 
112
112
  If the staging Agora URL differs, pass:
113
113
 
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: athena-python-docx
3
- Version: 0.20.0
4
- Summary: Drop-in replacement for python-docx that connects to Athena's Superdoc/Keryx collaborative document stack
3
+ Version: 0.21.0
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>
7
7
  License-Expression: MIT
@@ -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
@@ -27,7 +28,7 @@ Description-Content-Type: text/markdown
27
28
 
28
29
  # athena-python-docx
29
30
 
30
- Drop-in replacement for [python-docx](https://python-docx.readthedocs.io/) that connects to Athena's Superdoc + Keryx collaborative document stack.
31
+ Drop-in replacement for [python-docx](https://python-docx.readthedocs.io/) that connects to Athena's Keryx-backed Word document stack.
31
32
 
32
33
  ## Quick start
33
34
 
@@ -35,7 +36,7 @@ Drop-in replacement for [python-docx](https://python-docx.readthedocs.io/) that
35
36
  from docx import Document
36
37
  from docx.shared import Inches, Pt, RGBColor
37
38
 
38
- # Open an existing SuperDoc asset by its Athena asset_id
39
+ # Open an existing Word document asset by its Athena asset_id
39
40
  with Document("asset_abc123") as doc:
40
41
  doc.add_heading("Market Analysis", level=1)
41
42
 
@@ -74,15 +75,14 @@ headless runner in `scripts/docx_exec_lab.py`. It executes an arbitrary
74
75
  Python snippet with this checkout of `athena-python-docx`, captures
75
76
  stdout/stderr/errors/timings, and can export the resulting `.docx` for
76
77
  text assertions. `scripts/docx_exec_lab_server.py` adds a local UI with
77
- an Olympus/SuperDoc embed preview pane. See `DOCX_EXEC_LAB.md`.
78
+ an Olympus Word document embed preview pane. See `DOCX_EXEC_LAB.md`.
78
79
 
79
80
  ## Environment variables
80
81
 
81
- Required when connecting to Keryx (set by Athena backend when executing in Daytona):
82
+ Required when connecting to DOCX Studio (set by Athena backend when executing in Daytona):
82
83
 
83
- - `SUPERDOC_COLLAB_TOKEN` — short-lived Keryx JWT
84
- - `KERYX_WS_URL` — Keryx WebSocket base URL
85
- - `ATHENA_WORKSPACE_ID` — workspace routing segment
84
+ - `ATHENA_DOCX_BASE_URL` — DOCX Studio API base URL
85
+ - `ATHENA_DOCX_API_KEY` — Athena API key or service-issued DOCX Studio token
86
86
 
87
87
  ## License
88
88
 
@@ -1,6 +1,6 @@
1
1
  # athena-python-docx
2
2
 
3
- Drop-in replacement for [python-docx](https://python-docx.readthedocs.io/) that connects to Athena's Superdoc + Keryx collaborative document stack.
3
+ Drop-in replacement for [python-docx](https://python-docx.readthedocs.io/) that connects to Athena's Keryx-backed Word document stack.
4
4
 
5
5
  ## Quick start
6
6
 
@@ -8,7 +8,7 @@ Drop-in replacement for [python-docx](https://python-docx.readthedocs.io/) that
8
8
  from docx import Document
9
9
  from docx.shared import Inches, Pt, RGBColor
10
10
 
11
- # Open an existing SuperDoc asset by its Athena asset_id
11
+ # Open an existing Word document asset by its Athena asset_id
12
12
  with Document("asset_abc123") as doc:
13
13
  doc.add_heading("Market Analysis", level=1)
14
14
 
@@ -47,15 +47,14 @@ headless runner in `scripts/docx_exec_lab.py`. It executes an arbitrary
47
47
  Python snippet with this checkout of `athena-python-docx`, captures
48
48
  stdout/stderr/errors/timings, and can export the resulting `.docx` for
49
49
  text assertions. `scripts/docx_exec_lab_server.py` adds a local UI with
50
- an Olympus/SuperDoc embed preview pane. See `DOCX_EXEC_LAB.md`.
50
+ an Olympus Word document embed preview pane. See `DOCX_EXEC_LAB.md`.
51
51
 
52
52
  ## Environment variables
53
53
 
54
- Required when connecting to Keryx (set by Athena backend when executing in Daytona):
54
+ Required when connecting to DOCX Studio (set by Athena backend when executing in Daytona):
55
55
 
56
- - `SUPERDOC_COLLAB_TOKEN` — short-lived Keryx JWT
57
- - `KERYX_WS_URL` — Keryx WebSocket base URL
58
- - `ATHENA_WORKSPACE_ID` — workspace routing segment
56
+ - `ATHENA_DOCX_BASE_URL` — DOCX Studio API base URL
57
+ - `ATHENA_DOCX_API_KEY` — Athena API key or service-issued DOCX Studio token
59
58
 
60
59
  ## License
61
60
 
@@ -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.20.0"
9
+ __version__ = "0.21.0"
10
10
 
11
11
  from docx.api import Document
12
12
  from docx._buffer import flush_all
@@ -0,0 +1,74 @@
1
+ """Helpers for SDK-native Athena citations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import uuid
6
+ from typing import Any
7
+
8
+ from docx._references import AssetReference
9
+ from docx.commands import AddCitation
10
+
11
+
12
+ def new_citation_id() -> str:
13
+ return f"citation_{uuid.uuid4().hex[:12]}"
14
+
15
+
16
+ def source_ref_to_dict(source: AssetReference) -> dict[str, Any]:
17
+ if not isinstance(source, AssetReference):
18
+ raise TypeError(f"source must be an AssetReference; got {type(source).__name__}")
19
+ return source.to_dict()
20
+
21
+
22
+ def anchor_to_dict(anchor: object | None) -> dict[str, Any] | None:
23
+ if anchor is None:
24
+ return None
25
+ if not hasattr(anchor, "to_dict"):
26
+ raise TypeError(
27
+ "anchor must be an Anchor dataclass (SheetRangeAnchor, "
28
+ f"PmNodeAnchor, etc.) — got {type(anchor).__name__}"
29
+ )
30
+ value = anchor.to_dict()
31
+ if not isinstance(value, dict) or not value.get("type"):
32
+ raise TypeError("anchor.to_dict() must return a dict with a 'type' field")
33
+ return value
34
+
35
+
36
+ def destination_anchor_to_dict(destination_anchor: object) -> dict[str, Any]:
37
+ if hasattr(destination_anchor, "to_dict"):
38
+ destination_anchor = destination_anchor.to_dict()
39
+ if not isinstance(destination_anchor, dict):
40
+ raise TypeError(
41
+ "destination_anchor must be a PmNodeAnchor, PmNodeRangeAnchor, or wire dict"
42
+ )
43
+ anchor_type = destination_anchor.get("type")
44
+ if anchor_type == "pm_node" and destination_anchor.get("nodeId"):
45
+ return dict(destination_anchor)
46
+ if (
47
+ anchor_type == "pm_node_range"
48
+ and destination_anchor.get("startNodeId")
49
+ and destination_anchor.get("endNodeId")
50
+ ):
51
+ return dict(destination_anchor)
52
+ raise ValueError(
53
+ "destination_anchor must be {'type':'pm_node','nodeId':...} or "
54
+ "{'type':'pm_node_range','startNodeId':...,'endNodeId':...}"
55
+ )
56
+
57
+
58
+ def build_add_citation_command(
59
+ *,
60
+ destination_anchor: object,
61
+ source: AssetReference,
62
+ anchor: object | None,
63
+ excerpt: str | None,
64
+ display_value: str | None,
65
+ ) -> tuple[str, AddCitation]:
66
+ citation_id = new_citation_id()
67
+ return citation_id, AddCitation(
68
+ destination_anchor=destination_anchor_to_dict(destination_anchor),
69
+ source_ref=source_ref_to_dict(source),
70
+ source_anchor=anchor_to_dict(anchor),
71
+ citation_id=citation_id,
72
+ excerpt=excerpt,
73
+ display_value=display_value,
74
+ )
@@ -1,10 +1,8 @@
1
1
  """HTTP bootstrap for Document.create().
2
2
 
3
- The SDK's primary transport is y-websocket via Superdoc; this module is
4
- the *only* HTTP client in the package. It exists solely so that
5
- ``Document.create()`` can hit ``POST /docs/empty`` on docx-studio to
6
- provision a new SuperDocument asset and receive a collab bundle to open
7
- the document with.
3
+ The SDK's transport is HTTP via docx-studio. This module keeps the
4
+ bootstrap and upload calls on urllib so ``Document.create()`` can provision
5
+ Word document assets without pulling in extra runtime dependencies.
8
6
 
9
7
  We use ``urllib`` from stdlib to avoid pulling ``httpx`` / ``requests``
10
8
  into the runtime — this code runs in Daytona sandboxes where every MB
@@ -15,6 +13,7 @@ from __future__ import annotations
15
13
 
16
14
  import json
17
15
  import os
16
+ import urllib.parse
18
17
  import urllib.error
19
18
  import urllib.request
20
19
  from typing import TypedDict
@@ -26,26 +25,12 @@ from docx.errors import (
26
25
  )
27
26
 
28
27
 
29
- class _CollabBundle(TypedDict):
30
- """The collab credentials returned alongside a freshly-created asset.
31
-
32
- Note: env-var-shape (matches what ``Session`` reads), even though
33
- the wire format from docx-studio uses different key names. The
34
- keys are renamed at parse time.
35
- """
36
-
37
- SUPERDOC_COLLAB_TOKEN: str
38
- KERYX_WS_URL: str
39
- ATHENA_WORKSPACE_ID: str
40
-
41
-
42
28
  class CreateAssetResult(TypedDict):
43
29
  """Parsed response from ``POST /docs/empty``."""
44
30
 
45
31
  asset_id: str
46
32
  name: str
47
33
  workspace_id: str
48
- collab: _CollabBundle
49
34
 
50
35
 
51
36
  _BASE_URL_ENV = "ATHENA_DOCX_BASE_URL"
@@ -67,6 +52,37 @@ def _derive_agora_base_url(docx_base_url: str) -> str | None:
67
52
  return docx_base_url.replace("docx-studio", "agora")
68
53
 
69
54
 
55
+ def _delete_created_asset_best_effort(
56
+ *,
57
+ agora_base_url: str,
58
+ asset_id: str,
59
+ api_key: str,
60
+ timeout: float,
61
+ ) -> None:
62
+ """Best-effort rollback for create-then-import failures."""
63
+ from docx import __version__
64
+
65
+ delete_url = (
66
+ agora_base_url.rstrip().rstrip("/")
67
+ + "/api/docx-studio/assets/"
68
+ + urllib.parse.quote(asset_id, safe="")
69
+ )
70
+ req = urllib.request.Request( # noqa: S310
71
+ delete_url,
72
+ method="DELETE",
73
+ headers={
74
+ "Authorization": f"Bearer {api_key}",
75
+ "Accept": "application/json",
76
+ "User-Agent": f"athena-python-docx/{__version__}",
77
+ },
78
+ )
79
+ try:
80
+ with urllib.request.urlopen(req, timeout=timeout) as resp: # noqa: S310
81
+ resp.read()
82
+ except Exception: # noqa: BLE001
83
+ pass
84
+
85
+
70
86
  def create_empty_document(
71
87
  *,
72
88
  base_url: str | None = None,
@@ -91,8 +107,7 @@ def create_empty_document(
91
107
  timeout: Request timeout in seconds.
92
108
 
93
109
  Returns:
94
- Parsed response dict with the new asset_id and a collab bundle
95
- ready to feed into ``Session(..., bundle=...)``.
110
+ Parsed response dict with the new asset_id, display name, and workspace.
96
111
 
97
112
  Raises:
98
113
  SessionError: missing base_url or unparseable response.
@@ -182,42 +197,22 @@ def create_empty_document(
182
197
  asset_id_obj = parsed.get("assetId")
183
198
  name_obj = parsed.get("name")
184
199
  ws_obj = parsed.get("workspaceId")
185
- collab_obj = parsed.get("collab")
186
200
  if not (
187
- isinstance(asset_id_obj, str)
188
- and isinstance(name_obj, str)
189
- and isinstance(ws_obj, str)
190
- and isinstance(collab_obj, dict)
201
+ isinstance(asset_id_obj, str) and isinstance(name_obj, str) and isinstance(ws_obj, str)
191
202
  ):
192
203
  raise SessionError(
193
204
  f"docx-studio response is missing required fields: {parsed!r}",
194
205
  )
195
- token_obj = collab_obj.get("token")
196
- ws_url_obj = collab_obj.get("wsUrl")
197
- bundle_ws_obj = collab_obj.get("workspaceId")
198
- if not (
199
- isinstance(token_obj, str)
200
- and isinstance(ws_url_obj, str)
201
- and isinstance(bundle_ws_obj, str)
202
- ):
203
- raise SessionError(
204
- f"docx-studio collab bundle is malformed: {collab_obj!r}",
205
- )
206
206
 
207
207
  return CreateAssetResult(
208
208
  asset_id=asset_id_obj,
209
209
  name=name_obj,
210
210
  workspace_id=ws_obj,
211
- collab=_CollabBundle(
212
- SUPERDOC_COLLAB_TOKEN=token_obj,
213
- KERYX_WS_URL=ws_url_obj,
214
- ATHENA_WORKSPACE_ID=bundle_ws_obj,
215
- ),
216
211
  )
217
212
 
218
213
 
219
214
  class UploadAssetResult(TypedDict):
220
- """Parsed response from agora's ``POST /api/super-docs/create-from-upload``."""
215
+ """Parsed response from the create-and-import upload flow."""
221
216
 
222
217
  asset_id: str
223
218
  name: str
@@ -231,14 +226,13 @@ def upload_document(
231
226
  api_key: str | None = None,
232
227
  name: str | None = None,
233
228
  parent_folder_id: str | None = None,
229
+ workspace_id: str | None = None,
234
230
  timeout: float = 120.0,
235
231
  ) -> UploadAssetResult:
236
- """Upload a local ``.docx`` to agora and create a new SuperDocument asset.
232
+ """Upload a local ``.docx`` to agora and create a new Word document asset.
237
233
 
238
- The endpoint lives on agora (``POST /api/super-docs/create-from-upload``),
239
- not docx-studio, because the upload + Y.Doc-seed path is implemented
240
- server-side in agora. The SDK still talks to docx-studio for command
241
- application; this function only handles the initial create.
234
+ The upload path creates a normal Word document asset through agora's
235
+ docx-studio bridge, then imports the local ``.docx`` into that asset.
242
236
 
243
237
  Args:
244
238
  docx_path: Filesystem path to the ``.docx`` to upload.
@@ -251,6 +245,8 @@ def upload_document(
251
245
  api_key: Athena API key. Falls back to ``$ATHENA_DOCX_API_KEY``.
252
246
  name: Optional asset title (defaults to the file's stem).
253
247
  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.
254
250
  timeout: Request timeout in seconds (uploads can be slow for big
255
251
  decks/templates).
256
252
 
@@ -262,10 +258,8 @@ def upload_document(
262
258
  AuthenticationError: missing api_key, or 401/403 from server.
263
259
  DocxError: any other 4xx/5xx, or a malformed response.
264
260
  """
265
- import io
266
- import mimetypes
261
+ import base64
267
262
  import os
268
- import uuid
269
263
 
270
264
  resolved_docx_base: str | None = docx_base_url or os.environ.get(_BASE_URL_ENV)
271
265
  # Create must authenticate as the REAL user so the new asset's ``created_by``
@@ -311,49 +305,25 @@ def upload_document(
311
305
  base = os.path.basename(docx_path)
312
306
  title = base[:-5] if base.lower().endswith(".docx") else base
313
307
 
314
- # Build the agora URL with query-string params (FastAPI binds non-body
315
- # args from the query string for multipart routes).
316
- from urllib.parse import urlencode
308
+ with open(docx_path, "rb") as f:
309
+ file_bytes: bytes = f.read()
317
310
 
318
- qs: dict[str, str] = {"is_template": "false", "title": title}
311
+ create_body: dict[str, str] = {"name": title}
319
312
  if parent_folder_id is not None:
320
- qs["parent_folder_id"] = parent_folder_id
321
-
322
- url: str = (
323
- resolved_agora_base.rstrip("/") + "/api/super-docs/create-from-upload?" + urlencode(qs)
324
- )
313
+ create_body["parent_folder_id"] = parent_folder_id
314
+ if workspace_id is not None:
315
+ create_body["workspace_id"] = workspace_id
325
316
 
326
- # Build a minimal multipart/form-data body without pulling in
327
- # `requests` — keep the urllib-only dep profile.
328
- with open(docx_path, "rb") as f:
329
- file_bytes: bytes = f.read()
330
- content_type: str = (
331
- mimetypes.guess_type(docx_path)[0]
332
- or "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
333
- )
334
- boundary: str = f"----athenaDocxBoundary{uuid.uuid4().hex}"
335
- filename = os.path.basename(docx_path)
336
- body_parts: list[bytes] = []
337
- body_parts.append(f"--{boundary}\r\n".encode("utf-8"))
338
- body_parts.append(
339
- (
340
- 'Content-Disposition: form-data; name="file"; '
341
- f'filename="{filename}"\r\n'
342
- f"Content-Type: {content_type}\r\n\r\n"
343
- ).encode("utf-8"),
344
- )
345
- body_parts.append(file_bytes)
346
- body_parts.append(f"\r\n--{boundary}--\r\n".encode("utf-8"))
347
- body: bytes = b"".join(body_parts)
317
+ create_url: str = resolved_agora_base.rstrip("/") + "/api/docx-studio/assets"
348
318
 
349
319
  from docx import __version__
350
320
 
351
321
  req = urllib.request.Request( # noqa: S310
352
- url,
353
- data=body,
322
+ create_url,
323
+ data=json.dumps(create_body).encode("utf-8"),
354
324
  method="POST",
355
325
  headers={
356
- "Content-Type": f"multipart/form-data; boundary={boundary}",
326
+ "Content-Type": "application/json",
357
327
  "Authorization": f"Bearer {resolved_key}",
358
328
  "Accept": "application/json",
359
329
  "User-Agent": f"athena-python-docx/{__version__}",
@@ -374,15 +344,13 @@ def upload_document(
374
344
  f"agora rejected the API key (HTTP {e.code}): {err_body}",
375
345
  ) from e
376
346
  raise DocxError(
377
- f"agora /api/super-docs/create-from-upload returned HTTP {e.code}: {err_body}",
347
+ f"agora /api/docx-studio/assets returned HTTP {e.code}: {err_body}",
378
348
  ) from e
379
349
  except urllib.error.URLError as e:
380
350
  raise SessionError(
381
- f"Unable to reach agora at {url}: {e.reason}",
351
+ f"Unable to reach agora at {create_url}: {e.reason}",
382
352
  ) from e
383
353
 
384
- # Avoid unused import warning when io isn't otherwise referenced.
385
- _ = io
386
354
  try:
387
355
  parsed: dict = json.loads(raw.decode("utf-8"))
388
356
  except (UnicodeDecodeError, json.JSONDecodeError) as e:
@@ -390,11 +358,74 @@ def upload_document(
390
358
  f"agora returned non-JSON response: {raw[:200]!r}",
391
359
  ) from e
392
360
 
393
- asset_id_obj = parsed.get("id")
394
- name_obj = parsed.get("title")
361
+ asset_id_obj = parsed.get("asset_id")
362
+ name_obj = parsed.get("name")
395
363
  if not (isinstance(asset_id_obj, str) and isinstance(name_obj, str)):
396
364
  raise SessionError(
397
365
  f"agora response is missing required fields: {parsed!r}",
398
366
  )
399
367
 
368
+ import_url: str = (
369
+ resolved_agora_base.rstrip("/")
370
+ + "/api/docx-studio/assets/"
371
+ + urllib.parse.quote(asset_id_obj, safe="")
372
+ + "/import"
373
+ )
374
+ import_body = {
375
+ "docx_base64": base64.b64encode(file_bytes).decode("ascii"),
376
+ "overwrite": True,
377
+ }
378
+ import_req = urllib.request.Request( # noqa: S310
379
+ import_url,
380
+ data=json.dumps(import_body).encode("utf-8"),
381
+ method="POST",
382
+ headers={
383
+ "Content-Type": "application/json",
384
+ "Authorization": f"Bearer {resolved_key}",
385
+ "Accept": "application/json",
386
+ "User-Agent": f"athena-python-docx/{__version__}",
387
+ },
388
+ )
389
+
390
+ try:
391
+ try:
392
+ with urllib.request.urlopen(import_req, timeout=timeout) as resp: # noqa: S310
393
+ import_raw: bytes = resp.read()
394
+ except urllib.error.HTTPError as e:
395
+ err_body = ""
396
+ try:
397
+ err_body = e.read().decode("utf-8", errors="replace")
398
+ except Exception: # noqa: BLE001
399
+ pass
400
+ if e.code in (401, 403):
401
+ raise AuthenticationError(
402
+ f"agora rejected the API key while importing DOCX (HTTP {e.code}): {err_body}",
403
+ ) from e
404
+ raise DocxError(
405
+ f"agora /api/docx-studio/assets/{asset_id_obj}/import returned HTTP {e.code}: {err_body}",
406
+ ) from e
407
+ except urllib.error.URLError as e:
408
+ raise SessionError(
409
+ f"Unable to reach agora at {import_url}: {e.reason}",
410
+ ) from e
411
+
412
+ try:
413
+ import_parsed: dict = json.loads(import_raw.decode("utf-8"))
414
+ except (UnicodeDecodeError, json.JSONDecodeError) as e:
415
+ raise SessionError(
416
+ f"agora import returned non-JSON response: {import_raw[:200]!r}",
417
+ ) from e
418
+ if import_parsed.get("asset_id") != asset_id_obj or import_parsed.get("seeded") is not True:
419
+ raise SessionError(
420
+ f"agora import response is missing required fields: {import_parsed!r}",
421
+ )
422
+ except Exception:
423
+ _delete_created_asset_best_effort(
424
+ agora_base_url=resolved_agora_base,
425
+ asset_id=asset_id_obj,
426
+ api_key=resolved_key,
427
+ timeout=timeout,
428
+ )
429
+ raise
430
+
400
431
  return UploadAssetResult(asset_id=asset_id_obj, name=name_obj)