athena-python-docx 0.6.2__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 (305) hide show
  1. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/CLAUDE.md +21 -5
  2. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/PKG-INFO +1 -1
  3. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/__init__.py +1 -1
  4. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/_buffer.py +66 -106
  5. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/_http_doc.py +2 -0
  6. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/commands.py +38 -13
  7. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/document.py +73 -200
  8. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/settings.py +20 -20
  9. athena_python_docx-0.7.0/docx/text/hyperlink.py +167 -0
  10. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/text/paragraph.py +122 -21
  11. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/pyproject.toml +1 -1
  12. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/fake_session.py +9 -4
  13. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/ours_spec.json +1822 -289
  14. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/parity_crawl.py +26 -3
  15. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/parity_diff.json +87 -185
  16. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/stock_spec.json +16 -16
  17. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/test_e2e_against_staging.py +1 -1
  18. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/parity/baseline_gaps.json +1 -1
  19. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/parity/reports/GAP_ANALYSIS.md +28 -3
  20. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/parity/reports/gap_report.json +1670 -2
  21. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/parity/snapshots/athena_latest.json +496 -991
  22. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_batching_perf.py +96 -126
  23. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_buffer.py +30 -13
  24. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_commands.py +12 -2
  25. athena_python_docx-0.7.0/tests/test_hyperlink_coalescing.py +199 -0
  26. athena_python_docx-0.7.0/tests/test_silent_stub_settings.py +75 -0
  27. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_wire_contract.py +5 -0
  28. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/uv.lock +1 -1
  29. athena_python_docx-0.6.2/docx/text/hyperlink.py +0 -123
  30. athena_python_docx-0.6.2/tests/test_silent_stub_settings.py +0 -68
  31. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/.gitignore +0 -0
  32. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/README.md +0 -0
  33. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/_batching.py +0 -0
  34. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/_http.py +0 -0
  35. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/_image_utils.py +0 -0
  36. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/_ptc.py +0 -0
  37. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/api.py +0 -0
  38. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/client.py +0 -0
  39. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/comments.py +0 -0
  40. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/enum/__init__.py +0 -0
  41. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/enum/section.py +0 -0
  42. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/enum/style.py +0 -0
  43. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/enum/table.py +0 -0
  44. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/enum/text.py +0 -0
  45. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/errors.py +0 -0
  46. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/exceptions.py +0 -0
  47. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/opc/__init__.py +0 -0
  48. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/opc/coreprops.py +0 -0
  49. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/revisions.py +0 -0
  50. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/section.py +0 -0
  51. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/shape.py +0 -0
  52. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/shared.py +0 -0
  53. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/styles/__init__.py +0 -0
  54. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/styles/style.py +0 -0
  55. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/styles/styles.py +0 -0
  56. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/table.py +0 -0
  57. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/text/__init__.py +0 -0
  58. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/text/font.py +0 -0
  59. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/text/pagebreak.py +0 -0
  60. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/text/parfmt.py +0 -0
  61. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/text/run.py +0 -0
  62. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/text/tabstops.py +0 -0
  63. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/docx/typing.py +0 -0
  64. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/scripts/publish.sh +0 -0
  65. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/scripts/release.sh +0 -0
  66. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/scripts/round_trip_smoke.py +0 -0
  67. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/__init__.py +0 -0
  68. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/conftest.py +0 -0
  69. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/METHODOLOGY.md +0 -0
  70. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/README.md +0 -0
  71. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/__init__.py +0 -0
  72. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/ab_probe_cases.py +0 -0
  73. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/ab_probe_runner.py +0 -0
  74. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/auto_gen_cases.py +0 -0
  75. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/binary_round_trip.py +0 -0
  76. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/cases.py +0 -0
  77. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/complex_cases.py +0 -0
  78. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/coverage_report.py +0 -0
  79. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/extract.py +0 -0
  80. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/extreme_cases.py +0 -0
  81. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/local_runner.py +0 -0
  82. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/mega_cases.py +0 -0
  83. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshot.py +0 -0
  84. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
  85. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
  86. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
  87. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
  88. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
  89. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
  90. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
  91. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
  92. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
  93. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
  94. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
  95. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
  96. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/103_cell_tables_enumeration.json +0 -0
  97. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
  98. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
  99. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
  100. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
  101. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
  102. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
  103. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
  104. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
  105. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
  106. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
  107. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
  108. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
  109. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
  110. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
  111. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
  112. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
  113. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
  114. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/23_nested_table.json +0 -0
  115. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
  116. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
  117. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
  118. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
  119. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
  120. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
  121. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
  122. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
  123. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
  124. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
  125. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
  126. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
  127. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
  128. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
  129. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
  130. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
  131. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
  132. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
  133. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
  134. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
  135. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
  136. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
  137. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
  138. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
  139. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
  140. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
  141. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
  142. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/51_nested_tables_deep.json +0 -0
  143. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
  144. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
  145. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
  146. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
  147. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
  148. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
  149. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
  150. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
  151. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
  152. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
  153. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
  154. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
  155. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
  156. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
  157. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
  158. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
  159. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
  160. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
  161. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
  162. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
  163. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
  164. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
  165. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
  166. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
  167. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
  168. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
  169. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
  170. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
  171. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
  172. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
  173. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
  174. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
  175. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
  176. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
  177. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
  178. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
  179. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
  180. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
  181. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
  182. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
  183. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
  184. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
  185. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
  186. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
  187. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
  188. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
  189. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
  190. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
  191. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex01_five_levels_deep_tables.json +0 -0
  192. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
  193. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
  194. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
  195. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
  196. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
  197. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
  198. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
  199. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
  200. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
  201. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
  202. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
  203. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
  204. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
  205. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
  206. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
  207. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
  208. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
  209. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
  210. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
  211. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
  212. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
  213. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
  214. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
  215. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
  216. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
  217. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
  218. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
  219. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
  220. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
  221. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
  222. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
  223. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
  224. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
  225. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
  226. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
  227. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
  228. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
  229. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
  230. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
  231. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
  232. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
  233. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
  234. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw14_nested_cell_table.json +0 -0
  235. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
  236. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/real_world_cases.py +0 -0
  237. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/round_trip_tests.py +0 -0
  238. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/fidelity/runner.py +0 -0
  239. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/parity/README.md +0 -0
  240. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/parity/__init__.py +0 -0
  241. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/parity/compare.py +0 -0
  242. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/parity/intentional_deviations.json +0 -0
  243. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/parity/introspect.py +0 -0
  244. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/parity/run_parity.py +0 -0
  245. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
  246. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/parity/test_parity_gap.py +0 -0
  247. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_collapsed_range_format.py +0 -0
  248. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_command_dataclasses.py +0 -0
  249. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_comments.py +0 -0
  250. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_document_create.py +0 -0
  251. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_http_transport.py +0 -0
  252. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_iter_inner_content.py +0 -0
  253. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_list_styles.py +0 -0
  254. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_merged_cells.py +0 -0
  255. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_paragraph_text_len_cache.py +0 -0
  256. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_parity_misc.py +0 -0
  257. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_parity_round2.py +0 -0
  258. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_phase_a_behavior.py +0 -0
  259. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_phase_b_headers_footers.py +0 -0
  260. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_phase_c_tables.py +0 -0
  261. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_pr19766_review_fixes.py +0 -0
  262. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_ptc.py +0 -0
  263. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_python_docx_api_parity.py +0 -0
  264. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_revisions.py +0 -0
  265. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
  266. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_add_picture.py +0 -0
  267. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_add_run.py +0 -0
  268. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
  269. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
  270. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_comments_get.py +0 -0
  271. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_document_audit.py +0 -0
  272. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_document_element.py +0 -0
  273. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_enum_section.py +0 -0
  274. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_font_audit.py +0 -0
  275. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_header_footer.py +0 -0
  276. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_hyperlink.py +0 -0
  277. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_inline_shape.py +0 -0
  278. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
  279. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_misc.py +0 -0
  280. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
  281. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_paragraph_style.py +0 -0
  282. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
  283. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_parfmt.py +0 -0
  284. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_row_col_cell.py +0 -0
  285. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_run_add_break.py +0 -0
  286. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
  287. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_run_style.py +0 -0
  288. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_run_style_strict.py +0 -0
  289. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_run_text.py +0 -0
  290. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_run_underline.py +0 -0
  291. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_section_audit.py +0 -0
  292. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_section_dimensions.py +0 -0
  293. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_section_onoff.py +0 -0
  294. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_shared_audit.py +0 -0
  295. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_style.py +0 -0
  296. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_styles.py +0 -0
  297. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_table_audit.py +0 -0
  298. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_table_cell.py +0 -0
  299. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_table_dimensions.py +0 -0
  300. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_silent_stub_table_layout.py +0 -0
  301. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_smoke_integration.py +0 -0
  302. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_style_acceptance.py +0 -0
  303. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_style_font.py +0 -0
  304. {athena_python_docx-0.6.2 → athena_python_docx-0.7.0}/tests/test_style_setters_contract.py +0 -0
  305. {athena_python_docx-0.6.2 → 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.2
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.2"
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(
@@ -286,7 +280,7 @@ class CommandBuffer:
286
280
  except Exception: # noqa: BLE001
287
281
  pass
288
282
 
289
- 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):
290
284
  return self._eager_flush_with(cmd)
291
285
 
292
286
  with self._lock:
@@ -294,23 +288,25 @@ class CommandBuffer:
294
288
  self._reset_timer_locked()
295
289
  return None
296
290
 
297
- def _is_batched_create(self, cmd: Command) -> bool:
298
- """Return True iff this command can be safely deferred because
299
- the caller pre-assigned a client-side id and the buffer is in
300
- batch mode.
301
-
302
- We only defer Create-shape commands (those with
303
- ``client_node_id`` / ``client_entity_id`` set). Pure queries
304
- like ``BlocksList`` must still eager-flush the caller is
305
- awaiting their data. Read-bearing creates without a client id
306
- (legacy callers / non-batch path) also stay eager so we don't
307
- 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.
308
302
  """
309
- if not self.is_batching:
310
- return False
311
- cli_node = getattr(cmd, "client_node_id", None)
312
- cli_ent = getattr(cmd, "client_entity_id", None)
313
- 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
+ )
314
310
 
