athena-python-docx 0.10.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 (318) hide show
  1. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/CLAUDE.md +35 -1
  2. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/PKG-INFO +1 -1
  3. {athena_python_docx-0.10.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.10.1/docx/_table_styles.py +92 -0
  6. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/document.py +59 -15
  7. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/table.py +4 -1
  8. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/pyproject.toml +1 -1
  9. athena_python_docx-0.10.1/tests/test_document_create_from_template.py +312 -0
  10. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_style_acceptance.py +5 -2
  11. athena_python_docx-0.10.1/tests/test_table_style_id_resolution.py +173 -0
  12. athena_python_docx-0.10.0/docx/_http.py +0 -189
  13. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/.gitignore +0 -0
  14. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/README.md +0 -0
  15. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/_batching.py +0 -0
  16. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/_buffer.py +0 -0
  17. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/_http_doc.py +0 -0
  18. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/_image_utils.py +0 -0
  19. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/_ptc.py +0 -0
  20. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/api.py +0 -0
  21. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/client.py +0 -0
  22. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/commands.py +0 -0
  23. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/comments.py +0 -0
  24. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/enum/__init__.py +0 -0
  25. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/enum/section.py +0 -0
  26. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/enum/style.py +0 -0
  27. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/enum/table.py +0 -0
  28. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/enum/text.py +0 -0
  29. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/errors.py +0 -0
  30. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/exceptions.py +0 -0
  31. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/opc/__init__.py +0 -0
  32. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/opc/coreprops.py +0 -0
  33. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/oxml/__init__.py +0 -0
  34. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/revisions.py +0 -0
  35. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/section.py +0 -0
  36. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/settings.py +0 -0
  37. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/shape.py +0 -0
  38. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/shared.py +0 -0
  39. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/styles/__init__.py +0 -0
  40. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/styles/style.py +0 -0
  41. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/styles/styles.py +0 -0
  42. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/text/__init__.py +0 -0
  43. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/text/font.py +0 -0
  44. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/text/hyperlink.py +0 -0
  45. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/text/pagebreak.py +0 -0
  46. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/text/paragraph.py +0 -0
  47. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/text/parfmt.py +0 -0
  48. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/text/run.py +0 -0
  49. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/text/tabstops.py +0 -0
  50. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/docx/typing.py +0 -0
  51. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/scripts/publish.sh +0 -0
  52. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/scripts/release.sh +0 -0
  53. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/scripts/round_trip_smoke.py +0 -0
  54. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/scripts/smoke_test_block_not_found.py +0 -0
  55. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/__init__.py +0 -0
  56. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/conftest.py +0 -0
  57. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/METHODOLOGY.md +0 -0
  58. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/README.md +0 -0
  59. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/__init__.py +0 -0
  60. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/ab_probe_cases.py +0 -0
  61. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/ab_probe_runner.py +0 -0
  62. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/auto_gen_cases.py +0 -0
  63. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/binary_round_trip.py +0 -0
  64. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/cases.py +0 -0
  65. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/complex_cases.py +0 -0
  66. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/coverage_report.py +0 -0
  67. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/extract.py +0 -0
  68. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/extreme_cases.py +0 -0
  69. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/fake_session.py +0 -0
  70. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/local_runner.py +0 -0
  71. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/mega_cases.py +0 -0
  72. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshot.py +0 -0
  73. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
  74. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
  75. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
  76. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
  77. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
  78. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
  79. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
  80. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
  81. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
  82. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
  83. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
  84. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
  85. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/103_cell_tables_enumeration.json +0 -0
  86. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
  87. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
  88. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
  89. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
  90. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
  91. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
  92. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
  93. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
  94. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
  95. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
  96. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
  97. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
  98. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
  99. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
  100. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
  101. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
  102. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
  103. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/23_nested_table.json +0 -0
  104. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
  105. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
  106. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
  107. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
  108. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
  109. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
  110. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
  111. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
  112. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
  113. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
  114. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
  115. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
  116. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
  117. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
  118. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
  119. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
  120. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
  121. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
  122. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
  123. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
  124. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
  125. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
  126. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
  127. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
  128. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
  129. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
  130. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
  131. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/51_nested_tables_deep.json +0 -0
  132. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
  133. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
  134. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
  135. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
  136. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
  137. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
  138. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
  139. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
  140. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
  141. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
  142. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
  143. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
  144. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
  145. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
  146. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
  147. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
  148. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
  149. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
  150. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
  151. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
  152. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
  153. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
  154. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
  155. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
  156. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
  157. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
  158. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
  159. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
  160. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
  161. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
  162. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
  163. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
  164. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
  165. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
  166. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
  167. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
  168. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
  169. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
  170. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
  171. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
  172. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
  173. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
  174. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
  175. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
  176. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
  177. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
  178. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
  179. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
  180. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex01_five_levels_deep_tables.json +0 -0
  181. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
  182. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
  183. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
  184. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
  185. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
  186. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
  187. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
  188. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
  189. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
  190. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
  191. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
  192. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
  193. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
  194. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
  195. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
  196. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
  197. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
  198. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
  199. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
  200. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
  201. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
  202. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
  203. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
  204. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
  205. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
  206. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
  207. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
  208. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
  209. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
  210. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
  211. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
  212. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
  213. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
  214. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
  215. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
  216. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
  217. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
  218. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
  219. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
  220. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
  221. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
  222. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
  223. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw14_nested_cell_table.json +0 -0
  224. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
  225. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/ours_spec.json +0 -0
  226. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/parity_crawl.py +0 -0
  227. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/parity_diff.json +0 -0
  228. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/real_world_cases.py +0 -0
  229. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/round_trip_tests.py +0 -0
  230. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/runner.py +0 -0
  231. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/stock_spec.json +0 -0
  232. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/fidelity/test_e2e_against_staging.py +0 -0
  233. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/parity/README.md +0 -0
  234. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/parity/__init__.py +0 -0
  235. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/parity/baseline_gaps.json +0 -0
  236. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/parity/compare.py +0 -0
  237. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/parity/intentional_deviations.json +0 -0
  238. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/parity/introspect.py +0 -0
  239. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
  240. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/parity/reports/gap_report.json +0 -0
  241. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/parity/run_parity.py +0 -0
  242. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/parity/snapshots/athena_latest.json +0 -0
  243. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
  244. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/parity/test_parity_gap.py +0 -0
  245. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_batching_perf.py +0 -0
  246. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_block_not_found_error.py +0 -0
  247. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_buffer.py +0 -0
  248. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_cell_text_plain_fastpath.py +0 -0
  249. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_collapsed_range_format.py +0 -0
  250. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_command_dataclasses.py +0 -0
  251. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_commands.py +0 -0
  252. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_comments.py +0 -0
  253. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_document_asset_id_property.py +0 -0
  254. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_document_create.py +0 -0
  255. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_document_factory_validation.py +0 -0
  256. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_e2e_partial_failure_cascade.py +0 -0
  257. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_http_transport.py +0 -0
  258. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_hyperlink_coalescing.py +0 -0
  259. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_insert_deferred.py +0 -0
  260. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_iter_inner_content.py +0 -0
  261. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_list_styles.py +0 -0
  262. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_merged_cells.py +0 -0
  263. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_oxml_shim.py +0 -0
  264. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_paragraph_text_len_cache.py +0 -0
  265. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_parity_misc.py +0 -0
  266. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_parity_round2.py +0 -0
  267. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_partial_failure_cascade.py +0 -0
  268. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_phase_a_behavior.py +0 -0
  269. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_phase_b_headers_footers.py +0 -0
  270. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_phase_c_tables.py +0 -0
  271. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_pr19766_review_fixes.py +0 -0
  272. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_ptc.py +0 -0
  273. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_python_docx_api_parity.py +0 -0
  274. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_revisions.py +0 -0
  275. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_add_paragraph_style.py +0 -0
  276. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_add_picture.py +0 -0
  277. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_add_run.py +0 -0
  278. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
  279. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_comments_add_comment.py +0 -0
  280. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_comments_get.py +0 -0
  281. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_document_audit.py +0 -0
  282. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_document_element.py +0 -0
  283. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_enum_section.py +0 -0
  284. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_font_audit.py +0 -0
  285. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_header_footer.py +0 -0
  286. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_hyperlink.py +0 -0
  287. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_inline_shape.py +0 -0
  288. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
  289. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_misc.py +0 -0
  290. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_paragraph_strict.py +0 -0
  291. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_paragraph_style.py +0 -0
  292. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
  293. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_parfmt.py +0 -0
  294. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_row_col_cell.py +0 -0
  295. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_run_add_break.py +0 -0
  296. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_run_bool_setters.py +0 -0
  297. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_run_style.py +0 -0
  298. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_run_style_strict.py +0 -0
  299. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_run_text.py +0 -0
  300. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_run_underline.py +0 -0
  301. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_section_audit.py +0 -0
  302. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_section_dimensions.py +0 -0
  303. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_section_onoff.py +0 -0
  304. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_settings.py +0 -0
  305. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_shared_audit.py +0 -0
  306. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_style.py +0 -0
  307. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_styles.py +0 -0
  308. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_table_audit.py +0 -0
  309. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_table_cell.py +0 -0
  310. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_table_dimensions.py +0 -0
  311. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_silent_stub_table_layout.py +0 -0
  312. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_smoke_integration.py +0 -0
  313. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_style_font.py +0 -0
  314. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_style_setters_contract.py +0 -0
  315. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_table_set_cell_perf.py +0 -0
  316. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_wire_contract.py +0 -0
  317. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/tests/test_zod_wire_contract.py +0 -0
  318. {athena_python_docx-0.10.0 → athena_python_docx-0.10.1}/uv.lock +0 -0
@@ -81,12 +81,46 @@ 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.
90
124
 
91
125
  - **`Document.asset_id: str`** read-only property — the Athena asset
92
126
  id this Document is bound to (`asset_<uuid>`). Agent-callable so
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: athena-python-docx
3
- Version: 0.10.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.10.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)
@@ -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)
@@ -174,12 +174,21 @@ class Document:
174
174
  user_name: str | None = None,
