athena-python-docx 0.10.1__tar.gz → 0.11.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 (338) hide show
  1. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/CLAUDE.md +178 -4
  2. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/PKG-INFO +1 -1
  3. athena_python_docx-0.11.0/docx/__init__.py +46 -0
  4. athena_python_docx-0.11.0/docx/_athena_extension.py +384 -0
  5. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/_http_doc.py +158 -11
  6. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/_image_utils.py +27 -0
  7. athena_python_docx-0.11.0/docx/bookmarks.py +194 -0
  8. athena_python_docx-0.11.0/docx/charts.py +142 -0
  9. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/client.py +34 -0
  10. athena_python_docx-0.11.0/docx/commands.py +1812 -0
  11. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/document.py +411 -0
  12. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/errors.py +24 -0
  13. athena_python_docx-0.11.0/docx/fields.py +151 -0
  14. athena_python_docx-0.11.0/docx/footnotes.py +248 -0
  15. athena_python_docx-0.11.0/docx/math.py +81 -0
  16. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/revisions.py +12 -0
  17. athena_python_docx-0.11.0/docx/sdt.py +216 -0
  18. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/section.py +77 -0
  19. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/shape.py +167 -0
  20. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/table.py +313 -19
  21. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/text/paragraph.py +250 -0
  22. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/text/run.py +261 -0
  23. athena_python_docx-0.11.0/docx/toc.py +79 -0
  24. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/pyproject.toml +1 -1
  25. athena_python_docx-0.11.0/tests/fidelity/cases.py +473 -0
  26. athena_python_docx-0.11.0/tests/fidelity/firm_templates/README.md +107 -0
  27. athena_python_docx-0.11.0/tests/fidelity/firm_templates/__init__.py +11 -0
  28. athena_python_docx-0.11.0/tests/fidelity/firm_templates/_runner.py +144 -0
  29. athena_python_docx-0.11.0/tests/fidelity/firm_templates/extractor.py +183 -0
  30. athena_python_docx-0.11.0/tests/fidelity/firm_templates/test_pw_corpus.py +364 -0
  31. athena_python_docx-0.11.0/tests/fidelity/firm_templates/test_pw_research_digest.py +129 -0
  32. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/parity/intentional_deviations.json +27 -1
  33. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/parity/reports/GAP_ANALYSIS.md +4 -3
  34. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/parity/reports/gap_report.json +51 -3
  35. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/parity/snapshots/athena_latest.json +9623 -3320
  36. athena_python_docx-0.11.0/tests/test_athena_extensions_contract.py +448 -0
  37. athena_python_docx-0.11.0/tests/test_athena_extensions_registry.py +298 -0
  38. athena_python_docx-0.11.0/tests/test_merged_cell_secondary_slot.py +251 -0
  39. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_parity_round2.py +34 -1
  40. athena_python_docx-0.11.0/tests/test_temporarily_unavailable.py +99 -0
  41. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_wire_contract.py +195 -0
  42. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/uv.lock +1 -1
  43. athena_python_docx-0.10.1/docx/__init__.py +0 -28
  44. athena_python_docx-0.10.1/docx/commands.py +0 -953
  45. athena_python_docx-0.10.1/tests/fidelity/cases.py +0 -176
  46. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/.gitignore +0 -0
  47. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/README.md +0 -0
  48. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/_batching.py +0 -0
  49. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/_buffer.py +0 -0
  50. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/_http.py +0 -0
  51. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/_ptc.py +0 -0
  52. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/_table_styles.py +0 -0
  53. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/api.py +0 -0
  54. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/comments.py +0 -0
  55. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/enum/__init__.py +0 -0
  56. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/enum/section.py +0 -0
  57. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/enum/style.py +0 -0
  58. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/enum/table.py +0 -0
  59. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/enum/text.py +0 -0
  60. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/exceptions.py +0 -0
  61. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/opc/__init__.py +0 -0
  62. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/opc/coreprops.py +0 -0
  63. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/oxml/__init__.py +0 -0
  64. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/settings.py +0 -0
  65. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/shared.py +0 -0
  66. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/styles/__init__.py +0 -0
  67. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/styles/style.py +0 -0
  68. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/styles/styles.py +0 -0
  69. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/text/__init__.py +0 -0
  70. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/text/font.py +0 -0
  71. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/text/hyperlink.py +0 -0
  72. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/text/pagebreak.py +0 -0
  73. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/text/parfmt.py +0 -0
  74. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/text/tabstops.py +0 -0
  75. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/docx/typing.py +0 -0
  76. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/scripts/publish.sh +0 -0
  77. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/scripts/release.sh +0 -0
  78. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/scripts/round_trip_smoke.py +0 -0
  79. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/scripts/smoke_test_block_not_found.py +0 -0
  80. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/__init__.py +0 -0
  81. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/conftest.py +0 -0
  82. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/METHODOLOGY.md +0 -0
  83. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/README.md +0 -0
  84. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/__init__.py +0 -0
  85. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/ab_probe_cases.py +0 -0
  86. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/ab_probe_runner.py +0 -0
  87. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/auto_gen_cases.py +0 -0
  88. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/binary_round_trip.py +0 -0
  89. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/complex_cases.py +0 -0
  90. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/coverage_report.py +0 -0
  91. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/extract.py +0 -0
  92. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/extreme_cases.py +0 -0
  93. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/fake_session.py +0 -0
  94. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/local_runner.py +0 -0
  95. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/mega_cases.py +0 -0
  96. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshot.py +0 -0
  97. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/01_basic_paragraph.json +0 -0
  98. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/02_multiple_headings.json +0 -0
  99. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/03_runs_with_formatting.json +0 -0
  100. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/04_font_name_and_size.json +0 -0
  101. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/05_font_color_rgb.json +0 -0
  102. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/06_font_character_properties.json +0 -0
  103. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/07_font_subscript_superscript.json +0 -0
  104. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/08_font_highlight.json +0 -0
  105. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/09_paragraph_alignment.json +0 -0
  106. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/100_table_negative_indexing.json +0 -0
  107. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/101_table_cells_flat_iteration.json +0 -0
  108. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/102_text_with_embedded_special_chars.json +0 -0
  109. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/103_cell_tables_enumeration.json +0 -0
  110. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/104_core_properties_datetime.json +0 -0
  111. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/105_default_one_section.json +0 -0
  112. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/106_heading_paragraph_format.json +0 -0
  113. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/107_varying_row_heights.json +0 -0
  114. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/10_paragraph_indents.json +0 -0
  115. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/11_paragraph_spacing.json +0 -0
  116. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/12_paragraph_keep_options.json +0 -0
  117. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/13_paragraph_tab_stops.json +0 -0
  118. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/14_run_add_tab_and_break.json +0 -0
  119. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/15_run_add_break_page.json +0 -0
  120. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/16_paragraph_clear_and_insert_before.json +0 -0
  121. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/17_table_basic.json +0 -0
  122. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/18_table_cell_text.json +0 -0
  123. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/19_table_row_column_sizing.json +0 -0
  124. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/20_table_cell_vertical_alignment.json +0 -0
  125. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/21_table_alignment_and_autofit.json +0 -0
  126. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/22_table_cell_paragraphs_iteration.json +0 -0
  127. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/23_nested_table.json +0 -0
  128. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/24_table_add_row_column.json +0 -0
  129. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/25_table_merge_cells.json +0 -0
  130. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/26_section_page_setup.json +0 -0
  131. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/27_section_margins.json +0 -0
  132. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/28_section_add_new.json +0 -0
  133. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/29_section_headers_linked.json +0 -0
  134. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/30_styles_iteration.json +0 -0
  135. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/31_styles_lookup_and_default.json +0 -0
  136. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/32_styles_add_paragraph_style.json +0 -0
  137. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/33_core_properties_set_and_get.json +0 -0
  138. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/34_inline_shapes_iterate_empty.json +0 -0
  139. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/35_full_report.json +0 -0
  140. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/36_replace_text_in_paragraph.json +0 -0
  141. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/37_iterate_runs_and_format_all_bold.json +0 -0
  142. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/38_font_all_properties.json +0 -0
  143. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/39_large_body_100_paragraphs.json +0 -0
  144. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/40_large_table_10x10.json +0 -0
  145. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/41_unicode_and_emoji.json +0 -0
  146. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/42_very_long_paragraph.json +0 -0
  147. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/43_paragraph_text_round_trip.json +0 -0
  148. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/44_paragraph_alignment_round_trip.json +0 -0
  149. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/45_cell_text_round_trip.json +0 -0
  150. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/46_run_text_setter_round_trip.json +0 -0
  151. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/47_font_size_round_trip.json +0 -0
  152. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/48_font_color_round_trip.json +0 -0
  153. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/49_resume_layout.json +0 -0
  154. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/50_multi_section_doc.json +0 -0
  155. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/51_nested_tables_deep.json +0 -0
  156. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/52_iterate_everything.json +0 -0
  157. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/53_apply_style_to_paragraphs.json +0 -0
  158. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/54_empty_everything.json +0 -0
  159. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/55_single_character_runs.json +0 -0
  160. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/56_everything_in_one.json +0 -0
  161. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/57_runs_after_multiple_text_appends.json +0 -0
  162. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/58_modify_runs_in_place.json +0 -0
  163. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/59_indent_round_trip.json +0 -0
  164. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/60_space_round_trip.json +0 -0
  165. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/61_cell_paragraph_with_runs.json +0 -0
  166. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/62_many_cell_paragraphs.json +0 -0
  167. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/63_table_style_round_trip.json +0 -0
  168. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/64_many_sections.json +0 -0
  169. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/65_20x20_table_formatted.json +0 -0
  170. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/66_toc_like_structure.json +0 -0
  171. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/67_paragraph_insert_before_chain.json +0 -0
  172. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/68_invoice.json +0 -0
  173. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/69_newsletter.json +0 -0
  174. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/70_add_and_iterate_back.json +0 -0
  175. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/71_academic_paper.json +0 -0
  176. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/72_legal_contract.json +0 -0
  177. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/73_form_with_many_tables.json +0 -0
  178. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/74_paragraph_with_10_runs.json +0 -0
  179. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/75_paragraph_negative_first_line_indent.json +0 -0
  180. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/76_rgbcolor_from_string.json +0 -0
  181. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/77_length_unit_conversions.json +0 -0
  182. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/78_paragraph_copy_style.json +0 -0
  183. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/79_bulk_cell_formatting.json +0 -0
  184. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/80_apply_style_after_add_run.json +0 -0
  185. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/81_multi_page_with_breaks.json +0 -0
  186. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/82_add_text_on_existing_run.json +0 -0
  187. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/83_clear_then_repopulate_paragraph.json +0 -0
  188. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/84_table_reread_row_count.json +0 -0
  189. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/85_header_footer_access.json +0 -0
  190. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/86_font_read_unset_returns_none.json +0 -0
  191. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/87_500_paragraph_doc.json +0 -0
  192. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/88_mixed_content_iteration.json +0 -0
  193. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/89_alignment_clear_via_none.json +0 -0
  194. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/90_cell_add_paragraph_styled.json +0 -0
  195. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/91_many_small_tables.json +0 -0
  196. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/92_margins_every_section.json +0 -0
  197. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/93_font_bool_reads_after_set.json +0 -0
  198. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/94_page_break_before_paragraph.json +0 -0
  199. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/95_paragraph_hyperlinks_empty.json +0 -0
  200. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/96_paragraph_contains_page_break.json +0 -0
  201. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/97_document_styles_by_key.json +0 -0
  202. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/98_style_contains_check.json +0 -0
  203. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/99_run_add_picture_from_bytes.json +0 -0
  204. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex01_five_levels_deep_tables.json +0 -0
  205. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex02_unicode_everywhere.json +0 -0
  206. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex03_1000_paragraphs.json +0 -0
  207. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex04_50x50_table.json +0 -0
  208. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex05_long_text_in_cell.json +0 -0
  209. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex06_hundred_tiny_runs.json +0 -0
  210. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex07_every_font_boolean.json +0 -0
  211. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex08_many_continuous_sections.json +0 -0
  212. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex09_many_tab_stops.json +0 -0
  213. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex10_complex_bom.json +0 -0
  214. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex11_banded_rows_formatting.json +0 -0
  215. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex12_section_reconfigure.json +0 -0
  216. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex13_cell_with_10_paragraphs.json +0 -0
  217. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex14_styled_report_table.json +0 -0
  218. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex15_paragraph_all_format_props.json +0 -0
  219. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex16_runs_interleaved_with_breaks.json +0 -0
  220. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex17_all_break_kinds.json +0 -0
  221. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex18_read_back_large_doc.json +0 -0
  222. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex19_mutate_all_runs.json +0 -0
  223. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/ex20_kitchen_sink_v2.json +0 -0
  224. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/mega01_book_chapter.json +0 -0
  225. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/mega02_research_proposal.json +0 -0
  226. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/mega03_financial_statement.json +0 -0
  227. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/mega04_recipe_card.json +0 -0
  228. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/mega05_user_manual.json +0 -0
  229. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/mega06_complex_newsletter.json +0 -0
  230. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/mega07_budget_spreadsheet.json +0 -0
  231. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/mega08_product_catalog.json +0 -0
  232. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/mega09_signed_contract.json +0 -0
  233. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/mega10_api_documentation.json +0 -0
  234. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/rw01_official_quickstart.json +0 -0
  235. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/rw02_paragraph_style_list.json +0 -0
  236. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/rw03_character_formatting.json +0 -0
  237. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/rw04_section_page_setup.json +0 -0
  238. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/rw05_toc_pattern.json +0 -0
  239. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/rw06_meeting_minutes.json +0 -0
  240. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/rw07_dense_formatting_demo.json +0 -0
  241. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/rw08_table_merged_header.json +0 -0
  242. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/rw09_bulk_run_iteration.json +0 -0
  243. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/rw10_colored_grid_table.json +0 -0
  244. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/rw11_header_text.json +0 -0
  245. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/rw12_first_page_footer.json +0 -0
  246. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/rw13_even_page_header.json +0 -0
  247. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/rw14_nested_cell_table.json +0 -0
  248. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/op_snapshots/rw15_paragraph_style_instance.json +0 -0
  249. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/ours_spec.json +0 -0
  250. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/parity_crawl.py +0 -0
  251. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/parity_diff.json +0 -0
  252. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/real_world_cases.py +0 -0
  253. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/round_trip_tests.py +0 -0
  254. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/runner.py +0 -0
  255. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/stock_spec.json +0 -0
  256. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/fidelity/test_e2e_against_staging.py +0 -0
  257. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/parity/README.md +0 -0
  258. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/parity/__init__.py +0 -0
  259. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/parity/baseline_gaps.json +0 -0
  260. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/parity/compare.py +0 -0
  261. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/parity/introspect.py +0 -0
  262. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/parity/run_parity.py +0 -0
  263. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/parity/snapshots/upstream_python_docx_1.2.0.json +0 -0
  264. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/parity/test_parity_gap.py +0 -0
  265. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_batching_perf.py +0 -0
  266. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_block_not_found_error.py +0 -0
  267. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_buffer.py +0 -0
  268. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_cell_text_plain_fastpath.py +0 -0
  269. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_collapsed_range_format.py +0 -0
  270. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_command_dataclasses.py +0 -0
  271. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_commands.py +0 -0
  272. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_comments.py +0 -0
  273. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_document_asset_id_property.py +0 -0
  274. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_document_create.py +0 -0
  275. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_document_create_from_template.py +0 -0
  276. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_document_factory_validation.py +0 -0
  277. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_e2e_partial_failure_cascade.py +0 -0
  278. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_http_transport.py +0 -0
  279. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_hyperlink_coalescing.py +0 -0
  280. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_insert_deferred.py +0 -0
  281. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_iter_inner_content.py +0 -0
  282. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_list_styles.py +0 -0
  283. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_merged_cells.py +0 -0
  284. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_oxml_shim.py +0 -0
  285. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_paragraph_text_len_cache.py +0 -0
  286. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_parity_misc.py +0 -0
  287. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_partial_failure_cascade.py +0 -0
  288. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_phase_a_behavior.py +0 -0
  289. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_phase_b_headers_footers.py +0 -0
  290. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_phase_c_tables.py +0 -0
  291. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_pr19766_review_fixes.py +0 -0
  292. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_ptc.py +0 -0
  293. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_python_docx_api_parity.py +0 -0
  294. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_revisions.py +0 -0
  295. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_add_paragraph_style.py +0 -0
  296. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_add_picture.py +0 -0
  297. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_add_run.py +0 -0
  298. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_cell_add_paragraph.py +0 -0
  299. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_comments_add_comment.py +0 -0
  300. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_comments_get.py +0 -0
  301. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_document_audit.py +0 -0
  302. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_document_element.py +0 -0
  303. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_enum_section.py +0 -0
  304. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_font_audit.py +0 -0
  305. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_header_footer.py +0 -0
  306. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_hyperlink.py +0 -0
  307. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_inline_shape.py +0 -0
  308. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_insert_paragraph_before.py +0 -0
  309. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_misc.py +0 -0
  310. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_paragraph_strict.py +0 -0
  311. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_paragraph_style.py +0 -0
  312. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_paragraph_style_strict.py +0 -0
  313. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_parfmt.py +0 -0
  314. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_row_col_cell.py +0 -0
  315. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_run_add_break.py +0 -0
  316. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_run_bool_setters.py +0 -0
  317. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_run_style.py +0 -0
  318. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_run_style_strict.py +0 -0
  319. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_run_text.py +0 -0
  320. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_run_underline.py +0 -0
  321. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_section_audit.py +0 -0
  322. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_section_dimensions.py +0 -0
  323. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_section_onoff.py +0 -0
  324. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_settings.py +0 -0
  325. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_shared_audit.py +0 -0
  326. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_style.py +0 -0
  327. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_styles.py +0 -0
  328. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_table_audit.py +0 -0
  329. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_table_cell.py +0 -0
  330. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_table_dimensions.py +0 -0
  331. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_silent_stub_table_layout.py +0 -0
  332. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_smoke_integration.py +0 -0
  333. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_style_acceptance.py +0 -0
  334. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_style_font.py +0 -0
  335. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_style_setters_contract.py +0 -0
  336. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_table_set_cell_perf.py +0 -0
  337. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_table_style_id_resolution.py +0 -0
  338. {athena_python_docx-0.10.1 → athena_python_docx-0.11.0}/tests/test_zod_wire_contract.py +0 -0
