athena-python-docx 0.6.2__tar.gz → 0.8.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 (313) hide show
  1. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/CLAUDE.md +46 -5
  2. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/PKG-INFO +1 -1
  3. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/__init__.py +1 -1
  4. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/_buffer.py +66 -106
  5. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/_http_doc.py +60 -5
  6. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/commands.py +47 -15
  7. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/document.py +73 -200
  8. athena_python_docx-0.8.0/docx/errors.py +88 -0
  9. athena_python_docx-0.8.0/docx/oxml/__init__.py +148 -0
  10. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/settings.py +20 -20
  11. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/table.py +90 -19
  12. athena_python_docx-0.8.0/docx/text/hyperlink.py +167 -0
  13. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/text/paragraph.py +122 -21
  14. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/pyproject.toml +1 -1
  15. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/fake_session.py +9 -4
  16. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -6
  17. athena_python_docx-0.8.0/tests/fidelity/op_snapshots/17_table_basic.json +4 -0
  18. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -6
  19. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +2 -1
  20. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/23_nested_table.json +0 -1
  21. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -1
  22. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/35_full_report.json +12 -10
  23. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -100
  24. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -1
  25. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -4
  26. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/51_nested_tables_deep.json +0 -1
  27. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -13
  28. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +2 -1
  29. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +3 -1
  30. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -1
  31. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +800 -401
  32. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -19
  33. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -1
  34. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -3
  35. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -30
  36. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +18 -10
  37. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -10
  38. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -100
  39. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex01_five_levels_deep_tables.json +0 -1
  40. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -4
  41. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -50
  42. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -2
  43. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +90 -66
  44. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +240 -81
  45. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -1
  46. athena_python_docx-0.8.0/tests/fidelity/op_snapshots/ex14_styled_report_table.json +211 -0
  47. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +120 -90
  48. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +18 -15
  49. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +18 -19
  50. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +28 -23
  51. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +32 -21
  52. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +40 -31
  53. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +8 -7
  54. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +64 -32
  55. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -12
  56. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -13
  57. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -8
  58. athena_python_docx-0.6.2/tests/fidelity/op_snapshots/ex14_styled_report_table.json → athena_python_docx-0.8.0/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +58 -47
  59. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw14_nested_cell_table.json +0 -2
  60. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/ours_spec.json +1822 -289
  61. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/parity_crawl.py +26 -3
  62. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/parity_diff.json +87 -185
  63. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/stock_spec.json +16 -16
  64. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/test_e2e_against_staging.py +1 -1
  65. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/parity/baseline_gaps.json +1 -1
  66. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/parity/compare.py +16 -1
  67. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/parity/reports/GAP_ANALYSIS.md +29 -5
  68. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/parity/reports/gap_report.json +1671 -12
  69. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/parity/snapshots/athena_latest.json +622 -1052
  70. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_batching_perf.py +96 -126
  71. athena_python_docx-0.8.0/tests/test_block_not_found_error.py +246 -0
  72. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_buffer.py +30 -13
  73. athena_python_docx-0.8.0/tests/test_cell_text_plain_fastpath.py +60 -0
  74. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_command_dataclasses.py +10 -1
  75. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_commands.py +12 -2
  76. athena_python_docx-0.8.0/tests/test_hyperlink_coalescing.py +199 -0
  77. athena_python_docx-0.8.0/tests/test_insert_deferred.py +138 -0
  78. athena_python_docx-0.8.0/tests/test_oxml_shim.py +123 -0
  79. athena_python_docx-0.8.0/tests/test_silent_stub_settings.py +75 -0
  80. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_wire_contract.py +5 -0
  81. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/uv.lock +1 -1
  82. athena_python_docx-0.6.2/docx/errors.py +0 -45
  83. athena_python_docx-0.6.2/docx/text/hyperlink.py +0 -123
  84. athena_python_docx-0.6.2/tests/fidelity/op_snapshots/17_table_basic.json +0 -5
  85. athena_python_docx-0.6.2/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -181
  86. athena_python_docx-0.6.2/tests/test_silent_stub_settings.py +0 -68
  87. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/.gitignore +0 -0
  88. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/README.md +0 -0
  89. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/_batching.py +0 -0
  90. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/_http.py +0 -0
  91. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/_image_utils.py +0 -0
  92. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/_ptc.py +0 -0
  93. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/api.py +0 -0
  94. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/client.py +0 -0
  95. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/comments.py +0 -0
  96. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/enum/__init__.py +0 -0
  97. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/enum/section.py +0 -0
  98. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/enum/style.py +0 -0
  99. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/enum/table.py +0 -0
  100. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/enum/text.py +0 -0
  101. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/exceptions.py +0 -0
  102. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/opc/__init__.py +0 -0
  103. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/opc/coreprops.py +0 -0
  104. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/revisions.py +0 -0
  105. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/section.py +0 -0
  106. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/shape.py +0 -0
  107. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/shared.py +0 -0
  108. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/styles/__init__.py +0 -0
  109. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/styles/style.py +0 -0
  110. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/styles/styles.py +0 -0
  111. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/text/__init__.py +0 -0
  112. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/text/font.py +0 -0
  113. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/text/pagebreak.py +0 -0
  114. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/text/parfmt.py +0 -0
  115. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/text/run.py +0 -0
  116. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/text/tabstops.py +0 -0
  117. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/docx/typing.py +0 -0
  118. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/scripts/publish.sh +0 -0
  119. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/scripts/release.sh +0 -0
  120. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/scripts/round_trip_smoke.py +0 -0
  121. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/__init__.py +0 -0
  122. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/conftest.py +0 -0
  123. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/METHODOLOGY.md +0 -0
  124. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/README.md +0 -0
  125. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/__init__.py +0 -0
  126. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/ab_probe_cases.py +0 -0
  127. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/ab_probe_runner.py +0 -0
  128. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/auto_gen_cases.py +0 -0
  129. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/binary_round_trip.py +0 -0
  130. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/cases.py +0 -0
  131. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/complex_cases.py +0 -0
  132. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/coverage_report.py +0 -0
  133. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/extract.py +0 -0
  134. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/extreme_cases.py +0 -0
  135. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/local_runner.py +0 -0
  136. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/mega_cases.py +0 -0
  137. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshot.py +0 -0
  138. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
  139. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
  140. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
  141. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
  142. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
  143. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
  144. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
  145. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
  146. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
  147. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
  148. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
  149. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/103_cell_tables_enumeration.json +0 -0
  150. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
  151. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
  152. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
  153. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
  154. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
  155. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
  156. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
  157. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
  158. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
  159. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
  160. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
  161. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
  162. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
  163. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
  164. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
  165. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
  166. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
  167. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
  168. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
  169. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
  170. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
  171. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
  172. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
  173. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
  174. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
  175. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
  176. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
  177. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
  178. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
  179. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
  180. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
  181. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
  182. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
  183. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
  184. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
  185. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
  186. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
  187. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
  188. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
  189. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
  190. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
  191. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
  192. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
  193. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
  194. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
  195. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
  196. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
  197. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
  198. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
  199. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
  200. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
  201. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
  202. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
  203. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
  204. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
  205. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
  206. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
  207. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
  208. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
  209. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
  210. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
  211. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
  212. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
  213. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
  214. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
  215. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
  216. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
  217. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
  218. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
  219. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
  220. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
  221. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
  222. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
  223. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
  224. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
  225. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
  226. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
  227. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
  228. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
  229. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
  230. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
  231. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
  232. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
  233. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
  234. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
  235. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
  236. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
  237. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
  238. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
  239. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
  240. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
  241. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
  242. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
  243. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
  244. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
  245. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
  246. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/real_world_cases.py +0 -0
  247. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/round_trip_tests.py +0 -0
  248. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/fidelity/runner.py +0 -0
  249. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/parity/README.md +0 -0
  250. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/parity/__init__.py +0 -0
  251. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/parity/intentional_deviations.json +0 -0
  252. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/parity/introspect.py +0 -0
  253. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/parity/run_parity.py +0 -0
  254. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
  255. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/parity/test_parity_gap.py +0 -0
  256. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_collapsed_range_format.py +0 -0
  257. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_comments.py +0 -0
  258. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_document_create.py +0 -0
  259. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_http_transport.py +0 -0
  260. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_iter_inner_content.py +0 -0
  261. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_list_styles.py +0 -0
  262. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_merged_cells.py +0 -0
  263. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_paragraph_text_len_cache.py +0 -0
  264. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_parity_misc.py +0 -0
  265. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_parity_round2.py +0 -0
  266. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_phase_a_behavior.py +0 -0
  267. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_phase_b_headers_footers.py +0 -0
  268. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_phase_c_tables.py +0 -0
  269. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_pr19766_review_fixes.py +0 -0
  270. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_ptc.py +0 -0
  271. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_python_docx_api_parity.py +0 -0
  272. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_revisions.py +0 -0
  273. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
  274. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_add_picture.py +0 -0
  275. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_add_run.py +0 -0
  276. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
  277. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
  278. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_comments_get.py +0 -0
  279. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_document_audit.py +0 -0
  280. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_document_element.py +0 -0
  281. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_enum_section.py +0 -0
  282. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_font_audit.py +0 -0
  283. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_header_footer.py +0 -0
  284. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_hyperlink.py +0 -0
  285. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_inline_shape.py +0 -0
  286. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
  287. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_misc.py +0 -0
  288. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
  289. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_paragraph_style.py +0 -0
  290. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
  291. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_parfmt.py +0 -0
  292. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_row_col_cell.py +0 -0
  293. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_run_add_break.py +0 -0
  294. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
  295. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_run_style.py +0 -0
  296. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_run_style_strict.py +0 -0
  297. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_run_text.py +0 -0
  298. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_run_underline.py +0 -0
  299. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_section_audit.py +0 -0
  300. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_section_dimensions.py +0 -0
  301. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_section_onoff.py +0 -0
  302. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_shared_audit.py +0 -0
  303. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_style.py +0 -0
  304. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_styles.py +0 -0
  305. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_table_audit.py +0 -0
  306. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_table_cell.py +0 -0
  307. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_table_dimensions.py +0 -0
  308. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_silent_stub_table_layout.py +0 -0
  309. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_smoke_integration.py +0 -0
  310. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_style_acceptance.py +0 -0
  311. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_style_font.py +0 -0
  312. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_style_setters_contract.py +0 -0
  313. {athena_python_docx-0.6.2 → athena_python_docx-0.8.0}/tests/test_zod_wire_contract.py +0 -0
