athena-python-docx 0.9.0__tar.gz → 0.10.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (324) hide show
  1. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/CLAUDE.md +19 -4
  2. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/PKG-INFO +1 -1
  3. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/__init__.py +1 -1
  4. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/_http_doc.py +6 -0
  5. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/api.py +56 -8
  6. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/commands.py +28 -0
  7. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/document.py +45 -39
  8. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/errors.py +37 -4
  9. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/table.py +90 -48
  10. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/pyproject.toml +1 -1
  11. athena_python_docx-0.10.0/scripts/smoke_test_block_not_found.py +175 -0
  12. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/fake_session.py +68 -0
  13. athena_python_docx-0.10.0/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +12 -0
  14. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/103_cell_tables_enumeration.json +0 -1
  15. athena_python_docx-0.10.0/tests/fidelity/op_snapshots/18_table_cell_text.json +9 -0
  16. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -3
  17. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +1 -4
  18. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/23_nested_table.json +1 -4
  19. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +1 -3
  20. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -2
  21. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/35_full_report.json +9 -33
  22. athena_python_docx-0.10.0/tests/fidelity/op_snapshots/40_large_table_10x10.json +103 -0
  23. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +1 -5
  24. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/49_resume_layout.json +3 -12
  25. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/51_nested_tables_deep.json +1 -4
  26. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +12 -48
  27. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +1 -4
  28. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -1
  29. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +793 -1993
  30. athena_python_docx-0.10.0/tests/fidelity/op_snapshots/68_invoice.json +62 -0
  31. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -1
  32. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/72_legal_contract.json +6 -24
  33. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +20 -80
  34. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +9 -27
  35. athena_python_docx-0.10.0/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +24 -0
  36. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -1
  37. athena_python_docx-0.10.0/tests/fidelity/op_snapshots/91_many_small_tables.json +153 -0
  38. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/ex01_five_levels_deep_tables.json +1 -4
  39. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +3 -12
  40. athena_python_docx-0.10.0/tests/fidelity/op_snapshots/ex04_50x50_table.json +53 -0
  41. athena_python_docx-0.10.0/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +5 -0
  42. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/mega10_api_documentation.json → athena_python_docx-0.10.0/tests/fidelity/op_snapshots/ex10_complex_bom.json +126 -113
  43. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +80 -320
  44. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -1
  45. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/rw10_colored_grid_table.json → athena_python_docx-0.10.0/tests/fidelity/op_snapshots/ex14_styled_report_table.json +36 -69
  46. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +84 -336
  47. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +13 -52
  48. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +18 -72
  49. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +21 -98
  50. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +20 -88
  51. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/ex14_styled_report_table.json → athena_python_docx-0.10.0/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +44 -75
  52. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +8 -36
  53. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json → athena_python_docx-0.10.0/tests/fidelity/op_snapshots/mega10_api_documentation.json +104 -97
  54. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +12 -36
  55. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +12 -36
  56. athena_python_docx-0.10.0/tests/fidelity/op_snapshots/rw08_table_merged_header.json +13 -0
  57. athena_python_docx-0.10.0/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +148 -0
  58. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/rw14_nested_cell_table.json +1 -4
  59. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/parity/reports/gap_report.json +1 -1
  60. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/parity/snapshots/athena_latest.json +0 -50
  61. athena_python_docx-0.10.0/tests/test_document_asset_id_property.py +52 -0
  62. athena_python_docx-0.10.0/tests/test_document_factory_validation.py +52 -0
  63. athena_python_docx-0.10.0/tests/test_e2e_partial_failure_cascade.py +278 -0
  64. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_parity_misc.py +4 -2
  65. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_document_audit.py +55 -61
  66. athena_python_docx-0.10.0/tests/test_table_set_cell_perf.py +127 -0
  67. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_wire_contract.py +9 -0
  68. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -24
  69. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -27
  70. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -403
  71. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/68_invoice.json +0 -114
  72. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -54
  73. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -453
  74. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -203
  75. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -8
  76. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -596
  77. athena_python_docx-0.9.0/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -35
  78. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/.gitignore +0 -0
  79. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/README.md +0 -0
  80. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/_batching.py +0 -0
  81. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/_buffer.py +0 -0
  82. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/_http.py +0 -0
  83. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/_image_utils.py +0 -0
  84. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/_ptc.py +0 -0
  85. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/client.py +0 -0
  86. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/comments.py +0 -0
  87. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/enum/__init__.py +0 -0
  88. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/enum/section.py +0 -0
  89. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/enum/style.py +0 -0
  90. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/enum/table.py +0 -0
  91. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/enum/text.py +0 -0
  92. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/exceptions.py +0 -0
  93. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/opc/__init__.py +0 -0
  94. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/opc/coreprops.py +0 -0
  95. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/oxml/__init__.py +0 -0
  96. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/revisions.py +0 -0
  97. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/section.py +0 -0
  98. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/settings.py +0 -0
  99. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/shape.py +0 -0
  100. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/shared.py +0 -0
  101. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/styles/__init__.py +0 -0
  102. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/styles/style.py +0 -0
  103. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/styles/styles.py +0 -0
  104. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/text/__init__.py +0 -0
  105. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/text/font.py +0 -0
  106. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/text/hyperlink.py +0 -0
  107. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/text/pagebreak.py +0 -0
  108. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/text/paragraph.py +0 -0
  109. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/text/parfmt.py +0 -0
  110. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/text/run.py +0 -0
  111. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/text/tabstops.py +0 -0
  112. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/docx/typing.py +0 -0
  113. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/scripts/publish.sh +0 -0
  114. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/scripts/release.sh +0 -0
  115. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/scripts/round_trip_smoke.py +0 -0
  116. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/__init__.py +0 -0
  117. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/conftest.py +0 -0
  118. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/METHODOLOGY.md +0 -0
  119. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/README.md +0 -0
  120. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/__init__.py +0 -0
  121. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/ab_probe_cases.py +0 -0
  122. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/ab_probe_runner.py +0 -0
  123. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/auto_gen_cases.py +0 -0
  124. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/binary_round_trip.py +0 -0
  125. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/cases.py +0 -0
  126. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/complex_cases.py +0 -0
  127. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/coverage_report.py +0 -0
  128. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/extract.py +0 -0
  129. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/extreme_cases.py +0 -0
  130. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/local_runner.py +0 -0
  131. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/mega_cases.py +0 -0
  132. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshot.py +0 -0
  133. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
  134. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
  135. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
  136. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
  137. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
  138. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
  139. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
  140. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
  141. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
  142. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
  143. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
  144. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
  145. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
  146. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
  147. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
  148. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
  149. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
  150. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
  151. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
  152. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
  153. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
  154. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
  155. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
  156. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
  157. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
  158. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
  159. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
  160. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
  161. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
  162. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
  163. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
  164. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
  165. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
  166. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
  167. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
  168. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
  169. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
  170. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
  171. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
  172. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
  173. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
  174. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
  175. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
  176. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
  177. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
  178. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
  179. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
  180. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
  181. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
  182. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
  183. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
  184. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
  185. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
  186. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
  187. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
  188. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
  189. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
  190. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
  191. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
  192. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
  193. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
  194. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
  195. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
  196. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
  197. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
  198. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
  199. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
  200. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
  201. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
  202. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
  203. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
  204. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
  205. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
  206. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
  207. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
  208. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
  209. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
  210. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
  211. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
  212. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
  213. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
  214. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
  215. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
  216. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
  217. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
  218. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
  219. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
  220. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
  221. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
  222. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
  223. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
  224. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
  225. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
  226. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
  227. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
  228. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
  229. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
  230. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
  231. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
  232. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
  233. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
  234. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
  235. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
  236. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
  237. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
  238. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
  239. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/ours_spec.json +0 -0
  240. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/parity_crawl.py +0 -0
  241. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/parity_diff.json +0 -0
  242. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/real_world_cases.py +0 -0
  243. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/round_trip_tests.py +0 -0
  244. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/runner.py +0 -0
  245. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/stock_spec.json +0 -0
  246. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
  247. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/parity/README.md +0 -0
  248. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/parity/__init__.py +0 -0
  249. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/parity/baseline_gaps.json +0 -0
  250. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/parity/compare.py +0 -0
  251. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/parity/intentional_deviations.json +0 -0
  252. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/parity/introspect.py +0 -0
  253. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/parity/reports/GAP_ANALYSIS.md +0 -0
  254. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/parity/run_parity.py +0 -0
  255. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
  256. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/parity/test_parity_gap.py +0 -0
  257. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_batching_perf.py +0 -0
  258. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_block_not_found_error.py +0 -0
  259. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_buffer.py +0 -0
  260. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_cell_text_plain_fastpath.py +0 -0
  261. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_collapsed_range_format.py +0 -0
  262. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_command_dataclasses.py +0 -0
  263. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_commands.py +0 -0
  264. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_comments.py +0 -0
  265. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_document_create.py +0 -0
  266. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_http_transport.py +0 -0
  267. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_hyperlink_coalescing.py +0 -0
  268. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_insert_deferred.py +0 -0
  269. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_iter_inner_content.py +0 -0
  270. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_list_styles.py +0 -0
  271. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_merged_cells.py +0 -0
  272. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_oxml_shim.py +0 -0
  273. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_paragraph_text_len_cache.py +0 -0
  274. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_parity_round2.py +0 -0
  275. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_partial_failure_cascade.py +0 -0
  276. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_phase_a_behavior.py +0 -0
  277. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_phase_b_headers_footers.py +0 -0
  278. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_phase_c_tables.py +0 -0
  279. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_pr19766_review_fixes.py +0 -0
  280. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_ptc.py +0 -0
  281. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_python_docx_api_parity.py +0 -0
  282. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_revisions.py +0 -0
  283. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
  284. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_add_picture.py +0 -0
  285. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_add_run.py +0 -0
  286. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
  287. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
  288. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_comments_get.py +0 -0
  289. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_document_element.py +0 -0
  290. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_enum_section.py +0 -0
  291. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_font_audit.py +0 -0
  292. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_header_footer.py +0 -0
  293. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_hyperlink.py +0 -0
  294. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_inline_shape.py +0 -0
  295. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
  296. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_misc.py +0 -0
  297. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
  298. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_paragraph_style.py +0 -0
  299. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
  300. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_parfmt.py +0 -0
  301. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_row_col_cell.py +0 -0
  302. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_run_add_break.py +0 -0
  303. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
  304. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_run_style.py +0 -0
  305. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_run_style_strict.py +0 -0
  306. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_run_text.py +0 -0
  307. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_run_underline.py +0 -0
  308. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_section_audit.py +0 -0
  309. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_section_dimensions.py +0 -0
  310. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_section_onoff.py +0 -0
  311. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_settings.py +0 -0
  312. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_shared_audit.py +0 -0
  313. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_style.py +0 -0
  314. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_styles.py +0 -0
  315. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_table_audit.py +0 -0
  316. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_table_cell.py +0 -0
  317. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_table_dimensions.py +0 -0
  318. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_silent_stub_table_layout.py +0 -0
  319. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_smoke_integration.py +0 -0
  320. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_style_acceptance.py +0 -0
  321. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_style_font.py +0 -0
  322. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_style_setters_contract.py +0 -0
  323. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/tests/test_zod_wire_contract.py +0 -0
  324. {athena_python_docx-0.9.0 → athena_python_docx-0.10.0}/uv.lock +0 -0