@@ -175,6 +175,174 @@ not file-backed. Each is documented in the relevant docstring.
175
175
  has zero track-changes API, so this entire surface is an Athena
176
176
  extension wired to SuperDoc's `doc.trackChanges.{list,get,decide}`.
177
177
 
178
+ ### Athena extensions for most-requested upstream features (v0.11.0+)
179
+
180
+ These surfaces are **additions** to python-docx, not deviations from
181
+ existing behavior — every upstream class, method, property, and
182
+ parameter remains 1:1 with python-docx 1.2.0. The parity ratchet at
183
+ `tests/parity/` is one-directional (upstream → athena) so Athena
184
+ extensions do not produce findings; they're listed here for human
185
+ review and so future contributors can find the rationale.
186
+
187
+ Each item below corresponds to a long-standing upstream feature gap.
188
+ Issue numbers reference `python-openxml/python-docx`.
189
+
190
+ - **`Paragraph.add_hyperlink(text, address, *, fragment=None,
191
+ tooltip=None) -> Hyperlink`** — appends a hyperlinked run.
192
+ python-docx surfaces `Hyperlink` as read-only; PR #74 has been open
193
+ since 2014 and is the single most-requested missing feature. Routes
194
+ through `CreateHyperlink` (SuperDoc `doc.hyperlinks.create`). Returns
195
+ the same `Hyperlink` class the read path already exposes. Also
196
+ available as `Run.add_hyperlink` for convenience.
197
+
198
+ - **`Run.add_field(kind, *, args=None, cached_result=None) -> Field`**
199
+ + **`Document.fields: Fields`** — generic Word field insertion.
200
+ Supports `"PAGE"`, `"NUMPAGES"`, `"DATE"`, `"TIME"`, `"AUTHOR"`,
201
+ `"FILENAME"`, `"NUMWORDS"`, `"REF"`, `"SEQ"`, `"TOC"`, and the
202
+ generic `"FIELD"` escape hatch. python-docx has no field API
203
+ whatsoever; issue #498 (page numbers) and #31 (23 comments) demand
204
+ this. `Fields` is sequence-like; `Fields.refresh_all(scope?)`
205
+ forces a recompute. Routes through `CreateField` / `FieldsRefresh`
206
+ / `FieldsList`.
207
+
208
+ - **`Document.bookmarks: Bookmarks`** + **`Paragraph.add_bookmark
209
+ (name)`** + **`Document.add_bookmark(name, *, paragraph=None,
210
+ run=None) -> Bookmark`** — named anchors for cross-references,
211
+ TOC entries, captions, and internal hyperlinks. python-docx
212
+ upstream has no bookmark API; issue #425 (35 comments) has been
213
+ open since 2017. Routes through `CreateBookmark` /
214
+ `DeleteBookmark` / `BookmarksList` / `BookmarksGet`.
215
+
216
+ - **`Document.footnotes: Footnotes`** + **`Run.add_footnote(text, *,
217
+ custom_mark=None) -> Footnote`** — footnote references with a
218
+ separate body story. python-docx Issue #1 (the first issue ever
219
+ filed, open since 2014) demands this; it drove the `bayoo-docx`
220
+ fork. Routes through `CreateFootnote` / `FootnotesList` /
221
+ `FootnotesGet` / `FootnotesPatch` / `FootnotesDelete`. The
222
+ parallel **`Document.endnotes: Endnotes`** + **`Run.add_endnote
223
+ (text, *, custom_mark=None)`** mirrors the same machinery for
224
+ end-of-section notes.
225
+
226
+ - **`Document.add_toc(*, levels=(1, 3), style_id=None,
227
+ include_hyperlinks=True, title=None, tab_leader="dot", at=None)
228
+ -> TableOfContents`** — inserts a TOC field at the cursor.
229
+ python-docx issue #36 (closed-without-action), #1207, #1403 demand
230
+ this. The returned `TableOfContents` proxy exposes `refresh()` to
231
+ recompute the cached result. Routes through `CreateTOC` /
232
+ `RefreshTOC`.
233
+
234
+ - **`Paragraph.add_caption(text, *, label="Figure", bookmark=None,
235
+ style=None) -> Paragraph`** — adds a caption paragraph with a SEQ
236
+ field for auto-numbering. python-docx issue #676 (11 👍). Routes
237
+ through `CreateCaption`.
238
+
239
+ - **`Run.add_cross_reference(target, *, reference_kind="text",
240
+ hyperlink=True) -> Field`** — REF field pointing at a bookmark or
241
+ SEQ. python-docx issue #97 (33 comments). Reference kinds:
242
+ `"text"`, `"number"`, `"page"`, `"label-number"`, `"above-below"`,
243
+ `"paragraph"`. Routes through `CreateCrossReference`.
244
+
245
+ - **`_Cell.set_borders(*, top=, bottom=, left=, right=,
246
+ inside_horizontal=, inside_vertical=)`** + **`_Cell.borders`**
247
+ property + **`Table.set_borders(...)`** — per-edge border control.
248
+ python-docx issue #201 (5 👍), #1238 demand a cell-borders API.
249
+ Routes through `SetCellBorders` / `SetTableBorders`.
250
+
251
+ - **`_Cell.set_shading(*, fill=, pattern=, color=)`** +
252
+ **`_Cell.shading`** property — cell fill / shading. python-docx
253
+ issue #146 (12 comments) demands this. Routes through
254
+ `SetCellShading`.
255
+
256
+ - **`_Cell.v_merge` / `_Cell.is_merge_continuation` /
257
+ `_Cell.is_merge_origin`** — robust merged-cell detection.
258
+ python-docx surfaces `_Cell.grid_span` (horizontal) but the
259
+ vertical merge state and master detection are issue #1312, #1434,
260
+ #1458. Pure derived properties from existing `tables.getCells`
261
+ data.
262
+
263
+ - **`Table.add_row(*, copy_format_from=None)`** — when `copy_format_from`
264
+ is provided (int row index or `_Row`), the new row inherits height,
265
+ borders, shading, vertical alignment, and run formatting from the
266
+ source row. python-docx issue #205 (9 👍). The no-arg form is
267
+ unchanged.
268
+
269
+ - **`InlineShape.alt_text` / `InlineShape.title` /
270
+ `InlineShape.decorative`** + mirrors on `Picture` — accessibility
271
+ metadata. python-docx PR #1530 is in flight but unreleased; the
272
+ feature has been requested for years. Routes through
273
+ `SetImageAltText` / `SetImageDecorative` (already wired in
274
+ SuperDoc).
275
+
276
+ - **`InlineShape.anchor(*, wrap="square", position_x_pt=,
277
+ position_y_pt=, relative_from_x=, relative_from_y=,
278
+ behind_text=)`** — promote an inline image to a floating
279
+ (anchored) image. python-docx is inline-only; issue #159 has been
280
+ open since 2017. Wrap modes: `"inline"`, `"square"`, `"tight"`,
281
+ `"through"`, `"top-bottom"`, `"behind"`, `"in-front"`. Routes
282
+ through `SetImageAnchor`.
283
+
284
+ - **`Document.add_watermark(*, text=None, image_path=None, ...)
285
+ -> str`** — page-watermark on every page. python-docx issue #845
286
+ (9 👍). Pass either `text` or `image_path`. Routes through
287
+ `CreateWatermark`. `Document.delete_watermark` is available via
288
+ the lower-level `DeleteWatermark` command.
289
+
290
+ - **`Document.charts: Charts`** + **`Document.add_chart(chart_type,
291
+ data, *, title=None, width_pt=None, height_pt=None, at=None)
292
+ -> Chart`** — chart insertion + data editing. python-docx issue
293
+ #179 (12 👍, 19 comments) demands creation, #1141 demands editing.
294
+ `chart_type` covers all Word 2016+ families. `Chart.data` setter
295
+ replaces the underlying table; `Chart.chart_type` setter switches
296
+ family. Routes through `CreateChart` / `SetChartData` /
297
+ `SetChartType` / `ChartsList` / `ChartsGet`.
298
+
299
+ - **`Run.add_math(content, *, format="mathml", display=False)
300
+ -> MathEquation`** — math equations. python-docx issue #320 (23
301
+ comments). Formats: `"mathml"`, `"omml"` (Word native), `"latex"`
302
+ (server-converted). Routes through `CreateMath` / `MathPatch`.
303
+
304
+ - **`Document.content_controls: ContentControls`** +
305
+ **`Document.add_content_control(control_kind, ...) -> ContentControl`**
306
+ — structured-document-tag (SDT) wrappers. python-docx issues #155,
307
+ #224 (15 comments), #1417 demand write support. Kinds:
308
+ `"rich-text"`, `"plain-text"`, `"picture"`, `"checkbox"`,
309
+ `"date"`, `"dropdown"`, `"combobox"`, `"repeating-section"`,
310
+ `"group"`. Routes through `CreateContentControl` /
311
+ `ContentControlsList` / `ContentControlsGet` /
312
+ `ContentControlsPatch` / `ContentControlsDelete`.
313
+
314
+ - **`Section.set_columns(*, count, equal_width=None, widths=None,
315
+ space_pt=None, separator=None)`** + **`Section.column_count`** +
316
+ **`Section.column_widths`** — multi-column section layout.
317
+ python-docx issue #167 (12 comments). Routes through
318
+ `SetSectionColumns`.
319
+
320
+ - **`Document.find_replace(pattern, replacement, *, regex=False,
321
+ match_case=False, whole_word=False, in_=None, max_replacements=None)
322
+ -> int`** — formatting-preserving find-and-replace primitive.
323
+ python-docx issues #30 (39 comments), #980 (12 👍) demand the
324
+ run-isolation primitive this provides. `in_` scopes to
325
+ `"body"` / `"headers"` / `"footers"` / `"footnotes"` /
326
+ `"endnotes"` / `"comments"` / `"all"`. Routes through
327
+ `FindReplace`.
328
+
329
+ - **`Paragraph.list_id` / `Paragraph.list_level` /
330
+ `Paragraph.list_format` / `Paragraph.list_number`** — list-item
331
+ numbering reads. python-docx issue #471 (13 👍) demands access to
332
+ list metadata. Routes through `NumberingGet`.
333
+
334
+ - **`Document.to_pdf(*, quality=None, destination=None,
335
+ include_revisions=False) -> bytes`** — render to PDF via the
336
+ Daytona-hosted LibreOffice. python-docx issue #113 was closed as
337
+ wontfix upstream but the request keeps recurring. Routes through
338
+ `ExportPDF` (SuperDoc handles the actual conversion).
339
+
340
+ These additions are surfaced under their natural python-docx-shaped
341
+ names. When python-docx upstream eventually ships any of them
342
+ natively, the SDK should swap to the upstream signature in place;
343
+ this section is the canonical reference for what each Athena
344
+ extension wires up.
345
+
178
346
  ### If you need a deviation