@@ -39,6 +39,18 @@ These standard python-docx members don't apply to a Superdoc-backed SDK:
39
39
  `Comment.tables` — SuperDoc models a comment as a single text body,
40
40
  not a `BlockItemContainer`. Use `Comment.text` instead. The
41
41
  paragraph/table-bearing surface raises `CommentsNotImplementedError`.
42
+ - **`docx.oxml.*`** — the entire raw-OOXML module tree (``ns.qn``,
43
+ ``OxmlElement``, ``CT_*`` classes, etc.) is unavailable because the
44
+ SDK is HTTP-only against a SuperDoc Y.Doc; there is no local lxml
45
+ tree to manipulate. As of 0.8.0, ``docx.oxml`` is a real stub
46
+ package that raises a typed
47
+ :class:`docx.oxml.OxmlNotAvailableError` (subclass of
48
+ ``ImportError``) on any attribute access, with an inline pointer at
49
+ the high-level API (``Document.add_paragraph``, ``Run.bold``,
50
+ ``_Cell.text``, …) or the typed command surface in
51
+ ``docx.commands``. Pre-0.8.0 this used to fail with the stdlib's
52
+ generic ``ModuleNotFoundError: No module named 'docx.oxml'`` and
53
+ agents had to retry to discover the gap.
42
54
 
