athena-python-docx 0.9.0__tar.gz → 0.10.1__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 (328) hide show
  1. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/CLAUDE.md +54 -5
  2. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/PKG-INFO +1 -1
  3. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/__init__.py +1 -1
  4. athena_python_docx-0.10.1/docx/_http.py +379 -0
  5. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/_http_doc.py +6 -0
  6. athena_python_docx-0.10.1/docx/_table_styles.py +92 -0
  7. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/api.py +56 -8
  8. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/commands.py +28 -0
  9. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/document.py +104 -54
  10. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/errors.py +37 -4
  11. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/table.py +94 -49
  12. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/pyproject.toml +1 -1
  13. athena_python_docx-0.10.1/scripts/smoke_test_block_not_found.py +175 -0
  14. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/fake_session.py +68 -0
  15. athena_python_docx-0.10.1/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +12 -0
  16. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/103_cell_tables_enumeration.json +0 -1
  17. athena_python_docx-0.10.1/tests/fidelity/op_snapshots/18_table_cell_text.json +9 -0
  18. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -3
  19. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +1 -4
  20. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/23_nested_table.json +1 -4
  21. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/24_table_add_row_column.json +1 -3
  22. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -2
  23. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/35_full_report.json +9 -33
  24. athena_python_docx-0.10.1/tests/fidelity/op_snapshots/40_large_table_10x10.json +103 -0
  25. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +1 -5
  26. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/49_resume_layout.json +3 -12
  27. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/51_nested_tables_deep.json +1 -4
  28. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/56_everything_in_one.json +12 -48
  29. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +1 -4
  30. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -1
  31. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +793 -1993
  32. athena_python_docx-0.10.1/tests/fidelity/op_snapshots/68_invoice.json +62 -0
  33. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/69_newsletter.json +0 -1
  34. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/72_legal_contract.json +6 -24
  35. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +20 -80
  36. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +9 -27
  37. athena_python_docx-0.10.1/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +24 -0
  38. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -1
  39. athena_python_docx-0.10.1/tests/fidelity/op_snapshots/91_many_small_tables.json +153 -0
  40. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex01_five_levels_deep_tables.json +1 -4
  41. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +3 -12
  42. athena_python_docx-0.10.1/tests/fidelity/op_snapshots/ex04_50x50_table.json +53 -0
  43. athena_python_docx-0.10.1/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +5 -0
  44. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/mega10_api_documentation.json → athena_python_docx-0.10.1/tests/fidelity/op_snapshots/ex10_complex_bom.json +126 -113
  45. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +80 -320
  46. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -1
  47. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/rw10_colored_grid_table.json → athena_python_docx-0.10.1/tests/fidelity/op_snapshots/ex14_styled_report_table.json +36 -69
  48. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +84 -336
  49. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/mega01_book_chapter.json +13 -52
  50. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/mega02_research_proposal.json +18 -72
  51. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/mega03_financial_statement.json +21 -98
  52. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/mega05_user_manual.json +20 -88
  53. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/ex14_styled_report_table.json → athena_python_docx-0.10.1/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +44 -75
  54. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/mega09_signed_contract.json +8 -36
  55. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json → athena_python_docx-0.10.1/tests/fidelity/op_snapshots/mega10_api_documentation.json +104 -97
  56. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +12 -36
  57. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +12 -36
  58. athena_python_docx-0.10.1/tests/fidelity/op_snapshots/rw08_table_merged_header.json +13 -0
  59. athena_python_docx-0.10.1/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +148 -0
  60. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw14_nested_cell_table.json +1 -4
  61. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/parity/reports/gap_report.json +1 -1
  62. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/parity/snapshots/athena_latest.json +0 -50
  63. athena_python_docx-0.10.1/tests/test_document_asset_id_property.py +52 -0
  64. athena_python_docx-0.10.1/tests/test_document_create_from_template.py +312 -0
  65. athena_python_docx-0.10.1/tests/test_document_factory_validation.py +52 -0
  66. athena_python_docx-0.10.1/tests/test_e2e_partial_failure_cascade.py +278 -0
  67. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_parity_misc.py +4 -2
  68. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_document_audit.py +55 -61
  69. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_style_acceptance.py +5 -2
  70. athena_python_docx-0.10.1/tests/test_table_set_cell_perf.py +127 -0
  71. athena_python_docx-0.10.1/tests/test_table_style_id_resolution.py +173 -0
  72. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_wire_contract.py +9 -0
  73. athena_python_docx-0.9.0/docx/_http.py +0 -189
  74. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -24
  75. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -27
  76. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -403
  77. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/68_invoice.json +0 -114
  78. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -54
  79. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -453
  80. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -203
  81. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -8
  82. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -596
  83. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -35
  84. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/.gitignore +0 -0
  85. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/README.md +0 -0
  86. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/_batching.py +0 -0
  87. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/_buffer.py +0 -0
  88. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/_image_utils.py +0 -0
  89. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/_ptc.py +0 -0
  90. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/client.py +0 -0
  91. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/comments.py +0 -0
  92. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/enum/__init__.py +0 -0
  93. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/enum/section.py +0 -0
  94. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/enum/style.py +0 -0
  95. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/enum/table.py +0 -0
  96. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/enum/text.py +0 -0
  97. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/exceptions.py +0 -0
  98. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/opc/__init__.py +0 -0
  99. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/opc/coreprops.py +0 -0
  100. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/oxml/__init__.py +0 -0
  101. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/revisions.py +0 -0
  102. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/section.py +0 -0
  103. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/settings.py +0 -0
  104. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/shape.py +0 -0
  105. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/shared.py +0 -0
  106. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/styles/__init__.py +0 -0
  107. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/styles/style.py +0 -0
  108. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/styles/styles.py +0 -0
  109. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/text/__init__.py +0 -0
  110. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/text/font.py +0 -0
  111. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/text/hyperlink.py +0 -0
  112. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/text/pagebreak.py +0 -0
  113. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/text/paragraph.py +0 -0
  114. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/text/parfmt.py +0 -0
  115. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/text/run.py +0 -0
  116. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/text/tabstops.py +0 -0
  117. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/docx/typing.py +0 -0
  118. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/scripts/publish.sh +0 -0
  119. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/scripts/release.sh +0 -0
  120. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/scripts/round_trip_smoke.py +0 -0
  121. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/__init__.py +0 -0
  122. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/conftest.py +0 -0
  123. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/METHODOLOGY.md +0 -0
  124. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/README.md +0 -0
  125. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/__init__.py +0 -0
  126. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/ab_probe_cases.py +0 -0
  127. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/ab_probe_runner.py +0 -0
  128. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/auto_gen_cases.py +0 -0
  129. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/binary_round_trip.py +0 -0
  130. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/cases.py +0 -0
  131. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/complex_cases.py +0 -0
  132. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/coverage_report.py +0 -0
  133. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/extract.py +0 -0
  134. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/extreme_cases.py +0 -0
  135. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/local_runner.py +0 -0
  136. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/mega_cases.py +0 -0
  137. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshot.py +0 -0
  138. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
  139. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
  140. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
  141. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
  142. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
  143. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
  144. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
  145. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
  146. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
  147. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
  148. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
  149. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
  150. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
  151. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
  152. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
  153. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
  154. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
  155. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
  156. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
  157. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
  158. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
  159. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
  160. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
  161. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
  162. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
  163. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
  164. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
  165. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
  166. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
  167. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
  168. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
  169. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
  170. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
  171. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
  172. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
  173. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
  174. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
  175. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
  176. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
  177. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
  178. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
  179. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
  180. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
  181. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
  182. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
  183. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
  184. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
  185. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
  186. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
  187. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
  188. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
  189. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
  190. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
  191. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
  192. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
  193. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
  194. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
  195. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
  196. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
  197. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
  198. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
  199. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
  200. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
  201. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
  202. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
  203. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
  204. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
  205. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
  206. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
  207. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
  208. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
  209. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
  210. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
  211. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
  212. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
  213. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
  214. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
  215. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
  216. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
  217. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
  218. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
  219. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
  220. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
  221. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
  222. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
  223. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
  224. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
  225. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
  226. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
  227. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
  228. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
  229. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
  230. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
  231. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
  232. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
  233. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
  234. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
  235. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
  236. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
  237. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
  238. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
  239. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
  240. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
  241. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
  242. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
  243. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
  244. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/ours_spec.json +0 -0
  245. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/parity_crawl.py +0 -0
  246. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/parity_diff.json +0 -0
  247. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/real_world_cases.py +0 -0
  248. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/round_trip_tests.py +0 -0
  249. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/runner.py +0 -0
  250. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/stock_spec.json +0 -0
  251. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/fidelity/test_e2e_against_staging.py +0 -0
  252. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/parity/README.md +0 -0
  253. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/parity/__init__.py +0 -0
  254. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/parity/baseline_gaps.json +0 -0
  255. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/parity/compare.py +0 -0
  256. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/parity/intentional_deviations.json +0 -0
  257. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/parity/introspect.py +0 -0
  258. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
  259. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/parity/run_parity.py +0 -0
  260. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
  261. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/parity/test_parity_gap.py +0 -0
  262. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_batching_perf.py +0 -0
  263. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_block_not_found_error.py +0 -0
  264. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_buffer.py +0 -0
  265. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_cell_text_plain_fastpath.py +0 -0
  266. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_collapsed_range_format.py +0 -0
  267. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_command_dataclasses.py +0 -0
  268. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_commands.py +0 -0
  269. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_comments.py +0 -0
  270. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_document_create.py +0 -0
  271. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_http_transport.py +0 -0
  272. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_hyperlink_coalescing.py +0 -0
  273. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_insert_deferred.py +0 -0
  274. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_iter_inner_content.py +0 -0
  275. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_list_styles.py +0 -0
  276. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_merged_cells.py +0 -0
  277. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_oxml_shim.py +0 -0
  278. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_paragraph_text_len_cache.py +0 -0
  279. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_parity_round2.py +0 -0
  280. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_partial_failure_cascade.py +0 -0
  281. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_phase_a_behavior.py +0 -0
  282. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_phase_b_headers_footers.py +0 -0
  283. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_phase_c_tables.py +0 -0
  284. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_pr19766_review_fixes.py +0 -0
  285. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_ptc.py +0 -0
  286. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_python_docx_api_parity.py +0 -0
  287. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_revisions.py +0 -0
  288. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_add_paragraph_style.py +0 -0
  289. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_add_picture.py +0 -0
  290. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_add_run.py +0 -0
  291. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
  292. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_comments_add_comment.py +0 -0
  293. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_comments_get.py +0 -0
  294. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_document_element.py +0 -0
  295. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_enum_section.py +0 -0
  296. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_font_audit.py +0 -0
  297. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_header_footer.py +0 -0
  298. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_hyperlink.py +0 -0
  299. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_inline_shape.py +0 -0
  300. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
  301. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_misc.py +0 -0
  302. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_paragraph_strict.py +0 -0
  303. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_paragraph_style.py +0 -0
  304. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
  305. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_parfmt.py +0 -0
  306. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_row_col_cell.py +0 -0
  307. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_run_add_break.py +0 -0
  308. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_run_bool_setters.py +0 -0
  309. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_run_style.py +0 -0
  310. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_run_style_strict.py +0 -0
  311. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_run_text.py +0 -0
  312. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_run_underline.py +0 -0
  313. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_section_audit.py +0 -0
  314. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_section_dimensions.py +0 -0
  315. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_section_onoff.py +0 -0
  316. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_settings.py +0 -0
  317. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_shared_audit.py +0 -0
  318. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_style.py +0 -0
  319. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_styles.py +0 -0
  320. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_table_audit.py +0 -0
  321. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_table_cell.py +0 -0
  322. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_table_dimensions.py +0 -0
  323. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_table_layout.py +0 -0
  324. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_smoke_integration.py +0 -0
  325. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_style_font.py +0 -0
  326. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_style_setters_contract.py +0 -0
  327. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/tests/test_zod_wire_contract.py +0 -0
  328. {athena_python_docx-0.9.0 → athena_python_docx-0.10.1}/uv.lock +0 -0