179
347
 
180
348
  If there is a genuine technical reason why a deviation from python-docx is necessary:
@@ -192,10 +360,16 @@ This is a **thin HTTP client** that mimics the sync python-docx API.
192
360
  - Every internal call constructs a typed `Command` dataclass
193
361
  (`docx.commands`) and ships it through a `CommandBuffer`
194
362
  (`docx._buffer`) which POSTs to docx-studio's `/docs/:id/commands`.
195
- - HTTP transport: `requests.Session` with a Retry (3 attempts, 0.5s
196
- backoff, 429/502/503/504). The legacy `transport="direct"` (embedded
197
- Superdoc CLI + y-websocket from Python) was removed in 0.5.0; agent
198
- pods no longer embed Superdoc.
363
+ - HTTP transport: `requests.Session` with a Retry (3 attempts, exponential
364
+ backoff `factor=2.0` plus 0.5s jitter when urllib3 ≥2.0 supports it,
365
+ ~14s total budget, 429/502/503/504). Once retries are exhausted on
366
+ 502/503/504, the SDK raises `DocxStudioTemporarilyUnavailable`
367
+ (a `SessionError`) carrying `http_status` and an optional
368
+ `retry_after_seconds` parsed from the server's `Retry-After` header.
369
+ Agent code can catch this typed error to back off at the agent level
370
+ vs. treating it as a script bug. The legacy `transport="direct"`
371
+ (embedded Superdoc CLI + y-websocket from Python) was removed in
372
+ 0.5.0; agent pods no longer embed Superdoc.
199
373
  - **Transparent batching (0.7.0+):** Create* commands