@@ -88,13 +88,28 @@ not file-backed. Each is documented in the relevant docstring.
88
88
  hits `POST {base_url}/docs/empty`. The constructor positional-arg
89
89
  shape (`Document(asset_id)`) is preserved for parity.
90
90
 
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
91
+ - **`Document.asset_id: str`** read-only property the Athena asset
92
+ id this Document is bound to (`asset_<uuid>`). Agent-callable so
93
+ the typical pattern `doc = Document.create(...); print(doc.asset_id)`
94
+ works without reaching into `doc._session._asset_id`. Stock
95
+ python-docx has no analogue (it operates on local files), so this
96
+ is an Athena-only addition. Mirrors the parallel public accessors
97
+ on the other studios: `Workbook.workbook_id` (xlsx) and
98
+ `Presentation.deck_id` (pptx).
99
+
100
+ - **`Document.save(path_or_stream=None)`** — argument is optional and,
101
+ when supplied, raises
102
+ :class:`docx.errors.LocalSaveTargetNotSupportedError`. Stock
103
+ python-docx requires it and `TypeError`s on no-arg, but in an
93
104
  asset-backed SDK there is no local file to write — writes are always
94
105
  in-place against the Y.Doc. Forcing the upstream signature broke
95
106
  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.
107
+ near-universal Python pattern), so the no-arg form is supported.
108
+ When a path or stream IS supplied, the SDK cannot fulfill the
109
+ implied "write bytes to this target" contract, so the call raises
110
+ loudly rather than silently flushing — agents must use Olympus's
111
+ Export DOCX (which goes through the SuperDoc session that already
112
+ has the bytes) instead of expecting a local file.
98
113
 
