athena-python-docx 0.43.0__tar.gz → 0.44.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 (501) hide show
  1. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/PKG-INFO +8 -1
  2. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/README.md +7 -0
  3. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/__init__.py +1 -1
  4. athena_python_docx-0.44.0/docx/_asset_token.py +354 -0
  5. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/_http_doc.py +134 -33
  6. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/client.py +12 -9
  7. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/pyproject.toml +1 -1
  8. athena_python_docx-0.44.0/tests/test_asset_tokens.py +468 -0
  9. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/uv.lock +1 -1
  10. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/.gitignore +0 -0
  11. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/AGENTS.md +0 -0
  12. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/CLAUDE.md +0 -0
  13. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/DOCX_EXEC_LAB.md +0 -0
  14. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/_athena_extension.py +0 -0
  15. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/_batching.py +0 -0
  16. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/_buffer.py +0 -0
  17. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/_capabilities.py +0 -0
  18. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/_citations.py +0 -0
  19. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/_execution.py +0 -0
  20. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/_http.py +0 -0
  21. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/_image_utils.py +0 -0
  22. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/_postproc.py +0 -0
  23. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/_proxy.py +0 -0
  24. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/_ptc.py +0 -0
  25. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/_references.py +0 -0
  26. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/_run_order.py +0 -0
  27. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/_table_styles.py +0 -0
  28. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/_timeouts.py +0 -0
  29. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/_tracked.py +0 -0
  30. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/_xml_text.py +0 -0
  31. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/api.py +0 -0
  32. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/blkcntnr.py +0 -0
  33. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/bookmarks.py +0 -0
  34. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/charts.py +0 -0
  35. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/commands.py +0 -0
  36. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/comments.py +0 -0
  37. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/document.py +0 -0
  38. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/drawing/__init__.py +0 -0
  39. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/enum/__init__.py +0 -0
  40. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/enum/base.py +0 -0
  41. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/enum/dml.py +0 -0
  42. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/enum/section.py +0 -0
  43. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/enum/shape.py +0 -0
  44. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/enum/style.py +0 -0
  45. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/enum/table.py +0 -0
  46. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/enum/text.py +0 -0
  47. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/errors.py +0 -0
  48. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/exceptions.py +0 -0
  49. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/fields.py +0 -0
  50. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/footnotes.py +0 -0
  51. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/image/__init__.py +0 -0
  52. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/image/exceptions.py +0 -0
  53. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/image/image.py +0 -0
  54. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/math.py +0 -0
  55. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/opc/__init__.py +0 -0
  56. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/opc/coreprops.py +0 -0
  57. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/oxml/__init__.py +0 -0
  58. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/revisions.py +0 -0
  59. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/sdt.py +0 -0
  60. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/section.py +0 -0
  61. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/session.py +0 -0
  62. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/settings.py +0 -0
  63. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/shape.py +0 -0
  64. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/shared.py +0 -0
  65. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/styles/__init__.py +0 -0
  66. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/styles/_table.py +0 -0
  67. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/styles/latent.py +0 -0
  68. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/styles/style.py +0 -0
  69. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/styles/styles.py +0 -0
  70. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/table.py +0 -0
  71. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/text/__init__.py +0 -0
  72. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/text/font.py +0 -0
  73. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/text/hyperlink.py +0 -0
  74. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/text/pagebreak.py +0 -0
  75. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/text/paragraph.py +0 -0
  76. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/text/parfmt.py +0 -0
  77. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/text/run.py +0 -0
  78. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/text/tabstops.py +0 -0
  79. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/toc.py +0 -0
  80. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/docx/typing.py +0 -0
  81. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/scripts/docx_exec_lab.py +0 -0
  82. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/scripts/docx_exec_lab_examples/fast_table_fill.py +0 -0
  83. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/scripts/docx_exec_lab_examples/find_replace_literal.py +0 -0
  84. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/scripts/docx_exec_lab_server.py +0 -0
  85. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/scripts/dump_wire_fixtures.py +0 -0
  86. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/scripts/release.sh +0 -0
  87. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/scripts/round_trip_smoke.py +0 -0
  88. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/scripts/smoke_test_block_not_found.py +0 -0
  89. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/scripts/validate_find_replace_asset.py +0 -0
  90. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/__init__.py +0 -0
  91. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/conftest.py +0 -0
  92. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/METHODOLOGY.md +0 -0
  93. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/README.md +0 -0
  94. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/__init__.py +0 -0
  95. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/__init__.py +0 -0
  96. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/agent_documents.py +0 -0
  97. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/default_style_run_formatting.py +0 -0
  98. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/header_footer_pictures.py +0 -0
  99. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/less_used_writes.py +0 -0
  100. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/merged_cell_identity.py +0 -0
  101. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/paragraph_text_setter.py +0 -0
  102. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/picture_runs.py +0 -0
  103. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/proxy_equality.py +0 -0
  104. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/return_types.py +0 -0
  105. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/xml_incompatible_text.py +0 -0
  106. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe/zero_and_negative_indents.py +0 -0
  107. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe_cases.py +0 -0
  108. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/ab_probe_runner.py +0 -0
  109. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/auto_gen_cases.py +0 -0
  110. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/binary_round_trip.py +0 -0
  111. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/cases.py +0 -0
  112. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/complex_cases.py +0 -0
  113. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/coverage_report.py +0 -0
  114. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/extract.py +0 -0
  115. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/extreme_cases.py +0 -0
  116. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/fake_session.py +0 -0
  117. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/firm_templates/README.md +0 -0
  118. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/firm_templates/__init__.py +0 -0
  119. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/firm_templates/_runner.py +0 -0
  120. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/firm_templates/extractor.py +0 -0
  121. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/firm_templates/test_pw_corpus.py +0 -0
  122. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/firm_templates/test_pw_research_digest.py +0 -0
  123. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/local_runner.py +0 -0
  124. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/mega_cases.py +0 -0
  125. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshot.py +0 -0
  126. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
  127. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
  128. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
  129. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
  130. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
  131. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
  132. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
  133. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
  134. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
  135. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
  136. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
  137. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
  138. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
  139. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
  140. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
  141. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
  142. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
  143. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
  144. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
  145. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
  146. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
  147. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
  148. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
  149. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
  150. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
  151. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
  152. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
  153. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
  154. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
  155. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
  156. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
  157. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
  158. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
  159. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
  160. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
  161. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
  162. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
  163. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
  164. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
  165. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
  166. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
  167. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
  168. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
  169. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
  170. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
  171. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
  172. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
  173. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
  174. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
  175. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
  176. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
  177. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
  178. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
  179. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
  180. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
  181. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
  182. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
  183. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
  184. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
  185. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
  186. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
  187. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
  188. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
  189. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
  190. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
  191. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
  192. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
  193. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
  194. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
  195. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
  196. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
  197. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
  198. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
  199. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
  200. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
  201. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
  202. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
  203. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
  204. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
  205. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
  206. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
  207. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
  208. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
  209. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
  210. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
  211. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
  212. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
  213. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
  214. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
  215. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
  216. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
  217. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
  218. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
  219. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
  220. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
  221. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
  222. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
  223. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
  224. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
  225. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
  226. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
  227. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
  228. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
  229. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
  230. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
  231. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
  232. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
  233. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
  234. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
  235. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
  236. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
  237. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
  238. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
  239. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
  240. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
  241. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
  242. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
  243. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
  244. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
  245. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
  246. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
  247. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
  248. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
  249. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
  250. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
  251. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
  252. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
  253. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
  254. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
  255. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
  256. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
  257. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
  258. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
  259. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
  260. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
  261. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
  262. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
  263. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
  264. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
  265. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
  266. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
  267. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
  268. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
  269. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
  270. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
  271. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
  272. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
  273. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw16_header_footer_tables.json +0 -0
  274. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw17_header_letterhead_filled.json +0 -0
  275. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw18_header_table_properties.json +0 -0
  276. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw19_header_table_rows_columns.json +0 -0
  277. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/op_snapshots/rw20_header_table_merges.json +0 -0
  278. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/ours_spec.json +0 -0
  279. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/parity_crawl.py +0 -0
  280. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/parity_diff.json +0 -0
  281. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/real_world_cases.py +0 -0
  282. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/round_trip_tests.py +0 -0
  283. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/stock_spec.json +0 -0
  284. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
  285. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fixtures/field_reads.json +0 -0
  286. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fixtures/note_reference_reads.json +0 -0
  287. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fixtures/ruby_reads.json +0 -0
  288. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/fixtures/symbol_reads.json +0 -0
  289. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/live/__init__.py +0 -0
  290. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/live/baseline.json +0 -0
  291. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/live/fixture_scripts.py +0 -0
  292. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/live/reader.py +0 -0
  293. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/live/runner.py +0 -0
  294. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/live/sdk_driver.py +0 -0
  295. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/live/stock_driver.py +0 -0
  296. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/live/test_live_runner.py +0 -0
  297. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/parity/README.md +0 -0
  298. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/parity/__init__.py +0 -0
  299. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/parity/baseline_gaps.json +0 -0
  300. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/parity/compare.py +0 -0
  301. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/parity/intentional_deviations.json +0 -0
  302. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/parity/introspect.py +0 -0
  303. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
  304. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/parity/reports/gap_report.json +0 -0
  305. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/parity/run_parity.py +0 -0
  306. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/parity/snapshots/athena_latest.json +0 -0
  307. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
  308. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/parity/test_parity_gap.py +0 -0
  309. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_add_section.py +0 -0
  310. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_add_section_extract_items.py +0 -0
  311. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_alignment_values.py +0 -0
  312. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_athena_extension_creates_session_open.py +0 -0
  313. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_athena_extensions_contract.py +0 -0
  314. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_athena_extensions_registry.py +0 -0
  315. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_batch_chunking.py +0 -0
  316. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_batching_perf.py +0 -0
  317. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_battery_cases.py +0 -0
  318. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_block_not_found_error.py +0 -0
  319. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_buffer.py +0 -0
  320. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_cell_add_paragraph_wire_shape.py +0 -0
  321. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_cell_add_table_not_supported.py +0 -0
  322. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_cell_borders_spec.py +0 -0
  323. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_cell_fastpath_runs.py +0 -0
  324. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_cell_inner_add_hyperlink_stash.py +0 -0
  325. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_cell_inner_add_run_via_cell_insert.py +0 -0
  326. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_cell_inner_format_stash.py +0 -0
  327. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_cell_inner_run_format_stash.py +0 -0
  328. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_cell_inner_run_guard.py +0 -0
  329. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_cell_merge.py +0 -0
  330. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_cell_nested_tables.py +0 -0
  331. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_cell_paragraphs_listed.py +0 -0
  332. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_cell_text_plain_fastpath.py +0 -0
  333. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_cell_text_replace_semantics.py +0 -0
  334. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_citations.py +0 -0
  335. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_collapsed_range_format.py +0 -0
  336. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_color_format_theme.py +0 -0
  337. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_command_dataclasses.py +0 -0
  338. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_commands.py +0 -0
  339. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_comment_paragraphs.py +0 -0
  340. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_comments.py +0 -0
  341. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_comments_first_read.py +0 -0
  342. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_content_control_value.py +0 -0
  343. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_core_properties.py +0 -0
  344. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_create_workspace.py +0 -0
  345. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_document_asset_id_property.py +0 -0
  346. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_document_clear.py +0 -0
  347. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_document_create.py +0 -0
  348. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_document_create_from_template.py +0 -0
  349. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_document_factory_validation.py +0 -0
  350. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_document_settings_session_open.py +0 -0
  351. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_docx_exec_lab.py +0 -0
  352. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_docx_exec_lab_server.py +0 -0
  353. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_drawing_image.py +0 -0
  354. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
  355. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_empty_hyperlinks.py +0 -0
  356. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_enum_parity.py +0 -0
  357. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_execution_scope.py +0 -0
  358. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_export_docx_clean.py +0 -0
  359. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_export_rescue_anchors.py +0 -0
  360. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_field_reads_python_docx_parity.py +0 -0
  361. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_field_refresh.py +0 -0
  362. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_field_runs.py +0 -0
  363. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_find_replace_session_open.py +0 -0
  364. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_flushed_write_failures.py +0 -0
  365. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_http_transport.py +0 -0
  366. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_hyperlink_coalescing.py +0 -0
  367. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_image_url_data_uri.py +0 -0
  368. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_inline_page_break.py +0 -0
  369. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_inline_shape_alt_text.py +0 -0
  370. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_inline_shape_size.py +0 -0
  371. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_inline_shape_type.py +0 -0
  372. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_inline_shapes_floating.py +0 -0
  373. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_insert_deferred.py +0 -0
  374. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_iter_inner_content.py +0 -0
  375. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_latent_style_writes.py +0 -0
  376. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_line_clear_break.py +0 -0
  377. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_list_styles.py +0 -0
  378. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_listed_paragraph_reads.py +0 -0
  379. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_literal_no_break_hyphens.py +0 -0
  380. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_literal_soft_hyphens.py +0 -0
  381. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_merged_cell_identity.py +0 -0
  382. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_merged_cell_secondary_slot.py +0 -0
  383. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_merged_cells.py +0 -0
  384. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_missing_modules.py +0 -0
  385. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_nested_table_refused.py +0 -0
  386. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_note_reference_reads_python_docx_parity.py +0 -0
  387. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_oxml_shim.py +0 -0
  388. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_page_break.py +0 -0
  389. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_paragraph_delete.py +0 -0
  390. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_paragraph_indent_reads.py +0 -0
  391. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_paragraph_runs_hyperlinks.py +0 -0
  392. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_paragraph_text_empty_run.py +0 -0
  393. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_paragraph_text_len_cache.py +0 -0
  394. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_parfmt_clears.py +0 -0
  395. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_parity_misc.py +0 -0
  396. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_parity_round2.py +0 -0
  397. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_partial_failure_cascade.py +0 -0
  398. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_pdf_export_and_page_renders.py +0 -0
  399. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_phase_a_behavior.py +0 -0
  400. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_phase_b_headers_footers.py +0 -0
  401. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_phase_c_tables.py +0 -0
  402. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_positional_cell_id.py +0 -0
  403. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_postproc_cell_format_rewrite.py +0 -0
  404. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_postproc_cell_run_format_rewrite.py +0 -0
  405. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_postproc_ref_restore.py +0 -0
  406. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_pr19766_review_fixes.py +0 -0
  407. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_proxy_equality.py +0 -0
  408. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_ptc.py +0 -0
  409. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_python_docx_api_parity.py +0 -0
  410. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_read_run_ranges.py +0 -0
  411. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_rendered_page_breaks.py +0 -0
  412. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_repeated_node_read.py +0 -0
  413. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_restart_numbering.py +0 -0
  414. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_revisions.py +0 -0
  415. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_round_trip_smoke_script.py +0 -0
  416. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_ruby_reads_python_docx_parity.py +0 -0
  417. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_run_add_picture_anchor.py +0 -0
  418. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_run_break_inner_content.py +0 -0
  419. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_run_edges.py +0 -0
  420. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_run_formatting_wire.py +0 -0
  421. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_run_identity.py +0 -0
  422. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_run_later_runs.py +0 -0
  423. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_run_mark_inheritance.py +0 -0
  424. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_run_page_number_blocks.py +0 -0
  425. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_run_table_style_objects.py +0 -0
  426. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_run_text_python_docx.py +0 -0
  427. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_run_text_refreshes_paragraph.py +0 -0
  428. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_run_unmodeled_toggles.py +0 -0
  429. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_sdk_reads_served.py +0 -0
  430. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_section_read_lengths.py +0 -0
  431. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_sections_empty_list.py +0 -0
  432. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
  433. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_add_picture.py +0 -0
  434. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_add_run.py +0 -0
  435. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
  436. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
  437. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_comments_get.py +0 -0
  438. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_document_audit.py +0 -0
  439. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_document_element.py +0 -0
  440. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_enum_section.py +0 -0
  441. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_font_audit.py +0 -0
  442. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_header_footer.py +0 -0
  443. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_hyperlink.py +0 -0
  444. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_inline_shape.py +0 -0
  445. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
  446. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_misc.py +0 -0
  447. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
  448. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_paragraph_style.py +0 -0
  449. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
  450. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_parfmt.py +0 -0
  451. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_row_col_cell.py +0 -0
  452. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_run_add_break.py +0 -0
  453. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
  454. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_run_style.py +0 -0
  455. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_run_style_strict.py +0 -0
  456. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_run_text.py +0 -0
  457. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_run_underline.py +0 -0
  458. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_section_audit.py +0 -0
  459. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_section_dimensions.py +0 -0
  460. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_section_onoff.py +0 -0
  461. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_settings.py +0 -0
  462. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_shared_audit.py +0 -0
  463. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_style.py +0 -0
  464. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_styles.py +0 -0
  465. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_table_audit.py +0 -0
  466. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_table_cell.py +0 -0
  467. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_table_dimensions.py +0 -0
  468. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_silent_stub_table_layout.py +0 -0
  469. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_smoke_integration.py +0 -0
  470. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_style_acceptance.py +0 -0
  471. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_style_delete.py +0 -0
  472. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_style_font.py +0 -0
  473. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_style_formatting_writes.py +0 -0
  474. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_style_metadata_writes.py +0 -0
  475. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_style_names.py +0 -0
  476. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_style_resolution.py +0 -0
  477. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_style_setters_contract.py +0 -0
  478. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_style_tab_stops.py +0 -0
  479. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_style_tab_stops_read.py +0 -0
  480. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_symbol_reads_python_docx_parity.py +0 -0
  481. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_tab_stop_writes.py +0 -0
  482. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_table_add_row_grid_row.py +0 -0
  483. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_table_anchors.py +0 -0
  484. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_table_clears.py +0 -0
  485. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_table_direction.py +0 -0
  486. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_table_length_reads.py +0 -0
  487. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_table_listing_reads.py +0 -0
  488. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_table_read_reuse.py +0 -0
  489. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_table_set_cell_perf.py +0 -0
  490. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_table_style_id_resolution.py +0 -0
  491. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_temporarily_unavailable.py +0 -0
  492. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_text_control_characters.py +0 -0
  493. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_tracked_revisions_offsets.py +0 -0
  494. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_unmapped_alignment_reads.py +0 -0
  495. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_uv_lock_version.py +0 -0
  496. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_validate_find_replace_asset_script.py +0 -0
  497. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_versioned_read_reuse.py +0 -0
  498. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_wire_contract.py +0 -0
  499. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_xml_attr_guard.py +0 -0
  500. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_xml_incompatible_text.py +0 -0
  501. {athena_python_docx-0.43.0 → athena_python_docx-0.44.0}/tests/test_zod_wire_contract.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: athena-python-docx