200
374
  (`CreateParagraph`, `CreateHeading`, `CreateTable`, `CreateImage`)
201
375
  ALWAYS pre-mint a client-side UUID (`client_node_id`) and defer
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: athena-python-docx
3
- Version: 0.10.1
3
+ Version: 0.11.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>
@@ -0,0 +1,46 @@
1
+ """athena-python-docx — drop-in replacement for python-docx.
2
+
3
+ Calls translate into Superdoc SDK operations against a Keryx Y.Doc.
4
+ See CLAUDE.md for the API parity contract.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ __version__ = "0.11.0"
10
+
11
+ from docx.api import Document
12
+ from docx._buffer import flush_all
13
+ # Re-exports python-docx ships at docx top-level for convenience.
14
+ from docx.shared import Emu, Inches, Pt, Cm, Mm, Twips, Length, RGBColor
15
+
16
+ __all__ = [
17
+ "Document",
18
+ "Emu",
19
+ "Inches",
20
+ "Pt",
21
+ "Cm",
22
+ "Mm",
23
+ "Twips",
24
+ "Length",
25
+ "RGBColor",
26
+ "flush_all",
27
+ "__version__",
28
+ ]
29
+
30
+
31
+ # ---------------------------------------------------------------------------
32
+ # Athena-extension modules (no python-docx upstream parity surface). These
33
+ # are documented in python-sdk/CLAUDE.md § "Intentional deviations (additions
34
+ # / different semantics)" so the parity ratchet at tests/parity/ doesn't
35
+ # regress them. We don't auto-import them here because every one adds
36
+ # import-time cost for the common ``Document(asset_id)`` path; import the
37
+ # specific module you need:
38
+ #
39
+ # from docx.bookmarks import Bookmark
40
+ # from docx.fields import Field
41
+ # from docx.footnotes import Footnote, Endnote
42
+ # from docx.toc import TableOfContents
43
+ # from docx.charts import Chart
44
+ # from docx.math import MathEquation
45
+ # from docx.sdt import ContentControl
46
+ # ---------------------------------------------------------------------------
@@ -0,0 +1,384 @@
1
+ """Marker decorator + audit helpers for Athena extensions beyond
2
+ python-docx 1.x.
3
+
4
+ This SDK MUST be a 1:1 replica of python-docx (see ``CLAUDE.md`` § *API
5
+ Parity Rule*). Anything that is NOT in upstream python-docx is an
6
+ **Athena extension** and must be:
7
+
8
+ 1. Documented in ``CLAUDE.md`` § *Intentional deviations (additions /
9
+ different semantics)* — for the human-readable rationale.
10
+ 2. Marked at runtime with :func:`athena_extension` — so introspection
11
+ tooling (parity audits, docs generation, IDE plugins) can identify
12
+ the addition without grepping prose, and so the
13
+ :func:`iter_athena_extensions` walker can enumerate every addition
14
+ in one place.
15
+
16
+ The decorator stamps the wrapped object with introspectable attributes:
17
+
18
+ * ``__athena_extension__`` — always ``True``
19
+ * ``__athena_extension_issue__`` — upstream python-docx issue ref, e.g.
20
+ ``"python-docx#74"``
21
+ * ``__athena_extension_description__`` — one-line summary
22
+ * ``__athena_extension_since__`` — athena-python-docx SDK version that
23
+ introduced the surface
24
+
25
+ Apply at every Athena-extension layer:
26
+
27
+ * **Class** — decorator on the ``class`` statement::
28
+
29
+ @athena_extension(issue=1, description="Footnote proxy")
30
+ class Footnote: ...
31
+
32
+ * **Method / function** — decorator on the ``def`` statement::
33
+
34
+ @athena_extension(issue=74, description="Paragraph.add_hyperlink")
35
+ def add_hyperlink(self, ...): ...
36
+
37
+ * **Property** — decorator on the **getter** (before ``@property``)::
38
+
39
+ @property
40
+ @athena_extension(issue=471)
41
+ def list_id(self): ...
42
+
43
+ When the property has a setter, mark the setter's underlying function
44
+ too (so writes are auditable independently of reads)::
45
+
46
+ @list_id.setter
47
+ @athena_extension(issue=471)
48
+ def list_id(self, value): ...
49
+
50
+ * **Module-level constant** — for whole modules that are entirely
51
+ Athena extensions, set at top of file::
52
+
53
+ __athena_extension_module__ = True
54
+ __athena_extension_issue__ = "python-docx#1"
55
+ __athena_extension_description__ = "Footnotes / Endnotes"
56
+
57
+ The walker picks these up alongside per-member markers.
58
+
59
+ The decorator is import-time-cheap, has no runtime overhead at call
60
+ sites (it returns the original object after stamping), and never
61
+ modifies docstrings or signatures. The introspectable attributes are
62
+ parity-neutral — the upstream-vs-athena comparator in
63
+ ``tests/parity/compare.py`` only looks at signatures + kinds, not at
64
+ function ``__dict__`` keys.
65
+ """
66
+
67
+ from __future__ import annotations
68
+
69
+ import importlib
70
+ import pkgutil
71
+ from typing import Any, Callable, Iterator, TypeVar
72
+
73
+
74
+ # Attribute names — kept as module constants so audits and tests can
75
+ # reference them without re-typing the string everywhere.
76
+ ATHENA_EXTENSION_ATTR: str = "__athena_extension__"
77
+ ATHENA_EXTENSION_ISSUE_ATTR: str = "__athena_extension_issue__"
78
+ ATHENA_EXTENSION_DESCRIPTION_ATTR: str = "__athena_extension_description__"
79
+ ATHENA_EXTENSION_SINCE_ATTR: str = "__athena_extension_since__"
80
+
81
+ ATHENA_EXTENSION_MODULE_ATTR: str = "__athena_extension_module__"
82
+
83
+ # Default ``since`` value — overridable per-marker. Bumped when a new
84
+ # wave of additions lands; pre-existing extensions (Document.create,
85
+ # Document.revisions, etc.) carry their original since values.
86
+ _DEFAULT_SINCE: str = "0.11.0"
87
+
88
+
89
+ T = TypeVar("T")
90
+
91
+
92
+ def athena_extension(
93
+ *,
94
+ issue: int | str | None = None,
95
+ description: str = "",
96
+ since: str = _DEFAULT_SINCE,
97
+ ) -> Callable[[T], T]:
98
+ """Decorator that marks a class / function / method as an Athena
99
+ extension beyond python-docx 1.x.
100
+
101
+ Stamps four introspectable attributes on the wrapped object:
102
+
103
+ - ``__athena_extension__`` — always ``True``
104
+ - ``__athena_extension_issue__`` — ``"python-docx#<n>"`` when
105
+ ``issue`` is an int; passed through verbatim when a string;
106
+ ``None`` when omitted
107
+ - ``__athena_extension_description__`` — ``description``
108
+ - ``__athena_extension_since__`` — ``since``
109
+
110
+ Returns the original object unchanged (apart from the new
111
+ attributes), so call sites pay zero runtime cost.
112
+
113
+ Args:
114
+ issue: python-docx upstream issue / PR number that this
115
+ extension addresses. ``int`` (e.g. ``74``) is auto-formatted
116
+ as ``"python-docx#74"``; ``str`` is used verbatim.
117
+ description: One-line human summary. Optional but recommended
118
+ for non-obvious additions.
119
+ since: The athena-python-docx SDK version that first shipped
120
+ this surface. Defaults to the current default-since
121
+ (``0.11.0``).
122
+
123
+ Example::
124
+
125
+ @athena_extension(issue=74, description="Add hyperlink (closes issue #74)")
126
+ def add_hyperlink(self, text, address, *, fragment=None, tooltip=None):
127
+ ...
128
+
129
+ For properties, decorate the underlying getter (and setter) BEFORE
130
+ ``@property`` so the markers stick to ``prop.fget`` /
131
+ ``prop.fset``::
132
+
133
+ @property
134
+ @athena_extension(issue=471)
135
+ def list_id(self): ...
136
+ """
137
+ issue_value: str | None
138
+ if issue is None:
139
+ issue_value = None
140
+ elif isinstance(issue, int):
141
+ issue_value = f"python-docx#{issue}"
142
+ else:
143
+ issue_value = str(issue)
144
+
145
+ def decorator(obj: T) -> T:
146
+ # ``setattr`` instead of ``obj.__athena_extension__ = …`` so the
147
+ # decorator works on classes, plain functions, lambdas, and any
148
+ # object whose namespace accepts attribute assignment. Built-in
149
+ # ``property`` descriptors don't (which is why callers must
150
+ # decorate the underlying getter, not the property itself).
151
+ try:
152
+ setattr(obj, ATHENA_EXTENSION_ATTR, True)
153
+ if issue_value is not None:
154
+ setattr(obj, ATHENA_EXTENSION_ISSUE_ATTR, issue_value)
155
+ if description:
156
+ setattr(obj, ATHENA_EXTENSION_DESCRIPTION_ATTR, description)
157
+ setattr(obj, ATHENA_EXTENSION_SINCE_ATTR, since)
158
+ except (AttributeError, TypeError):
159
+ # E.g. someone applied the decorator directly to a
160
+ # ``property`` object (which rejects setattr). Re-raise with
161
+ # a hint so the fix is obvious.
162
+ raise TypeError(
163
+ f"@athena_extension cannot be applied to "
164
+ f"{type(obj).__name__} — built-in descriptor rejects "
165
+ f"setattr. For properties, decorate the underlying "
166
+ f"getter/setter function BEFORE @property:\n\n"
167
+ f" @property\n"
168
+ f" @athena_extension(...)\n"
169
+ f" def my_prop(self): ...\n",
170
+ )
171
+ return obj
172
+
173
+ return decorator
174
+
175
+
176
+ def is_athena_extension(obj: Any) -> bool:
177
+ """Return ``True`` if ``obj`` is marked as an Athena extension.
178
+
179
+ Handles the property-wrapping case: a ``property`` descriptor
180
+ proxies to its ``fget`` (and ``fset``) — either marker counts.
181
+ """
182
+ if getattr(obj, ATHENA_EXTENSION_ATTR, False):
183
+ return True
184
+ if isinstance(obj, property):
185
+ if obj.fget is not None and getattr(obj.fget, ATHENA_EXTENSION_ATTR, False):
186
+ return True
187
+ if obj.fset is not None and getattr(obj.fset, ATHENA_EXTENSION_ATTR, False):
188
+ return True
189
+ return False
190
+
191
+
192
+ def athena_extension_metadata(obj: Any) -> dict[str, Any] | None:
193
+ """Return a dict of marker attributes for ``obj``, or ``None`` if
194
+ it isn't an Athena extension."""
195
+ target: Any = obj
196
+ if isinstance(obj, property):
197
+ if obj.fget is not None and getattr(obj.fget, ATHENA_EXTENSION_ATTR, False):
198
+ target = obj.fget
199
+ elif obj.fset is not None and getattr(obj.fset, ATHENA_EXTENSION_ATTR, False):
200
+ target = obj.fset
201
+ else:
202
+ return None
203
+ if not getattr(target, ATHENA_EXTENSION_ATTR, False):
204
+ return None
205
+ return {
206
+ "issue": getattr(target, ATHENA_EXTENSION_ISSUE_ATTR, None),
207
+ "description": getattr(target, ATHENA_EXTENSION_DESCRIPTION_ATTR, ""),
208
+ "since": getattr(target, ATHENA_EXTENSION_SINCE_ATTR, None),
209
+ }
210
+
211
+
212
+ # Module-level helpers should NOT pull these in transitively at SDK
213
+ # import time — keep the walker lazy.
214
+ def iter_athena_extensions(package: str = "docx") -> Iterator[dict[str, Any]]:
215
+ """Walk every submodule of ``package`` and yield one record per
216
+ Athena extension found.
217
+
218
+ Records have shape::
219
+
220
+ {
221
+ "kind": "module" | "class" | "function" | "method" |
222
+ "property" | "command",
223
+ "location": "docx.bookmarks.Bookmarks.add",
224
+ "issue": "python-docx#74",
225
+ "description": "Append a hyperlinked text run",
226
+ "since": "0.11.0",
227
+ }
228
+
229
+ The walker:
230
+
231
+ * Imports every module under ``package`` (it tolerates import
232
+ errors gracefully — a broken module is skipped, not fatal).
233
+ * Yields a ``"module"`` record for any submodule with
234
+ ``__athena_extension_module__ = True`` at module scope.
235
+ * Yields one record per top-level callable / class with the
236
+ marker.
237
+ * For each marked class, walks the class body and yields a
238
+ ``"method"`` / ``"property"`` record per marked member.
239
+
240
+ Used by ``tests/test_athena_extensions_registry.py`` to verify
241
+ coverage stays in sync with the surfaces documented in CLAUDE.md.
242
+ """
243
+ try:
244
+ root = importlib.import_module(package)
245
+ except ImportError:
246
+ return
247
+
248
+ # The root package itself may carry the module-level marker.
249
+ if getattr(root, ATHENA_EXTENSION_MODULE_ATTR, False):
250
+ yield {
251
+ "kind": "module",
252
+ "location": package,
253
+ "issue": getattr(root, ATHENA_EXTENSION_ISSUE_ATTR, None),
254
+ "description": getattr(root, ATHENA_EXTENSION_DESCRIPTION_ATTR, ""),
255
+ "since": getattr(root, ATHENA_EXTENSION_SINCE_ATTR, None),
256
+ }
257
+ yield from _iter_module_members(root, package)
258
+
259
+ if not hasattr(root, "__path__"):
260
+ return
261
+
262
+ for _finder, sub_name, _ispkg in pkgutil.walk_packages(
263
+ root.__path__, prefix=f"{package}.",
264
+ ):
265
+ try:
266
+ sub = importlib.import_module(sub_name)
267
+ except Exception: # noqa: BLE001 — broken submodule, skip.
268
+ continue
269
+ if getattr(sub, ATHENA_EXTENSION_MODULE_ATTR, False):
270
+ yield {
271
+ "kind": "module",
272
+ "location": sub_name,
273
+ "issue": getattr(sub, ATHENA_EXTENSION_ISSUE_ATTR, None),
274
+ "description": getattr(sub, ATHENA_EXTENSION_DESCRIPTION_ATTR, ""),
275
+ "since": getattr(sub, ATHENA_EXTENSION_SINCE_ATTR, None),
276
+ }
277
+ yield from _iter_module_members(sub, sub_name)
278
+
279
+
280
+ def _iter_module_members(
281
+ module: Any, module_name: str,
282
+ ) -> Iterator[dict[str, Any]]:
283
+ """Walk ``vars(module)`` and yield extension records for top-level
284
+ callables / classes. For each class, recurse into its members.
285
+
286
+ Underscore-prefixed *classes* (``_Cell``, ``_Row``, ``_Column``,
287
+ ``_NoteBase``, etc.) ARE walked — python-docx uses leading-
288
+ underscore class names for collection types that are part of the
289
+ public API. Underscore-prefixed *functions* / module attrs are
290
+ treated as private and skipped.
291
+ """
292
+ seen_classes: set[int] = set()
293
+ for attr_name, attr in vars(module).items():
294
+ is_class = isinstance(attr, type)
295
+ if attr_name.startswith("_") and not is_class:
296
+ continue
297
+ # Top-level marked function / dataclass.
298
+ if (
299
+ not is_class
300
+ and getattr(attr, ATHENA_EXTENSION_ATTR, False)
301
+ ):
302
+ kind = "command" if attr_name[0:1].isupper() else "function"
303
+ meta = athena_extension_metadata(attr) or {}
304
+ yield {
305
+ "kind": kind,
306
+ "location": f"{module_name}.{attr_name}",
307
+ **meta,
308
+ }
309
+ # Class: emit the class record (if marked) then recurse.
310
+ if is_class:
311
+ qual = getattr(attr, "__qualname__", attr_name)
312
+ mod_of_class = getattr(attr, "__module__", "")
313
+ # Skip re-exports — only emit the canonical definition's
314
+ # members. (E.g. ``docx.shape.Picture`` is re-exported from
315
+ # ``docx.shape``; emit once.)
316
+ if mod_of_class and mod_of_class != module_name:
317
+ continue
318
+ if id(attr) in seen_classes:
319
+ continue
320
+ seen_classes.add(id(attr))
321
+ if getattr(attr, ATHENA_EXTENSION_ATTR, False):
322
+ meta = athena_extension_metadata(attr) or {}
323
+ yield {
324
+ "kind": "class",
325
+ "location": f"{module_name}.{qual}",
326
+ **meta,
327
+ }
328
+ yield from _iter_class_members(attr, module_name, qual)
329
+
330
+
331
+ def _iter_class_members(
332
+ cls: type, module_name: str, class_qualname: str,
333
+ ) -> Iterator[dict[str, Any]]:
334
+ """Yield records for marked methods / properties on ``cls``.
335
+
336
+ Handles three wrapped-callable cases the bare
337
+ ``getattr(member, ATTR, False)`` check would miss:
338
+
339
+ * ``property`` — read marker from ``fget`` / ``fset``.
340
+ * ``classmethod`` — marker lives on ``__func__``.
341
+ * ``staticmethod`` — marker lives on ``__func__``.
342
+ """
343
+ for member_name, member in vars(cls).items():
344
+ if member_name.startswith("_"):
345
+ continue
346
+ # Peek into classmethod / staticmethod descriptors — the
347
+ # decorator wraps the underlying function, so the marker
348
+ # attribute lives on ``descriptor.__func__``.
349
+ probe: Any = member
350
+ if isinstance(member, (classmethod, staticmethod)):
351
+ probe = member.__func__
352
+ meta = athena_extension_metadata(probe)
353
+ if meta is None and probe is not member:
354
+ # Fall back to checking the descriptor itself (covers any
355
+ # marker explicitly applied to the wrapper).
356
+ meta = athena_extension_metadata(member)
357
+ if meta is None:
358
+ continue
359
+ if isinstance(member, property):
360
+ kind = "property"
361
+ elif isinstance(member, classmethod):
362
+ kind = "classmethod"
363
+ elif isinstance(member, staticmethod):
364
+ kind = "staticmethod"
365
+ else:
366
+ kind = "method"
367
+ yield {
368
+ "kind": kind,
369
+ "location": f"{module_name}.{class_qualname}.{member_name}",
370
+ **meta,
371
+ }
372
+
373
+
374
+ __all__ = [
375
+ "athena_extension",
376
+ "is_athena_extension",
377
+ "athena_extension_metadata",
378
+ "iter_athena_extensions",
379
+ "ATHENA_EXTENSION_ATTR",
380
+ "ATHENA_EXTENSION_ISSUE_ATTR",
381
+ "ATHENA_EXTENSION_DESCRIPTION_ATTR",
382
+ "ATHENA_EXTENSION_SINCE_ATTR",
383
+ "ATHENA_EXTENSION_MODULE_ATTR",
384
+ ]