99
114
  - **`Document.track_revisions: bool`** — when `True`, all subsequent
100
115
  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.0
4
4
  Summary: Drop-in replacement for python-docx that connects to Athena's Superdoc/Keryx collaborative document stack
5
5
  Project-URL: Homepage, https://athenaintelligence.ai
6
6
  Author-email: Athena Intelligence <engineering@athenaintelligence.ai>
@@ -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.0"
10
10
 
11
11
  from docx.api import Document
12
12
  from docx._buffer import flush_all
@@ -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,
@@ -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
@@ -140,6 +140,33 @@ class Insert(Command):
140
140
  content: dict[str, Any] | None = None
141
141
 
142
142
 
143
+ @dataclass
144
+ class TableSetCell(Command):
145
+ """Address a single table cell by ``(table_node_id, row, col)`` and insert
146
+ ``content`` into it. The applier resolves the cell's real nodeId
147
+ server-side (via one batch-wide ``tables.getCells`` call cached per
148
+ table), so the SDK doesn't need a prior ``tables.get_cells`` query —
149
+ each ``_Cell.text = …`` skips 1 HTTP round-trip and the whole table
150
+ flushes in one batch.
151
+
152
+ Buffered, not response-bearing. Placement defaults to ``insideEnd``
153
+ on the apps/api side (matches the prior ``_Cell.text`` setter
154
+ semantics: APPEND into the cell's existing paragraph, which produces
155
+ ``cell.text == value`` for a freshly created empty cell).
156
+
157
+ ``table_node_id`` may be a client-side UUID — the applier resolves
158
+ it through the per-batch ``clientIdMap`` set by the preceding
159
+ ``CreateTable``, so a freshly created table can be populated in the
160
+ same flush.
161
+ """
162
+
163
+ table_node_id: str
164
+ row_index: int
165
+ column_index: int
166
+ content: dict[str, Any]
167
+ placement: str | None = None
168
+
169
+
143
170
  @dataclass