@@ -81,20 +81,69 @@ remaining work* for the exact wire-op shape needed to unblock each.
81
81
  These differ from stock python-docx because the SDK is asset-backed,
82
82
  not file-backed. Each is documented in the relevant docstring.
83
83
 
84
- - **`Document.create(name=, base_url=, api_key=, parent_folder_id=, workspace_id=)`**
84
+ - **Built-in table style friendly names resolve to OOXML styleIds.**
85
+ Word's ``styles.xml`` ships built-in table styles whose styleIds
86
+ (``LightGrid-Accent1``, ``MediumShading1-Accent2``, …) differ from
87
+ their display names (``"Light Grid Accent 1"``, …). Stock
88
+ python-docx resolves the friendly form against the loaded
89
+ document's styles collection at write time. We can't read
90
+ ``styles.xml`` over HTTP, so ``Document.add_table(style=...)`` /
91
+ ``Table.style`` setter consult an in-process map
92
+ (``docx._table_styles.BUILTIN_TABLE_STYLE_IDS``) covering every
93
+ Word 2007 and Word 2013+ built-in table style (204 entries) before
94
+ the value reaches ``<w:tblStyle w:val="..."/>``. Without this
95
+ mapping Word can't resolve the friendly name in ``styles.xml``
96
+ and silently falls back to ``Normal Table`` (no borders, no
97
+ header fill, no banding). Custom user-defined style names pass
98
+ through unchanged. Whether Word actually renders a Word 2013+
99
+ style after resolution also depends on SuperDoc's exported
100
+ ``styles.xml`` containing the matching ``<w:style w:type="table">``
101
+ definition — at SuperDoc 1.8.1 the exported ``styles.xml`` only
102
+ carries the Word 2007 set, so Word 2013+ styleIds resolve in the
103
+ ``<w:tblStyle>`` element but still fall back to ``Normal Table``
104
+ until SuperDoc upstream ships an expanded ``styles.xml``.
105
+
106
+ - **`Document.create(name=, base_url=, api_key=, parent_folder_id=, workspace_id=, docx=, agora_base_url=)`**
85
107
  classmethod. Stock python-docx returns a blank in-memory document