315
311
  def flush(self) -> list[Any]:
316
312
  """Flush pending commands as one HTTP batch.
@@ -321,9 +317,10 @@ class CommandBuffer:
321
317
  After the batch returns, walks per-cmd results for
322
318
  ``real_node_id`` / ``real_entity_id`` echoes and updates any
323
319
  proxies registered via :meth:`register_proxy_id_ref` with the
324
- resolved server ids. This is what completes the round-trip for
325
- ``with doc.batch():`` — the SDK's caller-facing proxies pick up
326
- 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.
327
324
  """
328
325
  with self._lock:
329
326
  self._cancel_timer_locked()
@@ -375,43 +372,6 @@ class CommandBuffer:
375
372
  pass
376
373
  return results
377
374
 
378
- @contextmanager
379
- def batch(self) -> Generator[None, None, None]:
380
- """Group calls into one HTTP batch.
381
-
382
- Inside the ``with`` block:
383
-
384
- - Queries (``BlocksList``, ``Find``, ``GetNodeById``, …) still
385
- eager-flush — the caller is awaiting their result, so we have
386
- no choice.
387
- - Pure mutations (formatters, setters) accumulate without their
388
- idle timer firing.
389
- - Create*-shape commands that carry a ``client_node_id`` /
390
- ``client_entity_id`` (set by ``Document.add_paragraph`` and
391
- friends when they detect batch mode) also accumulate — the
392
- server resolves the client UUIDs to real SuperDoc ids in a
393
- single batch via the per-request ``clientIdMap``.
394
-
395
- On exit, drains anything left. Reentrant: nested ``batch()``
396
- blocks share the same accumulating queue; only the outermost
397
- block flushes.
398
- """
399
- with self._lock:
400
- self._cancel_timer_locked()
401
- old_window = self._auto_flush_seconds
402
- self._auto_flush_seconds = 0.0 # disable timer-scheduled flush
403
- self._batch_depth += 1
404
- try:
405
- yield
406
- finally:
407
- with self._lock:
408
- self._batch_depth -= 1
409
- outermost = self._batch_depth == 0
410
- if outermost:
411
- self._auto_flush_seconds = old_window
412
- if outermost:
413
- self.flush()
414
-
415
375
  def close(self) -> None:
416
376
  """Flush and disable. Idempotent."""
417
377
  if self._closed:
@@ -427,11 +387,11 @@ class CommandBuffer:
427
387
  def _eager_flush_with(self, cmd: Command) -> Any:
428
388
  """Drain pending + run ``cmd`` in one batch; return cmd's result.
429
389
 
430
- When pending commands include batched Creates with registered
431
- proxy refs (e.g. a query mid-batch triggers an eager flush
432
- before the ``with doc.batch():`` block ends), the proxies still
433
- get their real ids written back via the same flush-time rewrite
434
- 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.
435
395
  """
436
396
  with self._lock:
437
397
  self._cancel_timer_locked()
@@ -93,6 +93,7 @@ from docx.commands import (
93
93
  SetSectionPageMargins,
94
94
  SetSectionPageSetup,
95
95
  SetSectionTitlePage,
96
+ SetSectionsOddEvenHeadersFooters,
96
97
  TablesGet,
97
98
  TablesGetCells,
98
99
  TablesGetProperties,
@@ -465,6 +466,7 @@ _OP_TO_COMMAND: dict[str, type[Command]] = {
465
466
  "sections.set_header_footer_margins": SetSectionHeaderFooterMargins,
466
467
  "sections.set_break_type": SetSectionBreakType,
467
468
  "sections.set_title_page": SetSectionTitlePage,
469
+ "sections.set_odd_even_headers_footers": SetSectionsOddEvenHeadersFooters,
468
470
  "sections.set_link_to_previous": SetSectionLinkToPrevious,
469
471
  # Tables (mutations)
470
472
  "tables.insert_row": TablesInsertRow,
@@ -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
 
@@ -856,6 +880,7 @@ __all__ = [
856
880
  "SetSectionHeaderFooterMargins",
857
881
  "SetSectionBreakType",
858
882
  "SetSectionTitlePage",
883
+ "SetSectionsOddEvenHeadersFooters",
859
884
  "SetSectionLinkToPrevious",
860
885
  # Tables (mutations)
861
886
  "TablesInsertRow",