175
175
  user_email: str | None = None,
176
176
  track_revisions: bool = False,
177
+ docx: str | None = None,
178
+ agora_base_url: str | None = None,
177
179
  ) -> "Document":
178
180
  """Create a new SuperDocument asset and return an opened Document.
179
181
 
180
- Hits ``POST {base_url}/docs/empty`` on docx-studio with the
181
- provided Athena API key, then returns a Document bound to the
182
- new asset.
182
+ By default this hits ``POST {base_url}/docs/empty`` on docx-studio
183
+ with the provided Athena API key, then returns a Document bound to
184
+ the new asset.
185
+
186
+ When ``docx`` is provided, the local ``.docx`` file is uploaded as
187
+ the seed instead of starting from the bundled default template.
188
+ The upload goes through agora's ``/api/super-docs/create-from-upload``
189
+ endpoint (which seeds the new SuperDoc Y.Doc via SuperDoc SDK).
190
+ Mirrors stock python-docx's ``Document(docx=...)`` constructor
191
+ kwarg.
183
192
 
184
193
  Args:
185
194
  name: Display title for the new document. Defaults to the
@@ -192,6 +201,14 @@ class Document:
192
201
  workspace root.
193
202
  workspace_id: Optional workspace UUID; defaults to the
194
203
  caller's current workspace as resolved by Agora.
204
+ docx: Optional path to a local ``.docx`` file. When provided,
205
+ the file is uploaded and used as the seed for the new
206
+ document. Mirrors python-docx's
207
+ ``Document(docx="template.docx")`` pattern.
208
+ agora_base_url: Agora base URL used by the upload path. Falls
209
+ back to ``$ATHENA_AGORA_BASE_URL`` and finally to a
210
+ ``docx-studio`` → ``agora`` substitution against
211
+ ``base_url``. Ignored when ``docx`` is not set.
195
212
 
196
213
  Returns:
197
214
  A Document bound to the newly-created asset.
@@ -204,10 +221,12 @@ class Document:
204
221
  DEVIATION FROM python-docx: stock python-docx returns a blank
205
222
  in-memory document for ``Document(None)``. Our SDK is backed by
206
223
  an Athena asset, so we expose a separate ``create()`` factory
207
- rather than overloading the constructor.
224
+ rather than overloading the constructor. The ``docx=`` kwarg
225
+ mirrors the upstream constructor kwarg on this factory instead.
208
226
  """