3
- Version: 0.43.0
3
+ Version: 0.44.0
4
4
  Summary: Drop-in replacement for python-docx that connects to Athena's DOCX Studio collaborative document stack
5
5
  Project-URL: Homepage, https://athenaintelligence.ai
6
6
  Author-email: Athena Intelligence <engineering@athenaintelligence.ai>
@@ -81,6 +81,13 @@ Required when connecting to DOCX Studio (set by the Athena backend in the `execu
81
81
  - `ATHENA_DOCX_BASE_URL` — DOCX Studio API base URL
82
82
  - `ATHENA_DOCX_API_KEY` — Athena API key or service-issued DOCX Studio token
83
83
 
84
+ Without `ATHENA_DOCX_API_KEY` (and without `api_key=`), the SDK uses
85
+ `ATHENA_API_KEY`. When `ATHENA_API_URL` is set too (the bare Athena URL or one
86
+ ending in `/api/v0`), an opened document's calls carry a 15-minute token Athena
87
+ mints for that document alone (`POST /api/v0/assets/{asset_id}/studio-token`),
88
+ read-only for reads; `Document.create()` and its upload still send the key
89
+ (`docx/_asset_token.py`).
90
+
84
91
  ## License
85
92
 
86
93
  MIT
@@ -56,6 +56,13 @@ Required when connecting to DOCX Studio (set by the Athena backend in the `execu
56
56
  - `ATHENA_DOCX_BASE_URL` — DOCX Studio API base URL
57
57
  - `ATHENA_DOCX_API_KEY` — Athena API key or service-issued DOCX Studio token
58
58
 
59
+ Without `ATHENA_DOCX_API_KEY` (and without `api_key=`), the SDK uses
60
+ `ATHENA_API_KEY`. When `ATHENA_API_URL` is set too (the bare Athena URL or one
61
+ ending in `/api/v0`), an opened document's calls carry a 15-minute token Athena
62
+ mints for that document alone (`POST /api/v0/assets/{asset_id}/studio-token`),
63
+ read-only for reads; `Document.create()` and its upload still send the key
64
+ (`docx/_asset_token.py`).
65
+
59
66
  ## License
60
67
 
61
68
  MIT
@@ -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.43.0"
9
+ __version__ = "0.44.0"
10
10
 
11
11
  from docx.api import Document
12
12
  from docx._buffer import discard_all, flush_all
@@ -0,0 +1,354 @@
1
+ """Per-document DOCX Studio tokens, minted from Athena's public API.
2
+
3
+ A call on an existing Word document reaches DOCX Studio with, in this order:
4
+
5
+ 1. the ``api_key`` the caller passed;
6
+ 2. the studio's own key, ``ATHENA_DOCX_API_KEY``;
7
+ 3. when ``ATHENA_API_KEY`` and ``ATHENA_API_URL`` are both set, a token for that
8
+ document alone, minted by
9
+ ``POST {ATHENA_API_URL}/api/v0/assets/{asset_id}/studio-token`` with
10
+ ``X-API-KEY: $ATHENA_API_KEY`` and sent as ``Authorization: Bearer``. It names
11
+ the caller, the document, one access level and DOCX Studio, and lasts 15
12
+ minutes;
13
+ 4. otherwise ``ATHENA_API_KEY`` itself, as before.
14
+
15
+ Creating a document (``Document.create``, its upload) keeps the key of 1, 2 or
16
+ 4: a document that does not exist yet has no token. A token goes only to its
17
+ own document's routes.
18
+
19
+ A token is minted once per (agora, key, asset, access) for the whole process,
20
+ again 90 seconds before it expires, and once more when the studio answers 401.
21
+ A read mints a read-only token (``{"access": "view"}``: the version check, the
22
+ layout, page renders, the clean export), a write a read-write one
23
+ (``{"access": "edit"}``, which Athena grants only to a caller who may edit).
24
+ Every command batch is a write to DOCX Studio, whose ``/commands`` route needs
25
+ edit access even for a batch that only queries. ``ATHENA_API_URL`` ends in
26
+ ``/api/v0`` inside an Athena computer and is the bare agora URL in a session VM
27
+ or locally; both work.
28
+
29
+ Athena's refusals (400, 401, 403, 404, 422) raise at once, without a retry. A
30
+ deployment that signs no asset tokens answers 503: the client then uses the
31
+ studio key when one is set, and otherwise raises an error that names it.
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ import hashlib
37
+ import inspect
38
+ import os
39
+ import threading
40
+ import time
41
+ from collections import OrderedDict
42
+ from dataclasses import dataclass, field
43
+ from typing import Any
44
+ from urllib.parse import quote
45
+
46
+ import requests
47
+ from requests.adapters import HTTPAdapter
48
+ from urllib3.util.retry import Retry
49
+
50
+ from docx.errors import (
51
+ AuthenticationError,
52
+ DocxError,
53
+ DocxStudioTemporarilyUnavailable,
54
+ NotFoundError,
55
+ SessionError,
56
+ )
57
+
58
+ API_KEY_ENV = "ATHENA_API_KEY"
59
+ API_URL_ENV = "ATHENA_API_URL"
60
+ STUDIO_KEY_ENV = "ATHENA_DOCX_API_KEY"
61
+ #: The token audience DOCX Studio verifies.
62
+ AUDIENCE = "docx-studio"
63
+ TOKEN_PREFIX = "aat_"
64
+ PUBLIC_API_PREFIX = "/api/v0"
65
+
66
+ #: The access a token is minted for.
67
+ VIEW = "view"
68
+ EDIT = "edit"
69
+
70
+ #: Minted again this long before Athena's ``expires_at_ms``.
71
+ REFRESH_BEFORE_EXPIRY_SECONDS = 90.0
72
+ #: The longest a token is held, whatever the local clock says (Athena's lifetime).
73
+ MAX_LIFETIME_SECONDS = 15 * 60.0
74
+ #: The shortest, so a clock running ahead of Athena's cannot mint per call.
75
+ MIN_LIFETIME_SECONDS = 30.0
76
+ MINT_TIMEOUT_SECONDS = 30.0
77
+ _MAX_CACHED_TOKENS = 256
78
+ _LOCK_STRIPES = 16
79
+
80
+ _KIND_BY_AUDIENCE = {
81
+ AUDIENCE: "a Word document",
82
+ "pptx-studio": "a presentation",
83
+ "xlsx-studio": "a spreadsheet",
84
+ }
85
+
86
+
87
+ def public_api_base(url: str) -> str:
88
+ """The public API base for ``url``, whether or not it already ends in it."""
89
+ base = url.strip().rstrip("/")
90
+ if base.endswith(PUBLIC_API_PREFIX):
91
+ return base
92
+ return base + PUBLIC_API_PREFIX
93
+
94
+
95
+ @dataclass(frozen=True)
96
+ class StudioToken:
97
+ """One minted token and what Athena said about it."""
98
+
99
+ token: str = field(repr=False)
100
+ asset_id: str
101
+ audience: str
102
+ #: ``"r"`` (reads only) or ``"rw"``.
103
+ access_type: str
104
+ #: ``time.monotonic()`` after which the token is minted again.
105
+ refresh_at: float
106
+
107
+ def is_fresh(self) -> bool:
108
+ return time.monotonic() < self.refresh_at
109
+
110
+
111
+ class SigningUnavailable(Exception):
112
+ """Athena's 503: this deployment signs no asset tokens."""
113
+
114
+
115
+ class _TokenCache:
116
+ """Process-wide tokens by (agora, key, asset, access), least recently used first."""
117
+
118
+ def __init__(self, max_entries: int) -> None:
119
+ self._entries: OrderedDict[tuple[str, str, str, str], StudioToken] = OrderedDict()
120
+ self._max_entries = max_entries
121
+ self._lock = threading.Lock()
122
+ # Minting holds one stripe, so threads wanting the same token wait for
123
+ # one mint instead of each minting their own.
124
+ self._mint_locks = tuple(threading.Lock() for _ in range(_LOCK_STRIPES))
125
+
126
+ def get(self, key: tuple[str, str, str, str]) -> StudioToken | None:
127
+ with self._lock:
128
+ token = self._entries.get(key)
129
+ if token is not None:
130
+ self._entries.move_to_end(key)
131
+ return token
132
+
133
+ def put(self, key: tuple[str, str, str, str], token: StudioToken) -> None:
134
+ with self._lock:
135
+ self._entries[key] = token
136
+ self._entries.move_to_end(key)
137
+ while len(self._entries) > self._max_entries:
138
+ self._entries.popitem(last=False)
139
+
140
+ def discard(self, key: tuple[str, str, str, str], token: StudioToken) -> None:
141
+ """Forget ``token`` unless another thread already replaced it."""
142
+ with self._lock:
143
+ if self._entries.get(key) is token:
144
+ del self._entries[key]
145
+
146
+ def mint_lock(self, key: tuple[str, str, str, str]) -> threading.Lock:
147
+ return self._mint_locks[hash(key) % _LOCK_STRIPES]
148
+
149
+ def clear(self) -> None:
150
+ with self._lock:
151
+ self._entries.clear()
152
+
153
+
154
+ _CACHE = _TokenCache(_MAX_CACHED_TOKENS)
155
+
156
+ _SESSION: requests.Session | None = None
157
+ _SESSION_LOCK = threading.Lock()
158
+
159
+
160
+ def _mint_session() -> requests.Session:
161
+ """One session for every mint: connection retries, and status retries for
162
+ 429/502/504. A 503 is Athena's answer, not a transient fault, so it is not
163
+ retried; minting is safe to resend (each mint only signs a new token)."""
164
+ global _SESSION
165
+ with _SESSION_LOCK:
166
+ if _SESSION is None:
167
+ retry_kwargs: dict[str, Any] = {
168
+ "total": None,
169
+ "status": 2,
170
+ "connect": 2,
171
+ "read": 0,
172
+ "other": 0,
173
+ "backoff_factor": 0.5,
174
+ "status_forcelist": (429, 502, 504),
175
+ "allowed_methods": frozenset(["POST"]),
176
+ "raise_on_status": False,
177
+ "respect_retry_after_header": True,
178
+ }
179
+ if "backoff_max" in inspect.signature(Retry).parameters:
180
+ retry_kwargs["backoff_max"] = 5.0
181
+ session = requests.Session()
182
+ adapter = HTTPAdapter(max_retries=Retry(**retry_kwargs))
183
+ session.mount("http://", adapter)
184
+ session.mount("https://", adapter)
185
+ _SESSION = session
186
+ return _SESSION
187
+
188
+
189
+ def _detail(response: requests.Response) -> str:
190
+ """Athena's reason (``{"detail": ...}``), else a short excerpt of the body."""
191
+ try:
192
+ body = response.json()
193
+ except ValueError:
194
+ return " ".join((response.text or "").split())[:200]
195
+ detail = body.get("detail") if isinstance(body, dict) else None
196
+ if isinstance(detail, dict):
197
+ detail = detail.get("message")
198
+ if isinstance(detail, str):
199
+ return detail.strip()
200
+ return ""
201
+
202
+
203
+ def _lifetime_seconds(expires_at_ms: int) -> float:
204
+ """How long to hold a token, from Athena's expiry and the local clock."""
205
+ remaining = expires_at_ms / 1000.0 - time.time()
206
+ return min(max(remaining, MIN_LIFETIME_SECONDS), MAX_LIFETIME_SECONDS)
207
+
208
+
209
+ class TokenMinter:
210
+ """Mints the studio tokens one ``ATHENA_API_KEY`` holds, through the cache."""
211
+
212
+ def __init__(self, *, api_url: str, api_key: str) -> None:
213
+ self.base = public_api_base(api_url)
214
+ self._api_key = api_key
215
+ self._fingerprint = hashlib.sha256(api_key.encode("utf-8")).hexdigest()
216
+
217
+ @classmethod
218
+ def from_environment(cls) -> TokenMinter | None:
219
+ """The minter for ``ATHENA_API_KEY`` at ``ATHENA_API_URL``; None when either is unset."""
220
+ api_key = os.environ.get(API_KEY_ENV)
221
+ api_url = os.environ.get(API_URL_ENV)
222
+ if not api_key or not api_url or not api_url.strip():
223
+ return None
224
+ return cls(api_url=api_url, api_key=api_key)
225
+
226
+ def _key(self, *, asset_id: str, access: str) -> tuple[str, str, str, str]:
227
+ return (self.base, self._fingerprint, asset_id, access)
228
+
229
+ def token(self, *, asset_id: str, access: str) -> StudioToken:
230
+ """The token for ``asset_id`` at ``access``: the cached one while fresh,
231
+ else a new one. Raises when Athena refuses it, when it is for another
232
+ studio, and :class:`SigningUnavailable` on a 503."""
233
+ key = self._key(asset_id=asset_id, access=access)
234
+ token = _CACHE.get(key)
235
+ if token is None or not token.is_fresh():
236
+ with _CACHE.mint_lock(key):
237
+ token = _CACHE.get(key)
238
+ if token is None or not token.is_fresh():
239
+ token = self._mint(asset_id=asset_id, access=access)
240
+ _CACHE.put(key, token)
241
+ if token.audience != AUDIENCE:
242
+ raise DocxError(
243
+ f"Asset {asset_id} is not a Word document: Athena edits it through "
244
+ f"{token.audience}. Open it with the Athena SDK for "
245
+ f"{_KIND_BY_AUDIENCE.get(token.audience, 'that kind of asset')}."
246
+ )
247
+ return token
248
+
249
+ def invalidate(self, *, token: StudioToken, access: str) -> None:
250
+ """Forget ``token`` (the studio answered 401 to it), so the next call mints."""
251
+ _CACHE.discard(self._key(asset_id=token.asset_id, access=access), token)
252
+
253
+ def _mint(self, *, asset_id: str, access: str) -> StudioToken:
254
+ url = f"{self.base}/assets/{quote(asset_id, safe='')}/studio-token"
255
+ try:
256
+ response = _mint_session().post(
257
+ url,
258
+ json={"access": access},
259
+ headers={"X-API-KEY": self._api_key, "Accept": "application/json"},
260
+ timeout=MINT_TIMEOUT_SECONDS,
261
+ )
262
+ except requests.RequestException as exc:
263
+ raise SessionError(
264
+ f"Unable to reach Athena at {url} to mint a DOCX Studio token for "
265
+ f"{asset_id}: {exc}"
266
+ ) from exc
267
+ try:
268
+ return self._read(response, asset_id=asset_id)
269
+ finally:
270
+ response.close()
271
+
272
+ @staticmethod
273
+ def _read(response: requests.Response, *, asset_id: str) -> StudioToken:
274
+ status = response.status_code
275
+ if status == 503:
276
+ raise SigningUnavailable(_detail(response) or "This deployment signs no asset tokens.")
277
+ detail = _detail(response)
278
+ reason = f"(HTTP {status}{': ' + detail if detail else ''})"
279
+ if status == 401:
280
+ raise AuthenticationError(
281
+ f"Athena refused ATHENA_API_KEY when asked for a DOCX Studio token for "
282
+ f"{asset_id} {reason}. The key is invalid, expired or revoked; retrying "
283
+ "with it will not help."
284
+ )
285
+ if status == 403:
286
+ raise AuthenticationError(f"Athena refused a DOCX Studio token for {asset_id} {reason}.")
287
+ if status == 404:
288
+ raise NotFoundError(
289
+ f"Athena refused a DOCX Studio token for {asset_id} {reason}: the document "
290
+ "does not exist, or this key may not read it.",
291
+ payload={"status": 404, "body": detail},
292
+ )
293
+ if status in (400, 422):
294
+ raise DocxError(f"Athena refused a DOCX Studio token for {asset_id} {reason}.")
295
+ if not response.ok:
296
+ raise DocxStudioTemporarilyUnavailable(
297
+ f"Athena could not mint a DOCX Studio token for {asset_id} {reason}.",
298
+ http_status=status,
299
+ )
300
+ try:
301
+ body = response.json()
302
+ except ValueError:
303
+ body = None
304
+ if not isinstance(body, dict):
305
+ body = {}
306
+ token = body.get("token")
307
+ audience = body.get("audience")
308
+ access_type = body.get("access_type")
309
+ expires_at_ms = body.get("expires_at_ms")
310
+ if (
311
+ not isinstance(token, str)
312
+ or not token.startswith(TOKEN_PREFIX)
313
+ or not isinstance(audience, str)
314
+ or access_type not in ("r", "rw")
315
+ or not isinstance(expires_at_ms, int)
316
+ or isinstance(expires_at_ms, bool)
317
+ or body.get("asset_id") != asset_id
318
+ ):
319
+ raise SessionError(
320
+ f"Athena's studio token answer for {asset_id} is not one this SDK can use "
321
+ "(expected token, audience, access_type, expires_at_ms and this asset id)."
322
+ )
323
+ return StudioToken(
324
+ token=token,
325
+ asset_id=asset_id,
326
+ audience=audience,
327
+ access_type=access_type,
328
+ refresh_at=time.monotonic()
329
+ + max(
330
+ _lifetime_seconds(expires_at_ms) - REFRESH_BEFORE_EXPIRY_SECONDS,
331
+ MIN_LIFETIME_SECONDS,
332
+ ),
333
+ )
334
+
335
+
336
+ def studio_credentials(*, api_key: str | None) -> tuple[str | None, TokenMinter | None]:
337
+ """What a client authenticates with: ``(key, minter)``.
338
+
339
+ ``key`` serves every route the minter does not: ``api_key`` when the caller
340
+ passed one, else ``ATHENA_DOCX_API_KEY``, else ``ATHENA_API_KEY``. ``minter``
341
+ mints per-document tokens when neither of the first two is set and
342
+ ``ATHENA_API_KEY`` and ``ATHENA_API_URL`` are.
343
+ """
344
+ if api_key:
345
+ return api_key, None
346
+ studio_key = os.environ.get(STUDIO_KEY_ENV)
347
+ if studio_key:
348
+ return studio_key, None
349
+ return os.environ.get(API_KEY_ENV), TokenMinter.from_environment()
350
+
351
+
352
+ def _clear_cache() -> None:
353
+ """Forget every cached token (tests)."""
354
+ _CACHE.clear()
@@ -36,12 +36,21 @@ import os
36
36
  import re
37
37
  import urllib.parse
38
38
  import warnings
39
- from typing import Any, TypedDict
39
+ from collections.abc import Callable
40
+ from typing import Any, TypedDict, TypeVar
40
41
 
41
42
  import requests
42
43
  from requests.adapters import HTTPAdapter
43
44
  from urllib3.util.retry import Retry
44
45
 
46
+ from docx._asset_token import (
47
+ EDIT,
48
+ STUDIO_KEY_ENV,
49
+ VIEW,
50
+ SigningUnavailable,
51
+ StudioToken,
52
+ TokenMinter,
53
+ )
45
54
  from docx._batching import call_abandoned, note_progress
46
55
  from docx._buffer import CommandBuffer, keep_reported_message
47
56
  from docx._tracked import TRACKED_AUTHORING_CAPABILITY, TRACKED_MINIMAL_PARAGRAPH_TEXT_CAPABILITY
@@ -211,6 +220,8 @@ VERSION_CHECK_TIMEOUT_SECONDS: float = 10.0
211
220
  # (``CommandsRequestSchema`` in ``apps/api/src/commands/types.ts``).
212
221
  MAX_BATCH_COMMANDS: int = 500
213
222
 
223
+ _T = TypeVar("_T")
224
+
214
225
 
215
226
  def _minted_ids(results: list[Any]) -> dict[str, str]:
216
227
  """The client → real ids that the Creates among a batch's ``results`` echo."""
@@ -720,6 +731,16 @@ def _parse_retry_after(value: str | None) -> float | None:
720
731
  return None
721
732
 
722
733
 
734
+ def _rejected(status_code: int, err_body: str) -> AuthenticationError:
735
+ """docx-studio's 401 or 403, carrying its status (a 401 to a minted token
736
+ is minted again once, :meth:`HttpClient._with_token`)."""
737
+ error = AuthenticationError(
738
+ f"docx-studio rejected the request (HTTP {status_code}): {err_body}",
739
+ )
740
+ error.http_status = status_code # type: ignore[attr-defined]
741
+ return error
742
+
743
+
723
744
  def _delegation_headers() -> dict[str, str]:
724
745
  """The user a service-key caller acts for, as ``X-Custom-Attributions``.
725
746
 
@@ -934,9 +955,7 @@ def _http_post_json(
934
955
 
935
956
  err_body: str = resp.text
936
957
  if resp.status_code in (401, 403):
937
- raise AuthenticationError(
938
- f"docx-studio rejected the request (HTTP {resp.status_code}): {err_body}",
939
- )
958
+ raise _rejected(resp.status_code, err_body)
940
959
  if resp.status_code == 404:
941
960
  raise NotFoundError(
942
961
  f"docx-studio returned HTTP 404: {err_body}",
@@ -1009,9 +1028,7 @@ def _http_get(
1009
1028
  return resp
1010
1029
  err_body: str = resp.text
1011
1030
  if resp.status_code in (401, 403):
1012
- raise AuthenticationError(
1013
- f"docx-studio rejected the request (HTTP {resp.status_code}): {err_body}",
1014
- )
1031
+ raise _rejected(resp.status_code, err_body)
1015
1032
  if resp.status_code == 404:
1016
1033
  try:
1017
1034
  parsed: object = resp.json()
@@ -1084,13 +1101,82 @@ class HttpClient:
1084
1101
  base_url: str,
1085
1102
  api_key: str,
1086
1103
  session: requests.Session | None = None,
1104
+ asset_tokens: TokenMinter | None = None,
1105
+ token_asset_id: str | None = None,
1087
1106
  ) -> None:
1107
+ """``api_key`` authenticates every call, except that with ``asset_tokens``
1108
+ the calls on ``token_asset_id`` carry a token minted for that document
1109
+ alone (``docx._asset_token``): read-only for a read, read-write for a
1110
+ command batch."""
1088
1111
  self._base_url: str = base_url.rstrip("/")
1089
1112
  self._api_key: str = api_key
1090
1113
  self._session: requests.Session = session or _build_retry_session()
1114
+ self._asset_tokens: TokenMinter | None = asset_tokens
1115
+ self._token_asset_id: str | None = token_asset_id
1091
1116
  # Header/footer paragraph ids, for the ``block not found`` hint.
1092
1117
  self.header_footer_block_ids: set[str] = set()
1093
1118
 
1119
+ def _bearer(
1120
+ self, *, asset_id: str, access: str
1121
+ ) -> tuple[str, tuple[StudioToken, str] | None]:
1122
+ """The credential for a call on ``asset_id``: its token, else the key."""
1123
+ minter = self._asset_tokens
1124
+ if minter is None or asset_id != self._token_asset_id:
1125
+ return self._api_key, None
1126
+ try:
1127
+ token = minter.token(asset_id=asset_id, access=access)
1128
+ except SigningUnavailable as exc:
1129
+ return self._without_asset_tokens(asset_id=asset_id, reason=str(exc)), None
1130
+ return token.token, (token, access)
1131
+
1132
+ def _without_asset_tokens(self, *, asset_id: str, reason: str) -> str:
1133
+ """Athena signs no asset tokens here (its 503): use the studio key from
1134
+ now on when one is set, else raise an error that says what to set."""
1135
+ studio_key = os.environ.get(STUDIO_KEY_ENV)
1136
+ if not studio_key:
1137
+ raise AuthenticationError(
1138
+ f"Athena could not mint a DOCX Studio token for {asset_id}: "
1139
+ f"{reason.rstrip('.')} (HTTP 503). Set {STUDIO_KEY_ENV} (or pass api_key=) "
1140
+ "to an Athena API key DOCX Studio accepts."
1141
+ )
1142
+ self._asset_tokens = None
1143
+ self._api_key = studio_key
1144
+ return studio_key
1145
+
1146
+ def _with_token(
1147
+ self, *, asset_id: str, access: str, send: Callable[[str], _T]
1148
+ ) -> _T:
1149
+ """``send(bearer)`` with the call's credential. A 401 to a minted token
1150
+ is sent once more with a newly minted one: docx-studio refused it before
1151
+ acting on it, so nothing is applied twice."""
1152
+ bearer, grant = self._bearer(asset_id=asset_id, access=access)
1153
+ try:
1154
+ return send(bearer)
1155
+ except AuthenticationError as exc:
1156
+ minter = self._asset_tokens
1157
+ if grant is None or minter is None or getattr(exc, "http_status", None) != 401:
1158
+ raise
1159
+ refused, granted_access = grant
1160
+ minter.invalidate(token=refused, access=granted_access)
1161
+ try:
1162
+ fresh = minter.token(asset_id=asset_id, access=granted_access)
1163
+ except SigningUnavailable:
1164
+ raise exc from None
1165
+ if fresh.token == refused.token:
1166
+ raise
1167
+ try:
1168
+ return send(fresh.token)
1169
+ except AuthenticationError as again:
1170
+ if getattr(again, "http_status", None) != 401:
1171
+ raise
1172
+ raise AuthenticationError(
1173
+ f"docx-studio refused the token Athena minted for {asset_id}, and the one "
1174
+ f"minted again after that refusal too ({again}). DOCX Studio at "
1175
+ f"{self._base_url} may belong to another Athena deployment than "
1176
+ "ATHENA_API_URL; check both URLs, or set ATHENA_DOCX_API_KEY to an Athena "
1177
+ "API key the studio accepts."
1178
+ ) from again
1179
+
1094
1180
  def close(self) -> None:
1095
1181
  """Release the pooled HTTP connections held by ``requests.Session``.
1096
1182
 
@@ -1285,13 +1371,18 @@ class HttpClient:
1285
1371
  body["changeMode"] = change_mode
1286
1372
  if user is not None:
1287
1373
  body["user"] = user
1288
- resp: dict = _http_post_json(
1289
- session=self._session,
1290
- url=url,
1291
- api_key=self._api_key,
1292
- body=body,
1293
- timeout=timeout_for_commands(commands=commands),
1294
- header_footer_block_ids=self.header_footer_block_ids,
1374
+ # docx-studio's /commands needs edit access, for a batch of queries too.
1375
+ resp: dict = self._with_token(
1376
+ asset_id=asset_id,
1377
+ access=EDIT,
1378
+ send=lambda bearer: _http_post_json(
1379
+ session=self._session,
1380
+ url=url,
1381
+ api_key=bearer,
1382
+ body=body,
1383
+ timeout=timeout_for_commands(commands=commands),
1384
+ header_footer_block_ids=self.header_footer_block_ids,
1385
+ ),
1295
1386
  )
1296
1387
  applied = resp.get("applied")
1297
1388
  # Commands the engine doesn't implement yet are reported in
@@ -1353,10 +1444,9 @@ class HttpClient:
1353
1444
  kind): a held read is then read again, so the read's own request
1354
1445
  reports what went wrong."""
1355
1446
  try:
1356
- resp = _http_get(
1357
- session=self._session,
1358
- url=self._doc_url(asset_id, "version"),
1359
- api_key=self._api_key,
1447
+ resp = self._get(
1448
+ asset_id,
1449
+ "version",
1360
1450
  params={"since": token},
1361
1451
  timeout=VERSION_CHECK_TIMEOUT_SECONDS,
1362
1452
  )
@@ -1369,14 +1459,31 @@ class HttpClient:
1369
1459
  def _doc_url(self, asset_id: str, path: str) -> str:
1370
1460
  return f"{self._base_url}/docs/{urllib.parse.quote(asset_id, safe='')}/{path}"
1371
1461
 
1462
+ def _get(
1463
+ self,
1464
+ asset_id: str,
1465
+ path: str,
1466
+ *,
1467
+ params: dict[str, str] | None = None,
1468
+ timeout: float = DEFAULT_OP_TIMEOUT_SECONDS,
1469
+ ) -> requests.Response:
1470
+ """``GET /docs/:id/<path>``, one of docx-studio's read-only routes (VIEW gate)."""
1471
+ return self._with_token(
1472
+ asset_id=asset_id,
1473
+ access=VIEW,
1474
+ send=lambda bearer: _http_get(
1475
+ session=self._session,
1476
+ url=self._doc_url(asset_id, path),
1477
+ api_key=bearer,
1478
+ params=params,
1479
+ timeout=timeout,
1480
+ ),
1481
+ )
1482
+
1372
1483
  def get_layout(self, asset_id: str) -> dict[str, Any]:
1373
1484
  """``GET /docs/:id/layout.json`` — the page count and page sizes as
1374
1485
  DOCX Studio's layout engine paginates the document (VIEW gate)."""
1375
- resp = _http_get(
1376
- session=self._session,
1377
- url=self._doc_url(asset_id, "layout.json"),
1378
- api_key=self._api_key,
1379
- )
1486
+ resp = self._get(asset_id, "layout.json")
1380
1487
  try:
1381
1488
  data: object = resp.json()
1382
1489
  except ValueError as e:
@@ -1388,12 +1495,7 @@ class HttpClient:
1388
1495
  def render_page_png(self, asset_id: str, page_number: int, *, scale: float) -> RenderedPagePng:
1389
1496
  """``GET /docs/:id/pages/:n.png?scale=`` — one page (1-indexed) as PNG,
1390
1497
  from the same layout engine the editor paginates with (VIEW gate)."""
1391
- resp = _http_get(
1392
- session=self._session,
1393
- url=self._doc_url(asset_id, f"pages/{page_number}.png"),
1394
- api_key=self._api_key,
1395
- params={"scale": f"{scale:g}"},
1396
- )
1498
+ resp = self._get(asset_id, f"pages/{page_number}.png", params={"scale": f"{scale:g}"})
1397
1499
  page_count_header = resp.headers.get("X-Page-Count", "")
1398
1500
  page_count = int(page_count_header) if page_count_header.isdigit() else 0
1399
1501
  return RenderedPagePng(png=resp.content, page_count=page_count)
@@ -1405,10 +1507,9 @@ class HttpClient:
1405
1507
  the plain export without ``X-Docx-Clean-Copy``: that is refused here,
1406
1508
  so an unclean copy never reaches the caller.
1407
1509
  """
1408
- resp = _http_get(
1409
- session=self._session,
1410
- url=self._doc_url(asset_id, "export.docx"),
1411
- api_key=self._api_key,
1510
+ resp = self._get(
1511
+ asset_id,
1512
+ "export.docx",
1412
1513
  params={"clean": "1", **({"clearTitle": "1"} if clear_title else {})},
1413
1514
  timeout=LONG_RUNNING_COMMAND_TIMEOUT_SECONDS,
1414
1515
  )