athena-python-docx 0.6.1__tar.gz → 0.7.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 (306) hide show
  1. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/CLAUDE.md +21 -5
  2. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/PKG-INFO +1 -1
  3. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/__init__.py +1 -1
  4. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/_buffer.py +71 -107
  5. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/_http_doc.py +12 -3
  6. athena_python_docx-0.7.0/docx/_ptc.py +138 -0
  7. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/commands.py +78 -13
  8. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/document.py +260 -200
  9. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/settings.py +20 -20
  10. athena_python_docx-0.7.0/docx/text/hyperlink.py +167 -0
  11. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/text/paragraph.py +122 -21
  12. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/pyproject.toml +1 -1
  13. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/fake_session.py +43 -4
  14. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +2 -0
  15. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +6 -1
  16. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/ours_spec.json +1822 -289
  17. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/parity_crawl.py +26 -3
  18. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/parity_diff.json +87 -185
  19. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/stock_spec.json +16 -16
  20. athena_python_docx-0.7.0/tests/fidelity/test_e2e_against_staging.py +523 -0
  21. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/parity/baseline_gaps.json +1 -1
  22. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/parity/reports/GAP_ANALYSIS.md +1 -1
  23. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/parity/reports/gap_report.json +8 -2
  24. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/parity/snapshots/athena_latest.json +291 -16
  25. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_batching_perf.py +96 -126
  26. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_buffer.py +30 -13
  27. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_commands.py +12 -2
  28. athena_python_docx-0.7.0/tests/test_hyperlink_coalescing.py +199 -0
  29. athena_python_docx-0.7.0/tests/test_list_styles.py +226 -0
  30. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_ptc.py +55 -3
  31. athena_python_docx-0.7.0/tests/test_silent_stub_settings.py +75 -0
  32. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_wire_contract.py +21 -1
  33. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/uv.lock +1 -1
  34. athena_python_docx-0.6.1/docx/_ptc.py +0 -173
  35. athena_python_docx-0.6.1/docx/text/hyperlink.py +0 -123
  36. athena_python_docx-0.6.1/tests/test_silent_stub_settings.py +0 -68
  37. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/.gitignore +0 -0
  38. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/README.md +0 -0
  39. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/_batching.py +0 -0
  40. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/_http.py +0 -0
  41. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/_image_utils.py +0 -0
  42. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/api.py +0 -0
  43. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/client.py +0 -0
  44. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/comments.py +0 -0
  45. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/enum/__init__.py +0 -0
  46. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/enum/section.py +0 -0
  47. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/enum/style.py +0 -0
  48. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/enum/table.py +0 -0
  49. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/enum/text.py +0 -0
  50. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/errors.py +0 -0
  51. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/exceptions.py +0 -0
  52. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/opc/__init__.py +0 -0
  53. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/opc/coreprops.py +0 -0
  54. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/revisions.py +0 -0
  55. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/section.py +0 -0
  56. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/shape.py +0 -0
  57. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/shared.py +0 -0
  58. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/styles/__init__.py +0 -0
  59. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/styles/style.py +0 -0
  60. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/styles/styles.py +0 -0
  61. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/table.py +0 -0
  62. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/text/__init__.py +0 -0
  63. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/text/font.py +0 -0
  64. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/text/pagebreak.py +0 -0
  65. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/text/parfmt.py +0 -0
  66. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/text/run.py +0 -0
  67. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/text/tabstops.py +0 -0
  68. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/docx/typing.py +0 -0
  69. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/scripts/publish.sh +0 -0
  70. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/scripts/release.sh +0 -0
  71. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/scripts/round_trip_smoke.py +0 -0
  72. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/__init__.py +0 -0
  73. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/conftest.py +0 -0
  74. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/METHODOLOGY.md +0 -0
  75. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/README.md +0 -0
  76. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/__init__.py +0 -0
  77. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/ab_probe_cases.py +0 -0
  78. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/ab_probe_runner.py +0 -0
  79. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/auto_gen_cases.py +0 -0
  80. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/binary_round_trip.py +0 -0
  81. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/cases.py +0 -0
  82. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/complex_cases.py +0 -0
  83. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/coverage_report.py +0 -0
  84. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/extract.py +0 -0
  85. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/extreme_cases.py +0 -0
  86. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/local_runner.py +0 -0
  87. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/mega_cases.py +0 -0
  88. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshot.py +0 -0
  89. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
  90. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
  91. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
  92. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
  93. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
  94. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
  95. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
  96. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
  97. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
  98. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
  99. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
  100. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
  101. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/103_cell_tables_enumeration.json +0 -0
  102. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
  103. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
  104. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
  105. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
  106. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
  107. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
  108. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
  109. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
  110. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
  111. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
  112. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
  113. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
  114. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
  115. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
  116. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
  117. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
  118. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
  119. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/23_nested_table.json +0 -0
  120. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
  121. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
  122. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
  123. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
  124. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
  125. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
  126. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
  127. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
  128. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
  129. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
  130. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
  131. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
  132. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
  133. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
  134. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
  135. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
  136. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
  137. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
  138. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
  139. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
  140. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
  141. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
  142. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
  143. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
  144. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
  145. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
  146. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
  147. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/51_nested_tables_deep.json +0 -0
  148. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
  149. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
  150. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
  151. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
  152. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
  153. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
  154. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
  155. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
  156. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
  157. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
  158. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
  159. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
  160. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
  161. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
  162. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
  163. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
  164. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
  165. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
  166. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
  167. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
  168. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
  169. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
  170. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
  171. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
  172. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
  173. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
  174. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
  175. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
  176. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
  177. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
  178. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
  179. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
  180. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
  181. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
  182. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
  183. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
  184. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
  185. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
  186. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
  187. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
  188. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
  189. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
  190. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
  191. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
  192. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
  193. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
  194. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
  195. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
  196. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex01_five_levels_deep_tables.json +0 -0
  197. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
  198. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
  199. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
  200. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
  201. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
  202. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
  203. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
  204. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
  205. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
  206. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
  207. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
  208. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
  209. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
  210. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
  211. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
  212. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
  213. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
  214. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
  215. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
  216. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
  217. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
  218. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
  219. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
  220. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
  221. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
  222. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
  223. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
  224. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
  225. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
  226. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
  227. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
  228. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
  229. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
  230. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
  231. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
  232. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
  233. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
  234. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
  235. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
  236. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
  237. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw14_nested_cell_table.json +0 -0
  238. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
  239. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/real_world_cases.py +0 -0
  240. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/round_trip_tests.py +0 -0
  241. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/fidelity/runner.py +0 -0
  242. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/parity/README.md +0 -0
  243. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/parity/__init__.py +0 -0
  244. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/parity/compare.py +0 -0
  245. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/parity/intentional_deviations.json +0 -0
  246. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/parity/introspect.py +0 -0
  247. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/parity/run_parity.py +0 -0
  248. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
  249. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/parity/test_parity_gap.py +0 -0
  250. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_collapsed_range_format.py +0 -0
  251. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_command_dataclasses.py +0 -0
  252. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_comments.py +0 -0
  253. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_document_create.py +0 -0
  254. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_http_transport.py +0 -0
  255. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_iter_inner_content.py +0 -0
  256. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_merged_cells.py +0 -0
  257. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_paragraph_text_len_cache.py +0 -0
  258. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_parity_misc.py +0 -0
  259. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_parity_round2.py +0 -0
  260. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_phase_a_behavior.py +0 -0
  261. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_phase_b_headers_footers.py +0 -0
  262. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_phase_c_tables.py +0 -0
  263. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_pr19766_review_fixes.py +0 -0
  264. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_python_docx_api_parity.py +0 -0
  265. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_revisions.py +0 -0
  266. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
  267. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_add_picture.py +0 -0
  268. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_add_run.py +0 -0
  269. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
  270. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
  271. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_comments_get.py +0 -0
  272. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_document_audit.py +0 -0
  273. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_document_element.py +0 -0
  274. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_enum_section.py +0 -0
  275. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_font_audit.py +0 -0
  276. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_header_footer.py +0 -0
  277. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_hyperlink.py +0 -0
  278. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_inline_shape.py +0 -0
  279. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
  280. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_misc.py +0 -0
  281. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
  282. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_paragraph_style.py +0 -0
  283. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
  284. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_parfmt.py +0 -0
  285. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_row_col_cell.py +0 -0
  286. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_run_add_break.py +0 -0
  287. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
  288. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_run_style.py +0 -0
  289. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_run_style_strict.py +0 -0
  290. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_run_text.py +0 -0
  291. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_run_underline.py +0 -0
  292. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_section_audit.py +0 -0
  293. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_section_dimensions.py +0 -0
  294. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_section_onoff.py +0 -0
  295. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_shared_audit.py +0 -0
  296. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_style.py +0 -0
  297. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_styles.py +0 -0
  298. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_table_audit.py +0 -0
  299. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_table_cell.py +0 -0
  300. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_table_dimensions.py +0 -0
  301. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_silent_stub_table_layout.py +0 -0
  302. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_smoke_integration.py +0 -0
  303. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_style_acceptance.py +0 -0
  304. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_style_font.py +0 -0
  305. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_style_setters_contract.py +0 -0
  306. {athena_python_docx-0.6.1 → athena_python_docx-0.7.0}/tests/test_zod_wire_contract.py +0 -0