144
171
  class Replace(Command):
145
172
  target: dict[str, Any]
@@ -899,6 +926,7 @@ __all__ = [
899
926
  "TablesSetCellProperties",
900
927
  "TablesSetColumnWidth",
901
928
  "TablesSetRowHeight",
929
+ "TableSetCell",
902
930
  # Images (mutations)
903
931
  "SetImageSize",
904
932
  # Queries
@@ -17,13 +17,16 @@ from __future__ import annotations
17
17
  import base64
18
18
  import io
19
19
  import sys
20
- import warnings
21
20
  from contextlib import contextmanager
22
21
  from typing import TYPE_CHECKING, BinaryIO, Generator, Iterator
23
22
 
24
23
  from docx._batching import run_sync
25
24
  from docx.client import Session
26
- from docx.errors import DocumentClosedError, ValidationError
25
+ from docx.errors import (
26
+ DocumentClosedError,
27
+ LocalSaveTargetNotSupportedError,
28
+ ValidationError,
29
+ )
27
30
 
28
31
  # python-docx re-exports a subset of symbols at docx.document; mirror those
29
32
  # so `from docx.document import Emu` etc. works.
@@ -121,20 +124,6 @@ def _classify_list_style(style_id: str | None) -> tuple[str, int] | None:
121
124
  return (kind, level)
122
125
 
123
126
 
124
- class IgnoredSaveTargetWarning(UserWarning):
125
- """Emitted when ``Document.save(path_or_stream=...)`` is called with a
126
- non-None argument.
127
-
128
- The SDK is asset-backed: edits are flushed in place against the
129
- Y.Doc, and the ``path_or_stream`` parameter is accepted only for
130
- parity-friendly call sites (see :meth:`Document.save`). A caller
131
- passing a path or stream almost certainly expects a local-file
132
- export, which this SDK cannot perform. Surfacing a warning makes
133
- the silent gap visible without breaking agent-generated code that
134
- reflexively calls ``doc.save()`` with no args.
135
- """
136
-
137
-
138
127
  class Document:
139
128
  """A Word document backed by a Superdoc/Keryx Y.Doc.
140
129
 
@@ -258,6 +247,20 @@ class Document:
258
247
 
259
248
  # ---- Public properties ----
260
249
 
250
+ @property
251
+ def asset_id(self) -> str:
252
+ """Athena asset id this Document is bound to (``asset_<uuid>``).
253
+
254
+ Agent-callable accessor — use this to capture the id of a
255
+ ``Document.create(...)`` so subsequent ``Document(asset_id)``
256
+ opens can target the same document in later tool calls.
257
+
258
+ DEVIATION FROM python-docx: stock python-docx has no analogue
259
+ because it operates on local files, not assets. Documented in
260
+ ``docs/API_PARITY_EXCEPTIONS.md``.
261
+ """
262
+ return self._session.asset_id
263
+
261
264
  @property
262
265
  def paragraphs(self) -> list["Paragraph"]:
263
266
  """Return all paragraphs in document order.
@@ -939,7 +942,12 @@ class Document:
939
942
  ),