86
108
  for `Document(None)`; we can't fabricate a SuperDocument asset
87
109
  client-side, so net-new asset creation is a separate factory that
88
110
  hits `POST {base_url}/docs/empty`. The constructor positional-arg
89
111
  shape (`Document(asset_id)`) is preserved for parity.
112
+ - **`docx=path`** mirrors the upstream `Document(docx="template.docx")`
113
+ constructor kwarg: when provided, the local `.docx` is uploaded to
114
+ agora's `/api/super-docs/create-from-upload` and used as the seed
115
+ for the new SuperDocument. The bytes are mirrored to S3 server-side
116
+ so subsequent template-asset reuse is fast. Without `docx=`, the
117
+ default Athena template is applied as today.
118
+ - **`agora_base_url=`** is a transport-detail kwarg only required when
119
+ the SDK can't derive the agora host from `base_url` (or from
120
+ `$ATHENA_AGORA_BASE_URL`). The default substitution rule replaces
121
+ `docx-studio` with `agora` in the docx-studio host; explicit
122
+ `agora_base_url` is required for local development or unusual
123
+ deployment topologies.
124
+
125
+ - **`Document.asset_id: str`** read-only property — the Athena asset
126
+ id this Document is bound to (`asset_<uuid>`). Agent-callable so
127
+ the typical pattern `doc = Document.create(...); print(doc.asset_id)`
128
+ works without reaching into `doc._session._asset_id`. Stock
129
+ python-docx has no analogue (it operates on local files), so this
130
+ is an Athena-only addition. Mirrors the parallel public accessors
131
+ on the other studios: `Workbook.workbook_id` (xlsx) and
132
+ `Presentation.deck_id` (pptx).
90
133
 
91
- - **`Document.save(path_or_stream=None)`** — argument is optional.
92
- Stock python-docx requires it and `TypeError`s on no-arg, but in an
134
+ - **`Document.save(path_or_stream=None)`** — argument is optional and,
135
+ when supplied, raises
136
+ :class:`docx.errors.LocalSaveTargetNotSupportedError`. Stock
137
+ python-docx requires it and `TypeError`s on no-arg, but in an
93
138
  asset-backed SDK there is no local file to write — writes are always