@@ -135,11 +135,27 @@ This is a **thin HTTP client** that mimics the sync python-docx API.
135
135
  backoff, 429/502/503/504). The legacy `transport="direct"` (embedded
136
136
  Superdoc CLI + y-websocket from Python) was removed in 0.5.0; agent
137
137
  pods no longer embed Superdoc.
138
- - Batching: queries and response-bearing creates flush eagerly (and
139
- drain pending pure mutations in the same batch); pure mutations
140
- buffer with a 100 ms idle timer. `Document.save()` and the context
141
- manager exit drain explicitly. `docx.flush_all()` is the Daytona
142
- prelude hook flushes every live buffer in the process.
138
+ - **Transparent batching (0.7.0+):** Create* commands
139
+ (`CreateParagraph`, `CreateHeading`, `CreateTable`, `CreateImage`)
140
+ ALWAYS pre-mint a client-side UUID (`client_node_id`) and defer
141
+ through the buffer. The applier resolves UUIDs to real SuperDoc
142
+ ids via per-batch `clientIdMap` on flush, and the buffer rewrites
143
+ each proxy's `_node_id` in-place. Net result: 30 `add_paragraph`
144
+ calls = 1 HTTP request, with no explicit `batch()` context manager
145
+ in the public API (1:1 python-docx parity).
146
+ - Flush triggers: queries (`BlocksList`, `Find`, `GetNodeById`, …)
147
+ drain pending ops before running; the 100 ms idle timer fires on
148
+ inactivity so collab updates stream live; `Document.save()` and
149
+ the context-manager exit drain explicitly; `docx.flush_all()` is
150
+ the Daytona-prelude hook that flushes every live buffer in the
151
+ process.
152
+ - Other response-bearing ops without a client id (`Insert`,
153
+ `ListsCreate`, `ListsAttach`, `CommentsCreate`, `CommentsPatch`,
154
+ `TrackChangesDecide`) keep their eager-flush semantics so callers
155
+ reading back ids still see real data. A `Document.add_paragraph`
156
+ with `style="List Bullet"` flushes the queued CreateParagraph in
157
+ the same batch as the follow-up `ListsCreate` — still one HTTP
158
+ request per logical operation.
143
159
  - The path-proxy in `_http_doc.py` is an internal translation layer:
144
160
  call sites that look like `await self._session.doc.create.paragraph(p)`
145
161
  resolve to `CommandBuffer.call(CreateParagraph(**p))`. Rewriting call
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: athena-python-docx
3
- Version: 0.6.1
3
+ Version: 0.7.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.6.1"
9
+ __version__ = "0.7.0"
10
10
 
11
11
  from docx.api import Document
12
12
  from docx._buffer import flush_all
@@ -1,9 +1,17 @@
1
1
  """HTTP command buffer for the docx-studio SDK.
2
2
 
3
3
  The buffer sits between SDK call sites and the HTTP transport. Every SDK
4
- mutation routes through it; we flush eagerly for queries and response-bearing
5
- ops (creates), and idle-batch pure mutations so a burst of setters becomes
6
- one HTTP request instead of N.
4
+ mutation routes through it; we flush eagerly for queries, and idle-batch
5
+ all other ops including Create* so a burst of ``add_paragraph`` calls
6
+ ships as one HTTP request instead of N.
7
+
8
+ Create* commands are deferred because the SDK pre-mints a client-side
9
+ UUID for every new node (see ``Document._mint_client_node_id``). The
10
+ proxy returned to the caller is stamped with that UUID; the applier
11
+ resolves UUIDs to real SuperDoc ids via ``clientIdMap`` and echoes the
12
+ mapping back, at which point the buffer rewrites each proxy's
13
+ ``_node_id``. This makes batching invisible at the call site — user
14
+ code looks like python-docx, runs at python-docx-batched speed.
7
15
 
8
16
  Concurrency model: the SDK serializes calls through ``_batching.run_sync``
9
17
  (one persistent event-loop thread), so the buffer's primary thread is the
@@ -23,11 +31,10 @@ from __future__ import annotations
23
31
  import sys
24
32
  import threading
25
33
  import weakref
26
- from contextlib import contextmanager
27
- from typing import TYPE_CHECKING, Any, Generator
34
+ from typing import TYPE_CHECKING, Any
28
35
 
29
36
  from docx import _ptc
30
- from docx.commands import Command, must_flush_immediately
37
+ from docx.commands import Command, is_response_bearing, must_flush_immediately
31
38
 
32
39
  if TYPE_CHECKING:
33
40
  from docx._http_doc import HttpClient
@@ -125,12 +132,17 @@ class CommandBuffer:
125
132
  """Buffers commands for one Document/asset.
126
133
 
127
134
  Behaviour:
128
- - Queries and response-bearing mutations (creates, inserts that return
129
- ids) flush immediately. Pending pure mutations are flushed in the
130
- same batch so ordering is preserved.
131
- - Pure mutations (formatters, setters) are queued. An idle timer flushes
132
- them after :data:`DEFAULT_AUTO_FLUSH_SECONDS` of inactivity, OR on
133
- the next eager call, OR on explicit ``flush()``.
135
+ - Queries (BlocksList, Find, GetNodeById, …) flush immediately and
136
+ drain any pending mutations in the same batch so ordering is preserved.
137
+ - Every other op formatters, setters, AND Create* — queues. The idle
138
+ timer flushes after :data:`DEFAULT_AUTO_FLUSH_SECONDS` of inactivity,
139
+ the next eager (query) call drains, or explicit ``flush()`` drains.
140
+
141
+ Create* commands defer because the SDK pre-mints a client-side UUID
142
+ (``client_node_id`` / ``client_entity_id``); the applier translates
143
+ the UUID to a real SuperDoc id in a per-batch ``clientIdMap`` and
144
+ echoes the mapping back so the SDK can rewrite each proxy's
145
+ ``_node_id`` on flush.
134
146
  """
135
147
 
136
148
  def __init__(
@@ -154,11 +166,6 @@ class CommandBuffer:
154
166
  # original semantics.
155
167
  self._change_mode: str | None = None
156
168
  self._user: dict[str, str] | None = None
157
- # Batch mode flag — when True, Create* commands buffer instead
158
- # of flushing immediately, and proxies bind to a client-side
159
- # UUID instead of waiting for the server response. See
160
- # :meth:`Document.batch` and ``PERFORMANCE_BATCHING_ANALYSIS.md``.
161
- self._batch_depth: int = 0
162
169
  # client_node_id → list of (proxy, attr) pairs to update with the
163
170
  # real nodeId after flush. Populated by ``add_paragraph`` /
164
171
  # ``add_heading`` / etc. when they queue a Create with a
@@ -168,22 +175,11 @@ class CommandBuffer:
168
175
  ] = {}
169
176
  _register(self)
170
177
 
171
- @property
172
- def is_batching(self) -> bool:
173
- """``True`` if a ``with doc.batch():`` block is currently open.
174
-
175
- SDK call sites read this to decide whether to defer Create*
176
- commands or eager-flush them. Reentrant — nested ``batch()``
177
- blocks increment the counter and decrement on exit; the outer
178
- block is what actually flushes.
179
- """
180
- return self._batch_depth > 0
181
-
182
178
  def register_proxy_id_ref(
183
179
  self, client_id: str, proxy: object, attr: str = "_node_id"
184
180
  ) -> None:
185
181
  """Register that ``proxy.<attr>`` should be rewritten from
186
- ``client_id`` to the real node id once the current batch flushes.
182
+ ``client_id`` to the real node id once the queue flushes.
187
183
 
188
184
  Called by ``Document.add_paragraph`` (and equivalents) when they
189
185
  queue a Create with a ``client_node_id``. The flush loop walks
@@ -254,21 +250,19 @@ class CommandBuffer:
254
250
  def call(self, cmd: Command) -> Any:
255
251
  """Execute or buffer ``cmd``.
256
252
 
257
- For eager commands (queries, response-bearing creates), drains
258
- the pending queue and runs ``cmd`` in the same batch; returns
259
- the per-cmd result dict.
260
-
261
- For pure mutations, appends to the queue, resets the idle timer,
262
- and returns ``None``. The caller MUST NOT rely on the return
263
- value of a buffered mutation — it's not available until flush.
264
-
265
- **Batch mode:** when ``self.is_batching`` is True AND the command
266
- carries a non-None ``client_node_id`` / ``client_entity_id``
267
- (Create*, CommentsCreate), the command is buffered rather than
268
- eager-flushed. The caller is expected to have pre-stamped a
269
- proxy with the client-side id; after the batch flushes, the
270
- applier's ``real_node_id`` / ``real_entity_id`` echo lets the
271
- SDK rewrite the proxy in-place.
253
+ For queries (``BlocksList``, ``Find``, ``GetNodeById``, …)
254
+ AND response-bearing commands that don't carry a client-side
255
+ id, drains the pending queue and runs ``cmd`` in the same
256
+ batch; returns the per-cmd result dict.
257
+
258
+ Otherwise pure mutations (formatters, setters) AND Create*
259
+ commands that carry a pre-minted ``client_node_id`` /
260
+ ``client_entity_id`` — appends to the queue, resets the idle
261
+ timer, and returns ``None``. Caller MUST NOT rely on the return
262
+ value of a buffered mutation. For deferred Creates, the
263
+ applier resolves the client UUID to a real SuperDoc id via
264
+ ``clientIdMap`` on flush and the buffer rewrites each registered
265
+ proxy's ``_node_id`` in-place.
272
266
  """
273
267
  if self._closed:
274
268
  raise RuntimeError(
@@ -278,11 +272,15 @@ class CommandBuffer:
278
272
  # PTC begin: one event per user-facing method call, before any
279
273
  # batching. Failures here can't crash the user's code path.
280
274
  try:
281
- cmd._ptc_call_id = _ptc.emit_begin(type(cmd).__name__, cmd.to_dict()) # type: ignore[attr-defined]
275
+ cmd._ptc_call_id = _ptc.emit_begin( # type: ignore[attr-defined]
276
+ type(cmd).__name__,
277
+ cmd.to_dict(),
278
+ asset_id=self._asset_id,
279
+ )
282
280
  except Exception: # noqa: BLE001
283
281
  pass
284
282
 
285
- if must_flush_immediately(cmd) and not self._is_batched_create(cmd):
283
+ if must_flush_immediately(cmd) and not self._has_client_id(cmd):
286
284
  return self._eager_flush_with(cmd)
287
285
 
288
286
  with self._lock:
@@ -290,23 +288,25 @@ class CommandBuffer:
290
288
  self._reset_timer_locked()
291
289
  return None
292
290
 
293
- def _is_batched_create(self, cmd: Command) -> bool:
294
- """Return True iff this command can be safely deferred because
295
- the caller pre-assigned a client-side id and the buffer is in
296
- batch mode.
297
-
298
- We only defer Create-shape commands (those with
299
- ``client_node_id`` / ``client_entity_id`` set). Pure queries
300
- like ``BlocksList`` must still eager-flush the caller is
301
- awaiting their data. Read-bearing creates without a client id
302
- (legacy callers / non-batch path) also stay eager so we don't
303
- change their semantics.
291
+ @staticmethod
292
+ def _has_client_id(cmd: Command) -> bool:
293
+ """Return ``True`` iff this command carries a pre-minted
294
+ client-side id (``client_node_id`` for nodes,
295
+ ``client_entity_id`` for comments).
296
+
297
+ Used by :meth:`call` to decide whether a response-bearing
298
+ Create can defer. Pure response-bearing ops without a client
299
+ id (``Insert``, ``CommentsPatch``, ``TrackChangesDecide``,
300
+ legacy callers) keep their eager-flush semantics so callers
301
+ that read the response still see real data.
304
302
  """
305
- if not self.is_batching:
306
- return False
307
- cli_node = getattr(cmd, "client_node_id", None)
308
- cli_ent = getattr(cmd, "client_entity_id", None)
309
- return cli_node is not None or cli_ent is not None
303
+ return (
304
+ is_response_bearing(cmd)
305
+ and (
306
+ getattr(cmd, "client_node_id", None) is not None
307
+ or getattr(cmd, "client_entity_id", None) is not None
308
+ )
309
+ )
310
310
 
311
311
  def flush(self) -> list[Any]:
312
312
  """Flush pending commands as one HTTP batch.
@@ -317,9 +317,10 @@ class CommandBuffer:
317
317
  After the batch returns, walks per-cmd results for
318
318
  ``real_node_id`` / ``real_entity_id`` echoes and updates any
319
319
  proxies registered via :meth:`register_proxy_id_ref` with the
320
- resolved server ids. This is what completes the round-trip for
321
- ``with doc.batch():`` — the SDK's caller-facing proxies pick up
322
- real ids transparently after the block exits.
320
+ resolved server ids. This completes the round-trip for the
321
+ always-on transparent batching path — the SDK's caller-facing
322
+ proxies pick up real ids without the caller ever seeing the
323
+ UUID swap.
323
324
  """
324
325
  with self._lock:
325
326
  self._cancel_timer_locked()
@@ -371,43 +372,6 @@ class CommandBuffer:
371
372
  pass
372
373
  return results
373
374
 
374
- @contextmanager
375
- def batch(self) -> Generator[None, None, None]:
376
- """Group calls into one HTTP batch.
377
-
378
- Inside the ``with`` block:
379
-
380
- - Queries (``BlocksList``, ``Find``, ``GetNodeById``, …) still
381
- eager-flush — the caller is awaiting their result, so we have
382
- no choice.
383
- - Pure mutations (formatters, setters) accumulate without their
384
- idle timer firing.
385
- - Create*-shape commands that carry a ``client_node_id`` /
386
- ``client_entity_id`` (set by ``Document.add_paragraph`` and
387
- friends when they detect batch mode) also accumulate — the
388
- server resolves the client UUIDs to real SuperDoc ids in a
389
- single batch via the per-request ``clientIdMap``.
390
-
391
- On exit, drains anything left. Reentrant: nested ``batch()``
392
- blocks share the same accumulating queue; only the outermost
393
- block flushes.
394
- """
395
- with self._lock:
396
- self._cancel_timer_locked()
397
- old_window = self._auto_flush_seconds
398
- self._auto_flush_seconds = 0.0 # disable timer-scheduled flush
399
- self._batch_depth += 1
400
- try:
401
- yield
402
- finally:
403
- with self._lock:
404
- self._batch_depth -= 1
405
- outermost = self._batch_depth == 0
406
- if outermost:
407
- self._auto_flush_seconds = old_window
408
- if outermost:
409
- self.flush()
410
-
411
375
  def close(self) -> None:
412
376
  """Flush and disable. Idempotent."""
413
377
  if self._closed:
@@ -423,11 +387,11 @@ class CommandBuffer:
423
387
  def _eager_flush_with(self, cmd: Command) -> Any:
424
388
  """Drain pending + run ``cmd`` in one batch; return cmd's result.
425
389
 
426
- When pending commands include batched Creates with registered
427
- proxy refs (e.g. a query mid-batch triggers an eager flush
428
- before the ``with doc.batch():`` block ends), the proxies still
429
- get their real ids written back via the same flush-time rewrite
430
- that :meth:`flush` does.
390
+ When pending commands include Creates with registered proxy
391
+ refs (queries mid-stream cause this the caller is awaiting
392
+ their result and the queue must drain first), the proxies
393
+ still get their real ids written back via the same flush-time
394
+ rewrite that :meth:`flush` does.
431
395
  """
432
396
  with self._lock:
433
397
  self._cancel_timer_locked()
@@ -68,6 +68,8 @@ from docx.commands import (
68
68
  Insert,
69
69
  InsertLineBreak,
70
70
  InsertTab,
71
+ ListsAttach,
72
+ ListsCreate,
71
73
  ListsMerge,
72
74
  ListsSplit,
73
75
  MarkdownToFragment,
@@ -91,6 +93,7 @@ from docx.commands import (
91
93
  SetSectionPageMargins,
92
94
  SetSectionPageSetup,
93
95
  SetSectionTitlePage,
96
+ SetSectionsOddEvenHeadersFooters,
94
97
  TablesGet,
95
98
  TablesGetCells,
96
99
  TablesGetProperties,
@@ -463,6 +466,7 @@ _OP_TO_COMMAND: dict[str, type[Command]] = {
463
466
  "sections.set_header_footer_margins": SetSectionHeaderFooterMargins,
464
467
  "sections.set_break_type": SetSectionBreakType,
465
468
  "sections.set_title_page": SetSectionTitlePage,
469
+ "sections.set_odd_even_headers_footers": SetSectionsOddEvenHeadersFooters,
466
470
  "sections.set_link_to_previous": SetSectionLinkToPrevious,
467
471
  # Tables (mutations)
468
472
  "tables.insert_row": TablesInsertRow,
@@ -503,9 +507,14 @@ _OP_TO_COMMAND: dict[str, type[Command]] = {
503
507
  "header_footers.get": HeaderFootersGet,
504
508
  "header_footers.resolve": HeaderFootersResolve,
505
509
  "header_footers.refs.set_linked_to_previous": HeaderFootersRefsSetLinkedToPrevious,
506
- # Lists & Selection (SuperDoc 1.8.1 additions; not exposed on the
507
- # python-docx parity surface registered here for typed-bus
508
- # completeness when advanced callers reach for them).
510
+ # Lists & Selection. ``lists.create`` and ``lists.attach`` are
511
+ # consumed internally by ``Document.add_paragraph`` to make
512
+ # ``style="List Bullet" | "List Number"`` produce real list
513
+ # structure (not just a style attribute on a flat paragraph).
514
+ # ``lists.merge``/``lists.split`` stay typed-bus-only — no
515
+ # python-docx parity surface for them.
516
+ "lists.create": ListsCreate,
517
+ "lists.attach": ListsAttach,
509
518
  "lists.merge": ListsMerge,
510
519
  "lists.split": ListsSplit,
511
520
  "selection.current": SelectionCurrent,
@@ -0,0 +1,138 @@
1
+ """Programmatic Tool Calling (PTC) — client side.
2
+
3
+ Activated only when ``ATHENA_PTC_URL`` is set. The URL is a presigned
4
+ endpoint URL with an HMAC-signed token embedded as ``?t=…``; the token
5
+ carries the (thread, parent_tool_call_id, run) triple and an expiry.
6
+ The SDK doesn't need to know that triple — it just POSTs to the URL
7
+ verbatim, and the server derives identity from the token.
8
+
9
+ Without ``ATHENA_PTC_URL`` set, every call here is a no-op.
10
+
11
+ Emits are fire-and-forget on a background daemon thread:
12
+
13
+ - Never raise into user code.
14
+ - Never block the calling thread.
15
+ - Re-read ``ATHENA_PTC_URL`` on every emit so updates between sandbox
16
+ executions take effect immediately (the SDK module itself is cached
17
+ across runs).
18
+ - Snapshot the URL at ``emit_begin`` time and carry it to ``emit_end``
19
+ so late end events can't be misattributed if a new sandbox run
20
+ swapped in a different URL between begin and end.
21
+
22
+ This module is *intentionally byte-identical* across the docx / pptx /
23
+ xlsx SDKs — the three SDKs have no shared base. Duplicating ~100 LOC
24
+ of stdlib code is cheaper than spinning up a 4th release pipeline.
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import json
30
+ import os
31
+ import queue
32
+ import threading
33
+ import time
34
+ import urllib.error
35
+ import urllib.request
36
+ import uuid
37
+ from typing import Any
38
+
39
+ _MAX_QUEUE = 4096
40
+ _MAX_BODY = 64 * 1024
41
+ _HTTP_TIMEOUT = 2.0
42
+
43
+ # Singleton state. PTC has exactly one outbox per process.
44
+ _outbox: queue.Queue[tuple[str, dict[str, Any]]] = queue.Queue(maxsize=_MAX_QUEUE)
45
+ _thread_lock = threading.Lock()
46
+ _thread_started = False
47
+ # call_id -> URL snapshot at begin time; lets emit_end target the
48
+ # original run's endpoint even if ATHENA_PTC_URL changed since.
49
+ _call_url: dict[str, str] = {}
50
+
51
+
52
+ def _drain() -> None:
53
+ while True:
54
+ item = _outbox.get()
55
+ if item is None:
56
+ return
57
+ url, body = item
58
+ try:
59
+ raw = json.dumps(body).encode("utf-8")
60
+ if len(raw) > _MAX_BODY:
61
+ key = "args" if body.get("phase") == "begin" else "result"
62
+ body[key] = {"__truncated__": True}
63
+ raw = json.dumps(body).encode("utf-8")
64
+ req = urllib.request.Request(
65
+ url,
66
+ data=raw,
67
+ method="POST",
68
+ headers={"Content-Type": "application/json"},
69
+ )
70
+ urllib.request.urlopen(req, timeout=_HTTP_TIMEOUT).close()
71
+ except (urllib.error.URLError, OSError, ValueError):
72
+ pass # never propagate
73
+
74
+
75
+ def _ensure_thread() -> None:
76
+ global _thread_started
77
+ if _thread_started:
78
+ return
79
+ with _thread_lock:
80
+ if _thread_started:
81
+ return
82
+ threading.Thread(target=_drain, name="athena-ptc", daemon=True).start()
83
+ _thread_started = True
84
+
85
+
86
+ def _send(url: str, body: dict[str, Any]) -> None:
87
+ _ensure_thread()
88
+ try:
89
+ _outbox.put_nowait((url, body))
90
+ except queue.Full:
91
+ pass # drop on backpressure; never block user code
92
+
93
+
94
+ def emit_begin(
95
+ tool_name: str,
96
+ args: dict[str, Any],
97
+ *,
98
+ asset_id: str | None = None,
99
+ ) -> str:
100
+ call_id = uuid.uuid4().hex
101
+ url = os.environ.get("ATHENA_PTC_URL")
102
+ if not url:
103
+ return call_id
104
+ _call_url[call_id] = url
105
+ body: dict[str, Any] = {
106
+ "callId": call_id,
107
+ "toolName": tool_name,
108
+ "phase": "begin",
109
+ "args": args,
110
+ "ts": time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime()),
111
+ }
112
+ if asset_id is not None:
113
+ body["assetId"] = asset_id
114
+ _send(url, body)
115
+ return call_id
116
+
117
+
118
+ def emit_end(
119
+ *,
120
+ call_id: str,
121
+ tool_name: str,
122
+ result: dict[str, Any] | None,
123
+ is_error: bool,
124
+ ) -> None:
125
+ url = _call_url.pop(call_id, None)
126
+ if url is None:
127
+ return # PTC was disabled at begin, or begin wasn't emitted
128
+ _send(
129
+ url,
130
+ {
131
+ "callId": call_id,
132
+ "toolName": tool_name,
133
+ "phase": "end",
134
+ "result": result,
135
+ "isError": is_error,
136
+ "ts": time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime()),
137
+ },
138
+ )
@@ -59,17 +59,19 @@ class Command:
59
59
 