209
227
  from docx import _ptc
210
- from docx._http import create_empty_document
228
+ from docx._http import create_empty_document, upload_document
229
+ from docx.errors import DocxError
211
230
 
212
231
  # PTC: surface asset creation as its own top-level sub-tool-card.
213
232
  # The asset_id isn't known until create_empty_document returns,
@@ -215,13 +234,37 @@ class Document:
215
234
  # asset_id into the end event's result.
216
235
  call_id = _ptc.emit_begin("CreateDocument", {"name": name})
217
236
  try:
218
- result = create_empty_document(
219
- base_url=base_url,
220
- api_key=api_key,
221
- name=name,
222
- parent_folder_id=parent_folder_id,
223
- workspace_id=workspace_id,
224
- )
237
+ if docx is not None:
238
+ # Fail-fast: the agora upload endpoint that backs the
239
+ # ``docx=`` path doesn't accept a workspace_id parameter
240
+ # (it always lands in the caller's current workspace).
241
+ # Silently dropping it would be a contract violation
242
+ # vs. the non-``docx`` path which honors workspace_id.
243
+ if workspace_id is not None:
244
+ raise DocxError(
245
+ "workspace_id is not supported when docx= is provided. "
246
+ "Omit workspace_id (the uploaded template lands in the "
247
+ "caller's current workspace), or use Document.create() "
248
+ "without docx= and add content via the SDK afterwards.",
249
+ )
250
+ upload_result = upload_document(
251
+ docx_path=docx,
252
+ docx_base_url=base_url,
253
+ agora_base_url=agora_base_url,
254
+ api_key=api_key,
255
+ name=name,
256
+ parent_folder_id=parent_folder_id,
257
+ )
258
+ asset_id = upload_result["asset_id"]
259
+ else:
260
+ result = create_empty_document(
261
+ base_url=base_url,
262
+ api_key=api_key,
263
+ name=name,
264
+ parent_folder_id=parent_folder_id,
265
+ workspace_id=workspace_id,
266
+ )
267
+ asset_id = result["asset_id"]
225
268
  except Exception as exc:
226
269
  _ptc.emit_end(
227
270
  call_id=call_id,
@@ -233,11 +276,11 @@ class Document:
233
276
  _ptc.emit_end(
234
277
  call_id=call_id,
235
278
  tool_name="CreateDocument",
236
- result={"ok": True, "assetId": result["asset_id"]},
279
+ result={"ok": True, "assetId": asset_id},
237
280
  is_error=False,
238
281
  )
239
282
  return cls(
240
- asset_id=result["asset_id"],
283
+ asset_id=asset_id,
241
284
  http_base_url=base_url,
242
285
  http_api_key=api_key,
243
286
  user_name=user_name,
@@ -933,12 +976,13 @@ class Document:
933
976
  node_id: str = client_node_id
934
977
 
935
978
  if style:
979
+ from docx._table_styles import resolve_table_style_id
936
980
  from docx.styles.style import BaseStyle
937
981
 
938
982
  style_id = style.style_id if isinstance(style, BaseStyle) else str(style)
939
983
  run_sync(
940
984
  self._session.doc.tables.set_style(
941
- {"nodeId": node_id, "styleId": style_id},
985
+ {"nodeId": node_id, "styleId": resolve_table_style_id(style_id)},
942
986
  ),
943
987
  )
944
988
 
@@ -487,10 +487,13 @@ class Table:
487
487
 
488
488
  @style.setter
489
489
  def style(self, value: str | None) -> None:
490
+ from docx._table_styles import resolve_table_style_id
491
+
490
492
  nid: str = self._fresh_node_id()
493
+ style_id = resolve_table_style_id(value) if value else "TableGrid"
491
494
  run_sync(
492
495
  self._session.doc.tables.set_style(
493
- {"nodeId": nid, "styleId": value or "TableGrid"},
496
+ {"nodeId": nid, "styleId": style_id},
494
497
  ),
495
498
  )
496
499