43
55
  ### Phase-3b upstream-blocked surface
44
56
 
@@ -135,11 +147,40 @@ This is a **thin HTTP client** that mimics the sync python-docx API.
135
147
  backoff, 429/502/503/504). The legacy `transport="direct"` (embedded
136
148
  Superdoc CLI + y-websocket from Python) was removed in 0.5.0; agent
137
149
  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.
150
+ - **Transparent batching (0.7.0+):** Create* commands
151
+ (`CreateParagraph`, `CreateHeading`, `CreateTable`, `CreateImage`)
152
+ ALWAYS pre-mint a client-side UUID (`client_node_id`) and defer
153
+ through the buffer. The applier resolves UUIDs to real SuperDoc
154
+ ids via per-batch `clientIdMap` on flush, and the buffer rewrites
155
+ each proxy's `_node_id` in-place. Net result: 30 `add_paragraph`
156
+ calls = 1 HTTP request, with no explicit `batch()` context manager
157
+ in the public API (1:1 python-docx parity).
158
+ - Flush triggers: queries (`BlocksList`, `Find`, `GetNodeById`, …)
159
+ drain pending ops before running; the 100 ms idle timer fires on
160
+ inactivity so collab updates stream live; `Document.save()` and
161
+ the context-manager exit drain explicitly; `docx.flush_all()` is
162
+ the Daytona-prelude hook that flushes every live buffer in the
163
+ process.
164
+ - **Insert deferral (0.8.0+):** `Insert` was removed from the
165
+ response-bearing set because every in-tree caller
166
+ (`Paragraph._insert_run_text_segments`, `Run.add_text`, `_Cell.text`)
167
+ ignores the response — the Run proxy wraps a known client-side range
168
+ and doesn't need the server's echoed node id. Net result: a styled
169
+ run (one `add_run` + 6 format setters) ships as 1 HTTP request
170
+ instead of 2. The remaining response-bearing ops without a client id
171
+ (`ListsCreate`, `ListsAttach`, `CommentsCreate`, `CommentsPatch`,
172
+ `TrackChangesDecide`) keep their eager-flush semantics so callers
173
+ reading back ids still see real data. A `Document.add_paragraph`
174
+ with `style="List Bullet"` flushes the queued CreateParagraph in
175
+ the same batch as the follow-up `ListsCreate` — still one HTTP
176
+ request per logical operation.
177
+ - **Plain-text cell fast path (0.8.0+):** `_Cell.text = value` skips
178
+ the eager `doc.markdownToFragment` query when ``value`` contains no
179
+ markdown control characters (the dominant case for tabular data —
180
+ numbers, labels, currency strings). The fragment is built locally
181
+ and the follow-up `doc.insert` is buffered, so plain cell
182
+ assignments are 0 HTTP requests until the next flush. See
183
+ ``docx.table._is_plain_text`` for the trigger predicate.
143
184
  - The path-proxy in `_http_doc.py` is an internal translation layer:
144
185
  call sites that look like `await self._session.doc.create.paragraph(p)`
145
186
  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.8.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.8.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,
@@ -111,6 +112,7 @@ from docx.commands import (
111
112
  )
112
113
  from docx.errors import (
113
114
  AuthenticationError,
115
+ BlockNotFoundError,
114
116
  DocxError,
115
117
  NotFoundError,
116
118
  SessionError,
@@ -133,6 +135,39 @@ _NOT_FOUND_ERROR_NAMES: frozenset[str] = frozenset(
133
135
  )
134
136
 
135
137
 
138
+ def _looks_like_block_not_found(err_obj: dict) -> bool:
139
+ """Detect SuperDoc's ``Block "<type>:<id>" was not found`` shape.
140
+
141
+ Triggers the typed :class:`BlockNotFoundError` so agent code can
142
+ distinguish a missing-block miss (cell-inner-paragraph addressing
143
+ bug, stale id from a different session) from a generic transport
144
+ or validation error.
145
+ """
146
+ msg = err_obj.get("message")
147
+ if not isinstance(msg, str):
148
+ return False
149
+ lower = msg.lower()
150
+ if "not found" not in lower:
151
+ return False
152
+ # Be conservative: require either ``block "`` (the SuperDoc CLI's
153
+ # quoted-id format) or ``block <id>`` followed by ``not found``.
154
+ return 'block "' in lower or ('block ' in lower and ' was not found' in lower)
155
+
156
+
157
+ _CELL_PARAGRAPH_HINT: str = (
158
+ "\n\nHint: SuperDoc 1.8.1 cannot format paragraphs nested inside "
159
+ "table cells via SetParagraphAlignment / SetParagraphStyle / "
160
+ "SetParagraphIndentation / SetParagraphSpacing or doc.insert with "
161
+ "a paragraph-block target. The cell's inner paragraph id is "
162
+ "returned by cell.getNodeById but isn't a top-level addressable "
163
+ "block. Materialize the cell's paragraph first via "
164
+ '``cell.text = "value"``, then re-read ``cell.paragraphs[0]`` and '
165
+ "apply format ops to that post-materialization Paragraph proxy. "
166
+ "Tracked upstream at docx-studio/SUPERDOC_UPSTREAM_REQUESTS.md "
167
+ "§ 'cell-inner paragraph addressing'."
168
+ )
169
+
170
+
136
171
  def _looks_like_not_found(parsed: dict) -> bool:
137
172
  """Inspect a docx-studio partial-failure dict and decide whether it
138
173
  describes a "no such entity" miss (vs. a real transport / validation
@@ -284,11 +319,30 @@ def _http_post_json(
284
319
  # sites (``Comments.get``) can coerce it to ``None`` without
285
320
  # swallowing real transport / validation failures.
286
321
  err_obj = parsed.get("error") if isinstance(parsed, dict) else None
287
- if isinstance(err_obj, dict) and _looks_like_not_found(err_obj):
288
- raise NotFoundError(
289
- f"docx-studio batch reported a partial failure: {parsed!r}",
290
- payload=err_obj,
291
- )
322
+ if isinstance(err_obj, dict):
323
+ base_msg = f"docx-studio batch reported a partial failure: {parsed!r}"
324
+ # ``BlockNotFoundError`` is the narrower miss — caller is most
325
+ # likely targeting a cell-inner paragraph or stale-session
326
+ # block id. Surface the typed exception plus an agent-readable
327
+ # workaround so the next attempt doesn't repeat the same
328
+ # mistake. The cell-paragraph hint only applies when the
329
+ # missing id has the ``paragraph:`` prefix (SuperDoc's quoted-
330
+ # id error format) — stale list-item / table-row / image ids
331
+ # would benefit from a different hint or none at all, and a
332
+ # red-herring "use cell.text" pointer would just waste the
333
+ # next retry.
334
+ if _looks_like_block_not_found(err_obj):
335
+ msg_str = err_obj.get("message")
336
+ paragraph_block = (
337
+ isinstance(msg_str, str)
338
+ and "paragraph:" in msg_str.lower()
339
+ )
340
+ raise BlockNotFoundError(
341
+ base_msg + (_CELL_PARAGRAPH_HINT if paragraph_block else ""),
342
+ payload=err_obj,
343
+ )
344
+ if _looks_like_not_found(err_obj):
345
+ raise NotFoundError(base_msg, payload=err_obj)
292
346
  raise DocxError(
293
347
  f"docx-studio batch reported a partial failure: {parsed!r}",
294
348
  )
@@ -465,6 +519,7 @@ _OP_TO_COMMAND: dict[str, type[Command]] = {
465
519
  "sections.set_header_footer_margins": SetSectionHeaderFooterMargins,
466
520
  "sections.set_break_type": SetSectionBreakType,
467
521
  "sections.set_title_page": SetSectionTitlePage,
522
+ "sections.set_odd_even_headers_footers": SetSectionsOddEvenHeadersFooters,
468
523
  "sections.set_link_to_previous": SetSectionLinkToPrevious,
469
524
  # Tables (mutations)
470
525
  "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
 
@@ -790,8 +814,15 @@ _RESPONSE_BEARING_TYPES: frozenset[str] = frozenset(
790
814
  # CreateImage returns {image: {nodeId}}; SetImageSize and the
791
815
  # InlineShape proxy both need that nodeId, so it must flush.
792
816
  "CreateImage",
793
- # Insert sometimes returns an id; treat as response-bearing for safety.
794
- "Insert",
817
+ # Insert is INTENTIONALLY not here as of 0.8.0. All in-tree
818
+ # callers (``Paragraph._insert_run_text_segments``, ``Run.add_text``,
819
+ # ``_Cell.text``) wrap a known client-side range/cell target and
820
+ # never read Insert's response; deferring it lets a styled
821
+ # ``add_run`` flow ship its text-insert in the same HTTP batch
822
+ # as the format setters that follow it. If a future caller needs
823
+ # the Insert response, route it through ``CommandBuffer._eager_flush_with``
824
+ # at the call site rather than re-adding Insert here, so the
825
+ # batched path isn't penalized for the one outlier.
795
826
  # CreateSectionBreak intentionally NOT here — it returns no node id;
796
827
  # the caller resolves the new section via sections.list afterwards.
797
828
  # CommentsCreate returns the new comment's entityId — caller wraps
@@ -856,6 +887,7 @@ __all__ = [
856
887
  "SetSectionHeaderFooterMargins",
857
888
  "SetSectionBreakType",
858
889
  "SetSectionTitlePage",
890
+ "SetSectionsOddEvenHeadersFooters",
859
891
  "SetSectionLinkToPrevious",
860
892
  # Tables (mutations)
861
893
  "TablesInsertRow",