60
60
  # --- Create* commands carry an optional client-assigned node id ---
61
61
  #
62
- # When the SDK runs inside ``with doc.batch():`` (see
63
- # ``Document.batch()``) every Create* command pre-generates a UUID and
64
- # stamps the new Paragraph/Heading/Table/Image proxy with it. The
65
- # applier translates ``client_node_id`` real SuperDoc nodeId in a
66
- # per-batch map (see ``apps/api/src/commands/applier.ts``), so
67
- # subsequent commands in the same batch can target the new block by
68
- # its client-side id before the response has even returned. After
69
- # flush, the buffer reads the resolved ``real_node_id`` out of each
70
- # Create's response and updates the proxy. Outside batch mode the
71
- # field is left empty and Create* still flushes immediately
72
- # (response-bearing) to preserve the existing eager-id behavior.
62
+ # The SDK pre-generates a UUID for every new Paragraph/Heading/Table/
63
+ # Image and stamps the proxy with it. The applier translates
64
+ # ``client_node_id`` real SuperDoc nodeId in a per-batch map (see
65
+ # ``apps/api/src/commands/applier.ts``), so subsequent commands in the
66
+ # same batch can target the new block by its client-side id before the
67
+ # response has even returned. After flush, the buffer reads the
68
+ # resolved ``real_node_id`` out of each Create's response and updates
69
+ # the proxy in-place.
70
+ #
71
+ # Direct internal callers that bypass ``Document`` (legacy helpers,
72
+ # fidelity tests) may leave the field unset; the buffer then eager-
73
+ # flushes the Create immediately so callers that read the response
74
+ # still see real data.
73
75
  #