94
139
  in-place against the Y.Doc. Forcing the upstream signature broke
95
140
  every agent invocation that reflexively called `doc.save()` (a
96
- near-universal Python pattern), so we accept it for parity-friendly
97
- call sites and ignore the value at runtime.
141
+ near-universal Python pattern), so the no-arg form is supported.
142
+ When a path or stream IS supplied, the SDK cannot fulfill the
143
+ implied "write bytes to this target" contract, so the call raises
144
+ loudly rather than silently flushing — agents must use Olympus's
145
+ Export DOCX (which goes through the SuperDoc session that already
146
+ has the bytes) instead of expecting a local file.
98
147
 
99
148
  - **`Document.track_revisions: bool`** — when `True`, all subsequent
100
149
  mutations are recorded as tracked revisions instead of direct edits.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: athena-python-docx
3
- Version: 0.9.0
3
+ Version: 0.10.1
4
4
  Summary: Drop-in replacement for python-docx that connects to Athena's Superdoc/Keryx collaborative document stack
5
5
  Project-URL: Homepage, https://athenaintelligence.ai
6
6
  Author-email: Athena Intelligence <engineering@athenaintelligence.ai>
@@ -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.9.0"
9
+ __version__ = "0.10.1"
10
10
 
11
11
  from docx.api import Document
12
12
  from docx._buffer import flush_all