940
943
  )
941
944
 
942
- tbl = Table(session=self._session, node_id=node_id)
945
+ tbl = Table(
946
+ session=self._session,
947
+ node_id=node_id,
948
+ rows=rows,
949
+ columns=cols,
950
+ )
943
951
  self._register_proxy_id(client_node_id, tbl)
944
952
  return tbl
945
953
 
@@ -1108,30 +1116,28 @@ class Document:
1108
1116
  optional. Stock python-docx requires it and raises ``TypeError``
1109
1117
  on ``save()`` no-arg, but in this asset-backed SDK there is no
1110
1118
  local file to write to — writes are always in-place against the
1111
- Y.Doc. Agent-generated code reflexively calls ``doc.save()`` and
1112
- forcing the upstream signature broke every such invocation
1113
- (`Untitled Word Document` session
1114
- ``thread_3f6b59e0-ae25-4f6e-8391-c286b17a3334``). The argument
1115
- is accepted for parity-friendly call sites but ignored at
1116
- runtime. To export to a local file, use the Olympus UI's
1117
- Export DOCX.
1118
-
1119
- When ``path_or_stream`` is non-None, an
1120
- :class:`IgnoredSaveTargetWarning` is emitted so the silent gap
1121
- is visible to callers. The flush still happens the call does
1122
- not raise but the user-supplied path/stream is not written
1123
- to. Previously a caller passing a string path got a fully
1124
- silent success and no file on disk.
1119
+ Y.Doc. Agent-generated code reflexively calls ``doc.save()``;
1120
+ forcing the upstream signature broke common agent call patterns,
1121
+ so no-arg ``doc.save()`` is supported.
1122
+
1123
+ When ``path_or_stream`` IS supplied (e.g. ``doc.save("./out.docx")``
1124
+ or ``doc.save(BytesIO())``), this raises
1125
+ :class:`docx.errors.LocalSaveTargetNotSupportedError`. The SDK
1126
+ cannot fulfill the implied contract of "write bytes to this
1127
+ target"; previously the call emitted a warning and silently
1128
+ flushed, leaving callers with no file on disk and no error.
1129
+ Raising forces the caller to address the gap explicitlyuse
1130
+ Olympus's Export DOCX (which goes through the SuperDoc session
1131
+ that already has the bytes).
1125
1132
  """
1126
1133
  if path_or_stream is not None:
1127
- warnings.warn(
1128
- "Document.save(path_or_stream=...) ignores its argument "
1129
- "athena-python-docx is asset-backed and cannot write to a "
1130
- "local file or stream. The flush completed in-place against "
1131
- "the Y.Doc; no bytes were written to "
1132
- f"{path_or_stream!r}. To export, use Olympus's Export DOCX.",
1133
- IgnoredSaveTargetWarning,
1134
- stacklevel=2,
1134
+ raise LocalSaveTargetNotSupportedError(
1135
+ "Document.save(path_or_stream=...) cannot write to a "
1136
+ "local file or stream — athena-python-docx is "
1137
+ "asset-backed and edits flush in place against the "
1138
+ f"Y.Doc. Got {path_or_stream!r}. Call doc.save() with "
1139
+ "no arguments to flush, or use Olympus's Export DOCX "
1140
+ "to obtain a .docx file."
1135
1141
  )
1136
1142
  self._flush()
1137
1143
 
@@ -31,10 +31,24 @@ class DocxError(Exception):
31
31
  :meth:`with_partial_applied` factory helper at raise sites instead.
32
32
  """