74
76
  # The field is ``client_node_id`` on the Python side and arrives at
75
77
  # the server as ``clientNodeId`` via ``_snake_to_camel`` — matches the
@@ -283,6 +285,23 @@ class SetSectionTitlePage(Command):
283
285
  enabled: bool
284
286
 
285
287
 
288
+ @dataclass
289
+ class SetSectionsOddEvenHeadersFooters(Command):
290
+ """Toggle the document-level ``<w:evenAndOddHeaders/>`` setting.
291
+
292
+ Despite living under the ``sections.*`` namespace on the SuperDoc
293
+ bound-doc API, this op carries no ``target`` — it sets a single
294
+ doc-level flag that applies to every section. Word stores the
295
+ underlying value on ``word/settings.xml`` as
296
+ ``<w:evenAndOddHeaders/>``, which is what python-docx's
297
+ ``Settings.odd_and_even_pages_header_footer`` writes to.
298
+
299
+ Available since SuperDoc 1.8.1 (``DocSectionsSetOddEvenHeadersFooters``).
300
+ """
301
+
302
+ enabled: bool
303
+
304
+
286
305
  @dataclass
287
306
  class SetSectionLinkToPrevious(Command):
288
307
  """Toggle the section-side ``linked-to-previous`` flag for one
@@ -532,8 +551,13 @@ class CommentsCreate(Command):
532
551
  text: str | None = None
533
552
  target: dict[str, Any] | None = None
534
553
  parent_id: str | None = None
535
- # Optional client-assigned entity id for true-batched CommentsCreate
536
- # inside ``with doc.batch():`` see Create* dataclass docstrings.
554
+ # Optional client-assigned entity id for transparent-batched
555
+ # CommentsCreate (parallel to ``client_node_id`` on Create*; the
556
+ # server applier resolves UUIDs via the same per-batch
557
+ # ``clientIdMap``). ``Comments.add_comment`` currently leaves this
558
+ # unset, so CommentsCreate eager-flushes — left in place for
559
+ # future callers that want to defer comment creates alongside
560
+ # other ops.
537
561
  client_entity_id: str | None = None
538
562
 
539
563
 
@@ -619,6 +643,40 @@ class HeaderFootersRefsSetLinkedToPrevious(Command):
619
643
  # every other op.
620
644
 
621
645
 
646
+ @dataclass
647
+ class ListsCreate(Command):
648
+ """Create a new list — either an empty list at an anchor, or by
649
+ converting an existing paragraph (range) into a list.
650
+
651
+ SuperDoc: ``doc.lists.create``.
652
+
653
+ - ``mode="empty"`` requires ``at`` (BlockAddress).
654
+ - ``mode="fromParagraphs"`` requires ``target`` (BlockAddressOrRange).
655
+ - ``kind`` is ``"bullet"`` or ``"ordered"``.
656
+ """
657
+
658
+ mode: str = "fromParagraphs"
659
+ at: dict[str, Any] | None = None
660
+ target: dict[str, Any] | None = None
661
+ kind: str | None = None
662
+ level: int | None = None
663
+ preset: str | None = None
664
+
665
+
666
+ @dataclass
667
+ class ListsAttach(Command):
668
+ """Attach an existing paragraph to an existing list as a new item.
669
+
670
+ SuperDoc: ``doc.lists.attach``. ``attach_to`` is the list item to
671
+ attach next to (defines list membership); ``target`` is the
672
+ paragraph being converted.
673
+ """
674
+
675
+ attach_to: dict[str, Any] = dataclasses.field(default_factory=dict)
676
+ target: dict[str, Any] = dataclasses.field(default_factory=dict)
677
+ level: int | None = None
678
+
679
+
622
680
  @dataclass
623
681
  class ListsMerge(Command):
624
682
  """Merge a list item with an adjacent list / list item.
@@ -768,6 +826,12 @@ _RESPONSE_BEARING_TYPES: frozenset[str] = frozenset(
768
826
  # describing the entities affected by accept/reject — callers
769
827
  # need that synchronously to invalidate their Revision proxies.
770
828
  "TrackChangesDecide",
829
+ # ListsCreate / ListsAttach return {item: {nodeId: <listItemId>}}.
830
+ # Document.add_paragraph reads listItemId to (1) update the
831
+ # Paragraph proxy's node_id so subsequent ops target the
832
+ # listItem, and (2) chain the next list item via lists.attach.
833
+ "ListsCreate",
834
+ "ListsAttach",
771
835
  }
772
836
  )
773
837
 
@@ -816,6 +880,7 @@ __all__ = [
816
880
  "SetSectionHeaderFooterMargins",
817
881
  "SetSectionBreakType",
818
882
  "SetSectionTitlePage",
883
+ "SetSectionsOddEvenHeadersFooters",
819
884
  "SetSectionLinkToPrevious",
820
885
  # Tables (mutations)
821
886
  "TablesInsertRow",