@@ -0,0 +1,379 @@
1
+ """HTTP bootstrap for Document.create().
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.
8
+
9
+ We use ``urllib`` from stdlib to avoid pulling ``httpx`` / ``requests``
10
+ into the runtime — this code runs in Daytona sandboxes where every MB
11
+ matters, and the existing SDK already keeps its dep tree tight.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import json
17
+ import os
18
+ import urllib.error
19
+ import urllib.request
20
+ from typing import TypedDict
21
+
22
+ from docx.errors import (
23
+ AuthenticationError,
24
+ DocxError,
25
+ SessionError,
26
+ )
27
+
28
+
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
+ class CreateAssetResult(TypedDict):
43
+ """Parsed response from ``POST /docs/empty``."""
44
+
45
+ asset_id: str
46
+ name: str
47
+ workspace_id: str
48
+ collab: _CollabBundle
49
+
50
+
51
+ _BASE_URL_ENV = "ATHENA_DOCX_BASE_URL"
52
+ _API_KEY_ENV = "ATHENA_DOCX_API_KEY" # noqa: S105
53
+ _AGORA_BASE_URL_ENV = "ATHENA_AGORA_BASE_URL"
54
+
55
+
56
+ def _derive_agora_base_url(docx_base_url: str) -> str | None:
57
+ """Best-effort derive an Agora base URL from a docx-studio base URL.
58
+
59
+ The Athena hosts follow a parallel ``<service>.<env>.athenaintel.com``
60
+ convention, so substituting ``docx-studio`` → ``agora`` lands on the
61
+ matching Agora service. Returns ``None`` when the substitution
62
+ doesn't apply (e.g. ``http://localhost:4100``), in which case the
63
+ caller must provide ``agora_base_url`` explicitly.
64
+ """
65
+ if "docx-studio" not in docx_base_url:
66
+ return None
67
+ return docx_base_url.replace("docx-studio", "agora")
68
+
69
+
70
+ def create_empty_document(
71
+ *,
72
+ base_url: str | None = None,
73
+ api_key: str | None = None,
74
+ name: str | None = None,
75
+ parent_folder_id: str | None = None,
76
+ workspace_id: str | None = None,
77
+ timeout: float = 30.0,
78
+ ) -> CreateAssetResult:
79
+ """Call ``POST {base_url}/docs/empty`` and parse the response.
80
+
81
+ Args:
82
+ base_url: docx-studio API base, e.g. ``https://docx-studio.stg.athenaintel.com``.
83
+ Falls back to ``$ATHENA_DOCX_BASE_URL``.
84
+ api_key: Athena API key (PropelAuth user API key) or PropelAuth
85
+ access token. Sent as ``Authorization: Bearer <api_key>``.
86
+ Falls back to ``$ATHENA_DOCX_API_KEY``.
87
+ name: Optional display title.
88
+ parent_folder_id: Optional parent folder; defaults to workspace root.
89
+ workspace_id: Optional workspace UUID; defaults to caller's
90
+ current workspace.
91
+ timeout: Request timeout in seconds.
92
+
93
+ Returns:
94
+ Parsed response dict with the new asset_id and a collab bundle
95
+ ready to feed into ``Session(..., bundle=...)``.
96
+
97
+ Raises:
98
+ SessionError: missing base_url or unparseable response.
99
+ AuthenticationError: missing api_key, or the server returned 401/403.
100
+ DocxError: any other 4xx/5xx from the server.
101
+ """
102
+ resolved_base: str | None = base_url or os.environ.get(_BASE_URL_ENV)
103
+ resolved_key: str | None = api_key or os.environ.get(_API_KEY_ENV)
104
+
105
+ if not resolved_base:
106
+ raise SessionError(
107
+ f"Missing base_url and {_BASE_URL_ENV} env var. "
108
+ "Pass base_url= to Document.create() or set the env var.",
109
+ )
110
+ if not resolved_key:
111
+ raise AuthenticationError(
112
+ f"Missing api_key and {_API_KEY_ENV} env var. "
113
+ "Pass api_key= to Document.create() or set the env var.",
114
+ )
115
+
116
+ url: str = resolved_base.rstrip("/") + "/docs/empty"
117
+ body: dict[str, str] = {}
118
+ if name is not None:
119
+ body["name"] = name
120
+ if parent_folder_id is not None:
121
+ body["parentFolderId"] = parent_folder_id
122
+ if workspace_id is not None:
123
+ body["workspaceId"] = workspace_id
124
+
125
+ # Lazy import — _http is loaded lazily by Document.create(), so the
126
+ # package is fully initialized by the time we reach this code.
127
+ from docx import __version__
128
+
129
+ payload: bytes = json.dumps(body).encode("utf-8")
130
+ req = urllib.request.Request( # noqa: S310
131
+ url,
132
+ data=payload,
133
+ method="POST",
134
+ headers={
135
+ "Content-Type": "application/json",
136
+ "Authorization": f"Bearer {resolved_key}",
137
+ "Accept": "application/json",
138
+ "User-Agent": f"athena-python-docx/{__version__}",
139
+ },
140
+ )
141
+
142
+ try:
143
+ with urllib.request.urlopen(req, timeout=timeout) as resp: # noqa: S310
144
+ raw: bytes = resp.read()
145
+ except urllib.error.HTTPError as e:
146
+ err_body: str = ""
147
+ try:
148
+ err_body = e.read().decode("utf-8", errors="replace")
149
+ except Exception: # noqa: BLE001
150
+ pass
151
+ if e.code in (401, 403):
152
+ raise AuthenticationError(
153
+ f"docx-studio rejected the API key (HTTP {e.code}): {err_body}",
154
+ ) from e
155
+ raise DocxError(
156
+ f"docx-studio /docs/empty returned HTTP {e.code}: {err_body}",
157
+ ) from e
158
+ except urllib.error.URLError as e:
159
+ raise SessionError(
160
+ f"Unable to reach docx-studio at {url}: {e.reason}",
161
+ ) from e
162
+
163
+ try:
164
+ parsed: dict = json.loads(raw.decode("utf-8"))
165
+ except (UnicodeDecodeError, json.JSONDecodeError) as e:
166
+ raise SessionError(
167
+ f"docx-studio returned non-JSON response: {raw[:200]!r}",
168
+ ) from e
169
+
170
+ asset_id_obj = parsed.get("assetId")
171
+ name_obj = parsed.get("name")
172
+ ws_obj = parsed.get("workspaceId")
173
+ collab_obj = parsed.get("collab")
174
+ if not (
175
+ isinstance(asset_id_obj, str)
176
+ and isinstance(name_obj, str)
177
+ and isinstance(ws_obj, str)
178
+ and isinstance(collab_obj, dict)
179
+ ):
180
+ raise SessionError(
181
+ f"docx-studio response is missing required fields: {parsed!r}",
182
+ )
183
+ token_obj = collab_obj.get("token")
184
+ ws_url_obj = collab_obj.get("wsUrl")
185
+ bundle_ws_obj = collab_obj.get("workspaceId")
186
+ if not (
187
+ isinstance(token_obj, str)
188
+ and isinstance(ws_url_obj, str)
189
+ and isinstance(bundle_ws_obj, str)
190
+ ):
191
+ raise SessionError(
192
+ f"docx-studio collab bundle is malformed: {collab_obj!r}",
193
+ )
194
+
195
+ return CreateAssetResult(
196
+ asset_id=asset_id_obj,
197
+ name=name_obj,
198
+ workspace_id=ws_obj,
199
+ collab=_CollabBundle(
200
+ SUPERDOC_COLLAB_TOKEN=token_obj,
201
+ KERYX_WS_URL=ws_url_obj,
202
+ ATHENA_WORKSPACE_ID=bundle_ws_obj,
203
+ ),
204
+ )
205
+
206
+
207
+ class UploadAssetResult(TypedDict):
208
+ """Parsed response from agora's ``POST /api/super-docs/create-from-upload``."""
209
+
210
+ asset_id: str
211
+ name: str
212
+
213
+
214
+ def upload_document(
215
+ *,
216
+ docx_path: str,
217
+ docx_base_url: str | None = None,
218
+ agora_base_url: str | None = None,
219
+ api_key: str | None = None,
220
+ name: str | None = None,
221
+ parent_folder_id: str | None = None,
222
+ timeout: float = 120.0,
223
+ ) -> UploadAssetResult:
224
+ """Upload a local ``.docx`` to agora and create a new SuperDocument asset.
225
+
226
+ The endpoint lives on agora (``POST /api/super-docs/create-from-upload``),
227
+ not docx-studio, because the upload + Y.Doc-seed path is implemented
228
+ server-side in agora. The SDK still talks to docx-studio for command
229
+ application; this function only handles the initial create.
230
+
231
+ Args:
232
+ docx_path: Filesystem path to the ``.docx`` to upload.
233
+ docx_base_url: docx-studio base URL — used as a fallback to derive
234
+ the agora URL when ``agora_base_url`` is not supplied. Falls
235
+ back to ``$ATHENA_DOCX_BASE_URL``.
236
+ agora_base_url: Explicit Agora base URL. Falls back to
237
+ ``$ATHENA_AGORA_BASE_URL`` and finally to a host substitution
238
+ of ``docx-studio`` → ``agora`` against ``docx_base_url``.
239
+ api_key: Athena API key. Falls back to ``$ATHENA_DOCX_API_KEY``.
240
+ name: Optional asset title (defaults to the file's stem).
241
+ parent_folder_id: Optional parent folder for the new asset.
242
+ timeout: Request timeout in seconds (uploads can be slow for big
243
+ decks/templates).
244
+
245
+ Returns:
246
+ Dict with the new asset_id and the resolved title.
247
+
248
+ Raises:
249
+ SessionError: missing base URLs / unreachable agora.
250
+ AuthenticationError: missing api_key, or 401/403 from server.
251
+ DocxError: any other 4xx/5xx, or a malformed response.
252
+ """
253
+ import io
254
+ import mimetypes
255
+ import os
256
+ import uuid
257
+
258
+ resolved_docx_base: str | None = docx_base_url or os.environ.get(_BASE_URL_ENV)
259
+ resolved_key: str | None = api_key or os.environ.get(_API_KEY_ENV)
260
+ resolved_agora_base: str | None = (
261
+ agora_base_url
262
+ or os.environ.get(_AGORA_BASE_URL_ENV)
263
+ or (_derive_agora_base_url(resolved_docx_base) if resolved_docx_base else None)
264
+ )
265
+
266
+ if not resolved_agora_base:
267
+ raise SessionError(
268
+ f"Missing agora_base_url, {_AGORA_BASE_URL_ENV}, and {_BASE_URL_ENV} "
269
+ "(or the docx-studio URL doesn't follow the docx-studio↔agora "
270
+ "host pattern). Pass agora_base_url= to Document.create() or set "
271
+ f"the {_AGORA_BASE_URL_ENV} env var.",
272
+ )
273
+ if not resolved_key:
274
+ raise AuthenticationError(
275
+ f"Missing api_key and {_API_KEY_ENV} env var. "
276
+ "Pass api_key= to Document.create() or set the env var.",
277
+ )
278
+
279
+ if not os.path.exists(docx_path):
280
+ raise SessionError(f"DOCX file not found: {docx_path}")
281
+ if not docx_path.lower().endswith(".docx"):
282
+ raise SessionError(
283
+ f"Only .docx files are accepted by the upload endpoint (got {docx_path})",
284
+ )
285
+
286
+ title = name
287
+ if title is None:
288
+ base = os.path.basename(docx_path)
289
+ title = base[:-5] if base.lower().endswith(".docx") else base
290
+
291
+ # Build the agora URL with query-string params (FastAPI binds non-body
292
+ # args from the query string for multipart routes).
293
+ from urllib.parse import urlencode
294
+
295
+ qs: dict[str, str] = {"is_template": "false", "title": title}
296
+ if parent_folder_id is not None:
297
+ qs["parent_folder_id"] = parent_folder_id
298
+
299
+ url: str = (
300
+ resolved_agora_base.rstrip("/")
301
+ + "/api/super-docs/create-from-upload?"
302
+ + urlencode(qs)
303
+ )
304
+
305
+ # Build a minimal multipart/form-data body without pulling in
306
+ # `requests` — keep the urllib-only dep profile.
307
+ with open(docx_path, "rb") as f:
308
+ file_bytes: bytes = f.read()
309
+ content_type: str = (
310
+ mimetypes.guess_type(docx_path)[0]
311
+ or "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
312
+ )
313
+ boundary: str = f"----athenaDocxBoundary{uuid.uuid4().hex}"
314
+ filename = os.path.basename(docx_path)
315
+ body_parts: list[bytes] = []
316
+ body_parts.append(f"--{boundary}\r\n".encode("utf-8"))
317
+ body_parts.append(
318
+ (
319
+ 'Content-Disposition: form-data; name="file"; '
320
+ f'filename="{filename}"\r\n'
321
+ f"Content-Type: {content_type}\r\n\r\n"
322
+ ).encode("utf-8"),
323
+ )
324
+ body_parts.append(file_bytes)
325
+ body_parts.append(f"\r\n--{boundary}--\r\n".encode("utf-8"))
326
+ body: bytes = b"".join(body_parts)
327
+
328
+ from docx import __version__
329
+
330
+ req = urllib.request.Request( # noqa: S310
331
+ url,
332
+ data=body,
333
+ method="POST",
334
+ headers={
335
+ "Content-Type": f"multipart/form-data; boundary={boundary}",
336
+ "Authorization": f"Bearer {resolved_key}",
337
+ "Accept": "application/json",
338
+ "User-Agent": f"athena-python-docx/{__version__}",
339
+ },
340
+ )
341
+
342
+ try:
343
+ with urllib.request.urlopen(req, timeout=timeout) as resp: # noqa: S310
344
+ raw: bytes = resp.read()
345
+ except urllib.error.HTTPError as e:
346
+ err_body: str = ""
347
+ try:
348
+ err_body = e.read().decode("utf-8", errors="replace")
349
+ except Exception: # noqa: BLE001
350
+ pass
351
+ if e.code in (401, 403):
352
+ raise AuthenticationError(
353
+ f"agora rejected the API key (HTTP {e.code}): {err_body}",
354
+ ) from e
355
+ raise DocxError(
356
+ f"agora /api/super-docs/create-from-upload returned HTTP {e.code}: {err_body}",
357
+ ) from e
358
+ except urllib.error.URLError as e:
359
+ raise SessionError(
360
+ f"Unable to reach agora at {url}: {e.reason}",
361
+ ) from e
362
+
363
+ # Avoid unused import warning when io isn't otherwise referenced.
364
+ _ = io
365
+ try:
366
+ parsed: dict = json.loads(raw.decode("utf-8"))
367
+ except (UnicodeDecodeError, json.JSONDecodeError) as e:
368
+ raise SessionError(
369
+ f"agora returned non-JSON response: {raw[:200]!r}",
370
+ ) from e
371
+
372
+ asset_id_obj = parsed.get("id")
373
+ name_obj = parsed.get("title")
374
+ if not (isinstance(asset_id_obj, str) and isinstance(name_obj, str)):
375
+ raise SessionError(
376
+ f"agora response is missing required fields: {parsed!r}",
377
+ )
378
+
379
+ return UploadAssetResult(asset_id=asset_id_obj, name=name_obj)
@@ -30,6 +30,7 @@ format is now typed end-to-end either way.
30
30
  from __future__ import annotations