33
33
 
34
- # Class-level default ``except DocxError as exc`` handlers can
35
- # read ``exc.partial_applied`` without ``hasattr`` even on errors
36
- # raised the legacy way (no ``with_partial_applied`` call).
37
- partial_applied: list[dict] = []
34
+ def __getattr__(self, name: str) -> list[dict]:
35
+ """Lazy default for ``partial_applied`` returns an empty list
36
+ when the attribute hasn't been set via :meth:`with_partial_applied`.
37
+
38
+ Using ``__getattr__`` (only called when ordinary attribute
39
+ lookup misses) instead of a class-level mutable default avoids
40
+ the footgun where ``exc.partial_applied.append(x)`` on a
41
+ legacy-raised error would mutate a shared list and make every
42
+ subsequent ``DocxError`` look partially applied. Each call here
43
+ returns a fresh empty list; mutating it has no cross-instance
44
+ effect.
45
+ """
46
+ if name == "partial_applied":
47
+ return []
48
+ # Match Python's default ``AttributeError`` for unknown attrs.
49
+ raise AttributeError(
50
+ f"{type(self).__name__!r} object has no attribute {name!r}",
51
+ )
38
52
 
39
53
  def with_partial_applied(
40
54
  self,
@@ -48,6 +62,8 @@ class DocxError(Exception):
48
62
  raise DocxError("...").with_partial_applied(prefix)
49
63
  """
50
64
  if applied:
65
+ # Writes to ``self.__dict__`` — subsequent reads bypass
66
+ # ``__getattr__`` entirely and see the per-instance list.
51
67
  self.partial_applied = list(applied)
52
68
  return self
53
69
 
@@ -71,6 +87,23 @@ class DocumentClosedError(DocxError):
71
87
  """Raised when operating on a closed Document."""
72
88
 
73
89
 
90
+ class LocalSaveTargetNotSupportedError(DocxError, NotImplementedError):
91
+ """Raised when ``Document.save(path_or_stream=...)`` is called with a
92
+ non-None argument.
93
+
94
+ The SDK is asset-backed: edits flush in place against the Y.Doc, so
95
+ there is no local file or stream to write to. The bare ``doc.save()``
96
+ no-arg form remains supported as the documented deviation from stock
97
+ python-docx (see :meth:`docx.document.Document.save`). To get a
98
+ ``.docx`` file out, use Olympus's Export DOCX action — the SuperDoc
99
+ session already has the bytes the caller wanted.
100
+
101
+ Subclasses :class:`NotImplementedError` so a generic
102
+ ``except NotImplementedError`` catches it without importing the
103
+ typed class.
104
+ """
105
+
106
+
74
107
  class ValidationError(DocxError):
75
108
  """Raised when SDK-level validation fails (bad args, out-of-range indices)."""
76
109
 
@@ -234,9 +234,26 @@ def _collect_block_ids(
234
234
 
235
235
 
236
236
  class Table:
237
- def __init__(self, *, session: "Session", node_id: str) -> None:
237
+ def __init__(
238
+ self,
239
+ *,
240
+ session: "Session",
241
+ node_id: str,
242
+ rows: int | None = None,
243
+ columns: int | None = None,
244
+ ) -> None:
238
245
  self._session: "Session" = session
239
246
  self._node_id: str = node_id
247
+ # Locally-cached grid dimensions. Populated by
248
+ # ``Document.add_table`` (which knows the rows/cols up-front)
249
+ # so ``Table.cell()`` can validate bounds without round-tripping
250
+ # to ``tables.get``. Stays ``None`` for Tables resolved later
251
+ # via ``find`` / iteration; ``cell()`` falls back to
252
+ # ``_fresh_node_info()`` in that case. ``tables.insert_row`` /
253
+ # ``tables.insert_column`` invalidate this cache so the next
254
+ # access re-fetches authoritative counts.
255
+ self._cached_rows: int | None = rows
256
+ self._cached_cols: int | None = columns
240
257
 
241
258
  def _fresh_node_info(self) -> tuple[str, dict]:
242
259
  """Resolve the live nodeId and return ``(node_id, info_dict)``.
@@ -332,21 +349,35 @@ class Table:
332
349
 
333
350
  Negative indices are accepted Python-style (``-1`` is the last
334
351
  row/column).
352
+
353
+ Fast path: when the Table proxy was constructed with known
354
+ dimensions (``Document.add_table`` and ``Table.insert_row`` /
355
+ ``insert_column`` keep this cache current), bounds-check
356
+ against the local cache without round-tripping to
357
+ ``tables.get``. This is what keeps a 30-cell write loop at
358
+ zero query-shaped HTTP calls — pre-cache this was one
359
+ ``tables.get`` per ``cell()`` access.
335
360
  """
336
- _, info_dict = self._fresh_node_info()
337
- row_count: int = int(
338
- info_dict.get("rows")
339
- or info_dict.get("rowCount")
340
- or (info_dict.get("table", {}) or {}).get("rows")
341
- or 0,
342
- )
343
- col_count: int = int(
344
- info_dict.get("columns")
345
- or info_dict.get("cols")
346
- or info_dict.get("columnCount")
347
- or (info_dict.get("table", {}) or {}).get("columns")
348
- or 0,
349
- )
361
+ row_count: int
362
+ col_count: int
363
+ if self._cached_rows is not None and self._cached_cols is not None:
364
+ row_count = self._cached_rows
365
+ col_count = self._cached_cols
366
+ else:
367
+ _, info_dict = self._fresh_node_info()
368
+ row_count = int(
369
+ info_dict.get("rows")
370
+ or info_dict.get("rowCount")
371
+ or (info_dict.get("table", {}) or {}).get("rows")
372
+ or 0,
373
+ )
374
+ col_count = int(
375
+ info_dict.get("columns")
376
+ or info_dict.get("cols")
377
+ or info_dict.get("columnCount")
378
+ or (info_dict.get("table", {}) or {}).get("columns")
379
+ or 0,
380
+ )
350
381
  # Normalize negative indices the same way list[] does.
351
382
  if row_idx < 0:
352
383
  row_idx += row_count
@@ -399,6 +430,10 @@ class Table:
399
430
  },
400
431
  ),
401
432
  )
433
+ # Keep the bounds-check cache current so the next ``cell()``
434
+ # access still hits the fast path.
435
+ if self._cached_rows is not None:
436
+ self._cached_rows += 1
402
437
  return _Row(table=self, index=row_count)
403
438
 
404
439
  def add_column(self, width: "Length") -> "_Column":
@@ -432,6 +467,9 @@ class Table:
432
467
  },
433
468
  ),
434
469
  )
470
+ # Keep the bounds-check cache current.
471
+ if self._cached_cols is not None:
472
+ self._cached_cols += 1
435
473
  new_col = _Column(table=self, index=col_count)
436
474
  new_col.width = width
437
475
  return new_col
@@ -1009,10 +1047,10 @@ class _Cell:
1009
1047
 
1010
1048
  The cell's single inner paragraph is addressed indirectly — Superdoc
1011
1049
  doesn't expose a paragraph ref that's usable as a `blockId` for text
1012
- selections. Instead we use `doc.insert` with a structural paragraph
1013
- fragment at `placement=insideEnd`, which APPENDS inline runs to the
1014
- cell's existing paragraph. For a freshly-created (empty) cell this
1015
- produces `cell.text == value` on read-back.
1050
+ selections. The apps/api applier wraps a `tables.getCells` lookup +
1051
+ a `doc.insert` with placement=insideEnd, which APPENDS inline runs
1052
+ to the cell's existing paragraph. For a freshly-created (empty)
1053
+ cell this produces `cell.text == value` on read-back.
1016
1054
 
1017
1055
  For cells that already contain text, callers who truly want "replace"
1018
1056
  semantics should first resolve the cell's paragraph via `doc.find`
@@ -1028,34 +1066,27 @@ class _Cell:
1028
1066
  raise TypeError(
1029
1067
  f"_Cell.text must be a str; got {type(value).__name__}",
1030
1068
  )
1031
- cell_id = self._cell_id()
1032
- session = self._table._session
1033
- cell_target: dict = {
1034
- "kind": "block",
1035
- "nodeType": "tableCell",
1036
- "nodeId": cell_id,
1037
- }
1038
- # Superdoc only accepts block-typed fragments at the top level
1039
- # (paragraph/heading/table/image/list/sectionBreak/sdt/tableOfContents).
1040
- # We need to materialize ``value`` into a
1041
- # ``{kind:"paragraph", paragraph:{inlines:[...]}}`` fragment.
1069
+ # As of 0.10.0 the setter buffers a ``TableSetCell`` command
1070
+ # addressed by ``(table_node_id, row, col)`` and skips both
1071
+ # eager queries that used to fire per cell — the
1072
+ # ``Table._fresh_node_info()`` call (``tables.get`` query)
1073
+ # AND the ``_cell_info()`` call (``tables.get_cells`` query).
1074
+ # The apps/api applier resolves the cell nodeId server-side
1075
+ # against a per-batch cache so N cell writes against one table
1076
+ # share one ``tables.getCells`` call.
1042
1077
  #
1043
- # As of 0.8.0 we build the fragment locally when ``value`` has no
1044
- # markdown-special characters that's the dominant cell-content
1045
- # case in practice (numeric cells, plain labels) and skipping
1046
- # the ``doc.markdownToFragment`` query removes one eager HTTP
1047
- # round-trip per ``cell.text = …``. Combined with
1048
- # ``Insert`` no longer being response-bearing (0.8.0), a 30-cell
1049
- # table assignment now ships in a single ``doc.insert`` batch
1050
- # instead of 60 sequential round-trips. See
1078
+ # Pre-0.10.0 a 30-cell table assignment was ~60 HTTP round-
1079
+ # trips (one ``tables.get`` + one ``tables.get_cells`` per
1080
+ # cell); post-0.10.0 it's a single ``POST /commands`` batch
1081
+ # containing the 30 TableSetCell commands plus whatever else
1082
+ # was already buffered. See
1051
1083
  # ``docx-studio/PERFORMANCE_BATCHING_ANALYSIS.md``.
1052
1084
  #
1053
- # Confirmed against real staging Superdoc. This is the ONLY shape
1054
- # that actually lands text inside a tableCell:
1055
- # - text mode + placement → rejected ("placement only valid
1056
- # with structural content")
1057
- # - doc.replace + tableCell target → replaces the cell itself,
1058
- # destroying the table structure
1085
+ # For plain text we build the fragment client-side. For
1086
+ # markdown content we still need one eager
1087
+ # ``markdown_to_fragment`` query (the parsed fragment isn't
1088
+ # something the apps/api applier can build for us yet).
1089
+ session = self._table._session
1059
1090
  try:
1060
1091
  fragment: dict
1061
1092
  if _is_plain_text(value):
@@ -1092,11 +1123,22 @@ class _Cell:
1092
1123
  ],
1093
1124
  },
1094
1125
  }
1126
+ # ``self._table._node_id`` is the raw client-or-server id
1127
+ # the Table proxy currently holds. The applier resolves it
1128
+ # through the per-batch clientIdMap so a freshly-created
1129
+ # table (still on its client UUID locally) routes to the
1130
+ # real id without forcing a flush here.
1131
+ #
1132
+ # Routed via the standard path-proxy (``tables.set_cell`` →
1133
+ # ``TableSetCell`` per ``_OP_TO_COMMAND``) so test fakes
1134
+ # that intercept at the namespace level see the call the
1135
+ # same way they see every other op.
1095
1136
  run_sync(
1096
- session.doc.insert(
1137
+ session.doc.tables.set_cell(
1097
1138
  {
1098
- "target": cell_target,
1099
- "placement": "insideEnd",
1139
+ "tableNodeId": self._table._node_id,
1140
+ "rowIndex": self._row,
1141
+ "columnIndex": self._col,
1100
1142
  "content": fragment,
1101
1143
  },
1102
1144
  ),
@@ -1105,7 +1147,7 @@ class _Cell:
1105
1147
  except Exception as e:
1106
1148
  raise RuntimeError(
1107
1149
  f"Failed to set _Cell.text on cell ({self._row}, {self._col}) "
1108
- f"of table {self._table._fresh_node_id()}: {e!r}",
1150
+ f"of table {self._table._node_id}: {e!r}",
1109
1151
  ) from e
1110
1152
 
1111
1153
  @property
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "athena-python-docx"
7
- version = "0.9.0"
7
+ version = "0.10.0"
8
8
  description = "Drop-in replacement for python-docx that connects to Athena's Superdoc/Keryx collaborative document stack"
9
9
  readme = "README.md"
10
10
  license = "MIT"