31
31
 
32
32
  import json
33
+ import os
33
34
  from typing import Any
34
35
 
35
36
  import requests
@@ -106,6 +107,7 @@ from docx.commands import (
106
107
  TablesSetRowHeight,
107
108
  TablesSetStyle,
108
109
  TablesSetTableOptions,
110
+ TableSetCell,
109
111
  TrackChangesDecide,
110
112
  TrackChangesGet,
111
113
  TrackChangesList,
@@ -372,6 +374,9 @@ def _http_post_json(
372
374
  "Accept": "application/json",
373
375
  "User-Agent": _user_agent(),
374
376
  }
377
+ custom_attr = os.environ.get("ATHENA_DOCX_CUSTOM_ATTRIBUTIONS")
378
+ if custom_attr:
379
+ headers["X-Custom-Attributions"] = custom_attr
375
380
  try:
376
381
  resp = session.post(
377
382
  url,
@@ -647,6 +652,7 @@ _OP_TO_COMMAND: dict[str, type[Command]] = {
647
652
  "tables.set_cell_properties": TablesSetCellProperties,
648
653
  "tables.set_column_width": TablesSetColumnWidth,
649
654
  "tables.set_row_height": TablesSetRowHeight,
655
+ "tables.set_cell": TableSetCell,
650
656
  # Images (mutations)
651
657
  "images.set_size": SetImageSize,
652
658
  "images.set_alt_text": SetImageAltText,
@@ -0,0 +1,92 @@
1
+ """Friendly-name → OOXML styleId resolution for Word's built-in table styles.
2
+
3
+ Word stores built-in table styles in ``word/styles.xml`` with internal
4
+ styleIds like ``LightGrid-Accent1`` and friendly display names like
5
+ ``"Light Grid Accent 1"``. python-docx accepts the friendly name and
6
+ resolves it against the loaded document's styles collection at write
7
+ time. Our SDK ships commands over HTTP and never sees ``styles.xml``,
8
+ so we keep an in-process map and resolve the friendly form to the
9
+ canonical styleId before the value reaches the
10
+ ``<w:tblStyle w:val="..."/>`` element in the exported ``.docx``.
11
+
12
+ Without this resolution Word can't find the style definition in
13
+ ``styles.xml`` and silently falls back to ``Normal Table`` — i.e.
14
+ no borders, no header fill, no banding.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+
20
+ def _build_builtin_table_style_map() -> dict[str, str]:
21
+ """Build the friendly-name → styleId map for all Word built-in table styles.
22
+
23
+ Mirrors every ``<w:lsdException w:name="...">`` table-style
24
+ declaration in Word's default ``styles.xml`` for Word 2007 and
25
+ Word 2013+. Pattern: friendly names use spaces; styleIds collapse
26
+ spaces in the base and join the optional ``Accent N`` suffix with
27
+ a hyphen.
28
+ """
29
+ table: dict[str, str] = {
30
+ "Normal Table": "TableNormal",
31
+ "Table Grid": "TableGrid",
32
+ # Word 2013+ added a single un-numbered "Grid Table Light"
33
+ # alongside the numbered 1–7 series.
34
+ "Grid Table Light": "GridTableLight",
35
+ }
36
+ # Word 2007 era — 14 bases × (base + 6 accents) = 98 entries.
37
+ bases_2007: tuple[tuple[str, str], ...] = (
38
+ ("Light Shading", "LightShading"),
39
+ ("Light List", "LightList"),
40
+ ("Light Grid", "LightGrid"),
41
+ ("Medium Shading 1", "MediumShading1"),
42
+ ("Medium Shading 2", "MediumShading2"),
43
+ ("Medium List 1", "MediumList1"),
44
+ ("Medium List 2", "MediumList2"),
45
+ ("Medium Grid 1", "MediumGrid1"),
46
+ ("Medium Grid 2", "MediumGrid2"),
47
+ ("Medium Grid 3", "MediumGrid3"),
48
+ ("Dark List", "DarkList"),
49
+ ("Colorful Shading", "ColorfulShading"),
50
+ ("Colorful List", "ColorfulList"),
51
+ ("Colorful Grid", "ColorfulGrid"),
52
+ )
53
+ # Word 2013+ Plain Table — 5 entries, no accents.
54
+ for n in range(1, 6):
55
+ table[f"Plain Table {n}"] = f"PlainTable{n}"
56
+ # Word 2013+ Grid Table / List Table — 7 bases × (base + 6 accents) = 49
57
+ # entries per series. The "Light"/"Dark"/"Colorful" suffix on some
58
+ # bases is part of the styleId, not a separate "Accent N" axis.
59
+ bases_2013: tuple[tuple[str, str], ...] = (
60
+ ("Grid Table 1 Light", "GridTable1Light"),
61
+ ("Grid Table 2", "GridTable2"),
62
+ ("Grid Table 3", "GridTable3"),
63
+ ("Grid Table 4", "GridTable4"),
64
+ ("Grid Table 5 Dark", "GridTable5Dark"),
65
+ ("Grid Table 6 Colorful", "GridTable6Colorful"),
66
+ ("Grid Table 7 Colorful", "GridTable7Colorful"),
67
+ ("List Table 1 Light", "ListTable1Light"),
68
+ ("List Table 2", "ListTable2"),
69
+ ("List Table 3", "ListTable3"),
70
+ ("List Table 4", "ListTable4"),
71
+ ("List Table 5 Dark", "ListTable5Dark"),
72
+ ("List Table 6 Colorful", "ListTable6Colorful"),
73
+ ("List Table 7 Colorful", "ListTable7Colorful"),
74
+ )
75
+ for friendly, style_id in (*bases_2007, *bases_2013):
76
+ table[friendly] = style_id
77
+ for n in range(1, 7):
78
+ table[f"{friendly} Accent {n}"] = f"{style_id}-Accent{n}"
79
+ return table
80
+
81
+
82
+ BUILTIN_TABLE_STYLE_IDS: dict[str, str] = _build_builtin_table_style_map()
83
+
84
+
85
+ def resolve_table_style_id(style: str) -> str:
86
+ """Return the OOXML styleId for a Word built-in table style friendly name.
87
+
88
+ Custom user-defined style names pass through unchanged so that
89
+ callers that already supply canonical styleIds (or non-built-in
90
+ style names defined elsewhere) keep working.
91
+ """
92
+ return BUILTIN_TABLE_STYLE_IDS.get(style, style)
@@ -6,7 +6,10 @@ python-docx signature:
6
6
  Our signature deviates from the path-based one because we don't open
7
7
  .docx files from disk — we open Y.Doc assets from Keryx. The parameter
8
8
  is reused: pass an asset_id string where python-docx would take a file
9
- path.
9
+ path. The id MUST be ``asset_<uuid>``; anything else (a title, a file
10
+ path, etc.) raises ``ValueError`` before any HTTP request. To mint a
11
+ new asset, call ``Document.create(name=...)`` and reuse the returned
12
+ ``doc.asset_id`` on subsequent ``Document(...)`` opens.
10
13
 
11
14
  For net-new asset creation, use the classmethod ``Document.create()``
12
15
  (see ``docx.document``). Stock python-docx returns a blank document
@@ -18,8 +21,18 @@ write that has to happen first. ``Document.create()`` handles that via
18
21
 
19
22
  from __future__ import annotations
20
23
 
24
+ import re
25
+
21
26
  from docx.document import Document as _Document
22
27
 
28
+ # Asset-id shape validation. Athena SuperDocument assets are
29
+ # ``asset_<uuid>``. Rejecting anything else up front prevents the
30
+ # common agent failure mode of passing a document TITLE
31
+ # (``Document("Q4 Report Draft")``) instead of its id, which would
32
+ # otherwise round-trip through the docx-studio session and fail with
33
+ # an opaque server error.
34
+ _ASSET_ID_PATTERN = re.compile(r"^asset_[A-Za-z0-9_-]+$")
35
+
23
36
 
24
37
  def Document(
25
38
  docx: str | None = None,
@@ -67,14 +80,49 @@ def Document(
67
80
  "To create a brand-new document, call "
68
81
  "Document.create(name=..., base_url=..., api_key=...).",
69
82
  )
70
- return _Document(
71
- asset_id=docx,
72
- http_base_url=base_url,
73
- http_api_key=api_key,
74
- user_name=user_name,
75
- user_email=user_email,
76
- track_revisions=track_revisions,
83
+ if not _ASSET_ID_PATTERN.match(docx):
84
+ raise ValueError(
85
+ f"Document({docx!r}) is not a valid SuperDocument asset id. "
86
+ f"Asset ids look like 'asset_<uuid>' "
87
+ f"(e.g., 'asset_3a9328bc-9c1c-4498-be8f-bda3883276f5'). To "
88
+ f"open an existing document, pass its asset id — NOT its "
89
+ f"title. To create a new document titled {docx!r}, call "
90
+ f"Document.create(name={docx!r}) and reuse the returned "
91
+ f"document's asset id on subsequent calls in the same "
92
+ f"conversation."
93
+ )
94
+
95
+ # PTC: surface document open as its own sub-tool-card so the
96
+ # frontend can react (e.g., auto-open the asset tab). The paired
97
+ # ``Document.create()`` path emits ``CreateDocument``; this open
98
+ # path emits ``OpenDocument`` with the asset id.
99
+ from docx import _ptc
100
+
101
+ call_id = _ptc.emit_begin("OpenDocument", {"assetId": docx})
102
+ try:
103
+ doc = _Document(
104
+ asset_id=docx,
105
+ http_base_url=base_url,
106
+ http_api_key=api_key,
107
+ user_name=user_name,
108
+ user_email=user_email,
109
+ track_revisions=track_revisions,
110
+ )
111
+ except Exception as exc:
112
+ _ptc.emit_end(
113
+ call_id=call_id,
114
+ tool_name="OpenDocument",
115
+ result={"ok": False, "error": str(exc)},
116
+ is_error=True,
117
+ )
118
+ raise
119
+ _ptc.emit_end(
120
+ call_id=call_id,
121
+ tool_name="OpenDocument",
122
+ result={"ok": True, "assetId": docx},
123
+ is_error=False,
77
124
  )
125
+ return doc
78
126
 
79
127
 
80
128
  # Re-export the classmethod factories at module level so callers can