lexidown 0.1.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 (397) hide show
  1. lexidown-0.1.0/DEVELOPMENT.md +179 -0
  2. lexidown-0.1.0/LICENSE +24 -0
  3. lexidown-0.1.0/MANIFEST.in +7 -0
  4. lexidown-0.1.0/PKG-INFO +259 -0
  5. lexidown-0.1.0/README.md +230 -0
  6. lexidown-0.1.0/THIRD_PARTY_NOTICES.md +65 -0
  7. lexidown-0.1.0/benchmarks/COMPARISON.md +74 -0
  8. lexidown-0.1.0/benchmarks/REPORT.md +282 -0
  9. lexidown-0.1.0/benchmarks/comparison-example.json +13 -0
  10. lexidown-0.1.0/benchmarks/comparison-results.json +7920 -0
  11. lexidown-0.1.0/benchmarks/requirements.txt +10 -0
  12. lexidown-0.1.0/benchmarks/results.json +25933 -0
  13. lexidown-0.1.0/benchmarks/sources.json +108 -0
  14. lexidown-0.1.0/examples/formatting.py +85 -0
  15. lexidown-0.1.0/package-lock.json +35 -0
  16. lexidown-0.1.0/package.json +9 -0
  17. lexidown-0.1.0/pyproject.toml +68 -0
  18. lexidown-0.1.0/scripts/benchmark.py +531 -0
  19. lexidown-0.1.0/scripts/benchmark_js.cjs +39 -0
  20. lexidown-0.1.0/scripts/benchmark_python.py +50 -0
  21. lexidown-0.1.0/scripts/check_wheel.py +49 -0
  22. lexidown-0.1.0/scripts/compare_converters.py +519 -0
  23. lexidown-0.1.0/scripts/differential.py +458 -0
  24. lexidown-0.1.0/scripts/js_oracle.cjs +41 -0
  25. lexidown-0.1.0/scripts/prepare_release.py +112 -0
  26. lexidown-0.1.0/setup.cfg +4 -0
  27. lexidown-0.1.0/setup.py +36 -0
  28. lexidown-0.1.0/src/lexidown/__init__.py +7 -0
  29. lexidown-0.1.0/src/lexidown/_lexbor.pxd +110 -0
  30. lexidown-0.1.0/src/lexidown/_native.pyx +405 -0
  31. lexidown-0.1.0/src/lexidown/_native_dom.pxi +939 -0
  32. lexidown-0.1.0/src/lexidown/_native_rules.pxi +324 -0
  33. lexidown-0.1.0/src/lexidown/commonmark.py +5 -0
  34. lexidown-0.1.0/src/lexidown/dom.py +20 -0
  35. lexidown-0.1.0/src/lexidown/html_parser.py +10 -0
  36. lexidown-0.1.0/src/lexidown/native_parser.py +5 -0
  37. lexidown-0.1.0/src/lexidown/service.py +11 -0
  38. lexidown-0.1.0/src/lexidown/utilities.py +35 -0
  39. lexidown-0.1.0/src/lexidown.egg-info/PKG-INFO +259 -0
  40. lexidown-0.1.0/src/lexidown.egg-info/SOURCES.txt +395 -0
  41. lexidown-0.1.0/src/lexidown.egg-info/dependency_links.txt +1 -0
  42. lexidown-0.1.0/src/lexidown.egg-info/requires.txt +5 -0
  43. lexidown-0.1.0/src/lexidown.egg-info/top_level.txt +1 -0
  44. lexidown-0.1.0/tests/__init__.py +0 -0
  45. lexidown-0.1.0/tests/fixtures/turndown.json +1082 -0
  46. lexidown-0.1.0/tests/test_benchmark.py +33 -0
  47. lexidown-0.1.0/tests/test_comparison.py +40 -0
  48. lexidown-0.1.0/tests/test_compiled_dom.py +104 -0
  49. lexidown-0.1.0/tests/test_compiled_rules.py +60 -0
  50. lexidown-0.1.0/tests/test_dom_optimization.py +46 -0
  51. lexidown-0.1.0/tests/test_native_parser.py +147 -0
  52. lexidown-0.1.0/tests/test_optimization.py +60 -0
  53. lexidown-0.1.0/tests/test_parser_optimization.py +36 -0
  54. lexidown-0.1.0/tests/test_release.py +134 -0
  55. lexidown-0.1.0/tests/test_serialization.py +83 -0
  56. lexidown-0.1.0/tests/test_turndown.py +429 -0
  57. lexidown-0.1.0/vendor/lexbor/BSD-LICENSE +32 -0
  58. lexidown-0.1.0/vendor/lexbor/LICENSE +223 -0
  59. lexidown-0.1.0/vendor/lexbor/NOTICE +16 -0
  60. lexidown-0.1.0/vendor/lexbor/README.md +56 -0
  61. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/array.c +208 -0
  62. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/array.h +100 -0
  63. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/array_obj.c +216 -0
  64. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/array_obj.h +134 -0
  65. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/avl.c +525 -0
  66. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/avl.h +92 -0
  67. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/base.h +108 -0
  68. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/bst.c +471 -0
  69. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/bst.h +108 -0
  70. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/bst_map.c +238 -0
  71. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/bst_map.h +87 -0
  72. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/conv.c +351 -0
  73. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/conv.h +62 -0
  74. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/core.h +35 -0
  75. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/def.h +59 -0
  76. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/diyfp.c +181 -0
  77. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/diyfp.h +285 -0
  78. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/dobject.c +187 -0
  79. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/dobject.h +88 -0
  80. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/dtoa.c +401 -0
  81. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/dtoa.h +54 -0
  82. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/fs.h +60 -0
  83. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/hash.c +477 -0
  84. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/hash.h +218 -0
  85. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/in.c +266 -0
  86. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/in.h +172 -0
  87. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/lexbor.h +43 -0
  88. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/mem.c +228 -0
  89. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/mem.h +141 -0
  90. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/mraw.c +421 -0
  91. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/mraw.h +114 -0
  92. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/perf.h +40 -0
  93. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/plog.c +73 -0
  94. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/plog.h +102 -0
  95. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/print.c +168 -0
  96. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/print.h +39 -0
  97. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/sbst.h +59 -0
  98. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/serialize.c +27 -0
  99. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/serialize.h +32 -0
  100. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/shs.c +118 -0
  101. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/shs.h +82 -0
  102. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/str.c +667 -0
  103. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/str.h +257 -0
  104. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/str_res.h +360 -0
  105. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/strtod.c +348 -0
  106. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/strtod.h +54 -0
  107. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/swar.h +97 -0
  108. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/types.h +39 -0
  109. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/utils.c +43 -0
  110. lexidown-0.1.0/vendor/lexbor/source/lexbor/core/utils.h +36 -0
  111. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/base.h +32 -0
  112. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/collection.c +97 -0
  113. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/collection.h +112 -0
  114. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/dom.h +29 -0
  115. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/exception.c +372 -0
  116. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/exception.h +99 -0
  117. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interface.c +110 -0
  118. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interface.h +86 -0
  119. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/attr.c +534 -0
  120. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/attr.h +141 -0
  121. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/attr_const.h +99 -0
  122. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/attr_res.h +263 -0
  123. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/cdata_section.c +55 -0
  124. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/cdata_section.h +38 -0
  125. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/character_data.c +116 -0
  126. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/character_data.h +51 -0
  127. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/comment.c +64 -0
  128. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/comment.h +42 -0
  129. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/document.c +593 -0
  130. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/document.h +336 -0
  131. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/document_fragment.c +36 -0
  132. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/document_fragment.h +36 -0
  133. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/document_type.c +232 -0
  134. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/document_type.h +135 -0
  135. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/element.c +896 -0
  136. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/element.h +361 -0
  137. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/event_target.c +32 -0
  138. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/event_target.h +34 -0
  139. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/node.c +1866 -0
  140. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/node.h +396 -0
  141. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/processing_instruction.c +92 -0
  142. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/processing_instruction.h +66 -0
  143. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/shadow_root.c +36 -0
  144. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/shadow_root.h +44 -0
  145. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/text.c +63 -0
  146. lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/text.h +42 -0
  147. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/attribute_steps.c +159 -0
  148. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/attribute_steps.h +49 -0
  149. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/attribute_steps_res.h +427 -0
  150. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/base.h +44 -0
  151. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/common.c +115 -0
  152. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/common.h +32 -0
  153. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/element_steps.c +220 -0
  154. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/element_steps.h +47 -0
  155. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/element_steps_res.h +427 -0
  156. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/encoding.c +670 -0
  157. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/encoding.h +243 -0
  158. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/html.h +109 -0
  159. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interface.c +168 -0
  160. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interface.h +189 -0
  161. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interface_res.h +5456 -0
  162. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/anchor_element.c +35 -0
  163. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/anchor_element.h +34 -0
  164. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/area_element.c +35 -0
  165. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/area_element.h +34 -0
  166. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/audio_element.c +35 -0
  167. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/audio_element.h +34 -0
  168. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/base_element.c +35 -0
  169. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/base_element.h +34 -0
  170. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/body_element.c +35 -0
  171. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/body_element.h +34 -0
  172. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/br_element.c +35 -0
  173. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/br_element.h +34 -0
  174. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/button_element.c +35 -0
  175. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/button_element.h +34 -0
  176. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/canvas_element.c +35 -0
  177. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/canvas_element.h +34 -0
  178. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/d_list_element.c +35 -0
  179. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/d_list_element.h +34 -0
  180. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/data_element.c +35 -0
  181. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/data_element.h +34 -0
  182. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/data_list_element.c +35 -0
  183. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/data_list_element.h +34 -0
  184. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/details_element.c +35 -0
  185. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/details_element.h +34 -0
  186. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/dialog_element.c +35 -0
  187. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/dialog_element.h +34 -0
  188. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/directory_element.c +35 -0
  189. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/directory_element.h +34 -0
  190. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/div_element.c +35 -0
  191. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/div_element.h +34 -0
  192. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/document.c +512 -0
  193. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/document.h +313 -0
  194. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/element.c +145 -0
  195. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/element.h +137 -0
  196. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/embed_element.c +35 -0
  197. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/embed_element.h +34 -0
  198. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/field_set_element.c +35 -0
  199. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/field_set_element.h +34 -0
  200. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/font_element.c +35 -0
  201. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/font_element.h +34 -0
  202. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/form_element.c +35 -0
  203. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/form_element.h +34 -0
  204. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/frame_element.c +35 -0
  205. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/frame_element.h +34 -0
  206. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/frame_set_element.c +35 -0
  207. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/frame_set_element.h +34 -0
  208. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/head_element.c +35 -0
  209. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/head_element.h +34 -0
  210. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/heading_element.c +35 -0
  211. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/heading_element.h +34 -0
  212. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/hr_element.c +35 -0
  213. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/hr_element.h +34 -0
  214. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/html_element.c +35 -0
  215. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/html_element.h +34 -0
  216. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/iframe_element.c +35 -0
  217. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/iframe_element.h +34 -0
  218. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/image_element.c +35 -0
  219. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/image_element.h +34 -0
  220. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/input_element.c +35 -0
  221. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/input_element.h +34 -0
  222. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/label_element.c +35 -0
  223. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/label_element.h +34 -0
  224. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/legend_element.c +35 -0
  225. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/legend_element.h +34 -0
  226. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/li_element.c +35 -0
  227. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/li_element.h +34 -0
  228. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/link_element.c +35 -0
  229. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/link_element.h +34 -0
  230. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/map_element.c +35 -0
  231. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/map_element.h +34 -0
  232. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/marquee_element.c +35 -0
  233. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/marquee_element.h +34 -0
  234. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/media_element.c +35 -0
  235. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/media_element.h +34 -0
  236. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/menu_element.c +35 -0
  237. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/menu_element.h +34 -0
  238. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/meta_element.c +35 -0
  239. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/meta_element.h +34 -0
  240. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/meter_element.c +35 -0
  241. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/meter_element.h +34 -0
  242. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/mod_element.c +35 -0
  243. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/mod_element.h +34 -0
  244. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/o_list_element.c +35 -0
  245. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/o_list_element.h +34 -0
  246. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/object_element.c +35 -0
  247. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/object_element.h +34 -0
  248. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/opt_group_element.c +35 -0
  249. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/opt_group_element.h +34 -0
  250. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/option_element.c +260 -0
  251. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/option_element.h +71 -0
  252. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/output_element.c +35 -0
  253. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/output_element.h +34 -0
  254. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/paragraph_element.c +35 -0
  255. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/paragraph_element.h +34 -0
  256. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/param_element.c +35 -0
  257. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/param_element.h +34 -0
  258. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/picture_element.c +35 -0
  259. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/picture_element.h +34 -0
  260. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/pre_element.c +35 -0
  261. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/pre_element.h +34 -0
  262. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/progress_element.c +35 -0
  263. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/progress_element.h +34 -0
  264. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/quote_element.c +35 -0
  265. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/quote_element.h +34 -0
  266. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/script_element.c +35 -0
  267. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/script_element.h +34 -0
  268. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/search_element.c +35 -0
  269. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/search_element.h +34 -0
  270. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/select_element.c +318 -0
  271. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/select_element.h +52 -0
  272. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/selectedcontent_element.c +110 -0
  273. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/selectedcontent_element.h +45 -0
  274. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/slot_element.c +35 -0
  275. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/slot_element.h +34 -0
  276. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/source_element.c +35 -0
  277. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/source_element.h +34 -0
  278. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/span_element.c +35 -0
  279. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/span_element.h +34 -0
  280. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/style_element.c +36 -0
  281. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/style_element.h +35 -0
  282. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_caption_element.c +35 -0
  283. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_caption_element.h +34 -0
  284. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_cell_element.c +35 -0
  285. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_cell_element.h +34 -0
  286. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_col_element.c +35 -0
  287. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_col_element.h +34 -0
  288. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_element.c +35 -0
  289. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_element.h +34 -0
  290. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_row_element.c +35 -0
  291. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_row_element.h +34 -0
  292. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_section_element.c +35 -0
  293. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_section_element.h +34 -0
  294. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/template_element.c +45 -0
  295. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/template_element.h +38 -0
  296. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/text_area_element.c +35 -0
  297. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/text_area_element.h +34 -0
  298. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/time_element.c +35 -0
  299. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/time_element.h +34 -0
  300. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/title_element.c +138 -0
  301. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/title_element.h +42 -0
  302. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/track_element.c +35 -0
  303. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/track_element.h +34 -0
  304. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/u_list_element.c +35 -0
  305. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/u_list_element.h +34 -0
  306. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/unknown_element.c +35 -0
  307. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/unknown_element.h +34 -0
  308. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/video_element.c +35 -0
  309. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/video_element.h +34 -0
  310. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/window.c +36 -0
  311. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/window.h +34 -0
  312. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/node.c +14 -0
  313. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/node.h +73 -0
  314. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/parser.c +498 -0
  315. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/parser.h +192 -0
  316. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/serialize.c +2041 -0
  317. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/serialize.h +94 -0
  318. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/serialize_ext.c +2129 -0
  319. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/serialize_ext.h +672 -0
  320. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tag.c +30 -0
  321. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tag.h +83 -0
  322. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tag_res.h +1913 -0
  323. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/token.c +388 -0
  324. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/token.h +147 -0
  325. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/token_attr.c +44 -0
  326. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/token_attr.h +67 -0
  327. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/error.c +106 -0
  328. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/error.h +153 -0
  329. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/res.h +4950 -0
  330. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state.c +2585 -0
  331. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state.h +251 -0
  332. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_comment.c +496 -0
  333. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_comment.h +27 -0
  334. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_doctype.c +1659 -0
  335. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_doctype.h +27 -0
  336. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_rawtext.c +303 -0
  337. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_rawtext.h +32 -0
  338. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_rcdata.c +311 -0
  339. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_rcdata.h +32 -0
  340. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_script.c +1209 -0
  341. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_script.h +32 -0
  342. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer.c +812 -0
  343. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer.h +389 -0
  344. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/active_formatting.c +241 -0
  345. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/active_formatting.h +117 -0
  346. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/error.c +94 -0
  347. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/error.h +130 -0
  348. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/after_after_body.c +73 -0
  349. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/after_after_frameset.c +74 -0
  350. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/after_body.c +93 -0
  351. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/after_frameset.c +98 -0
  352. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/after_head.c +232 -0
  353. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/before_head.c +154 -0
  354. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/before_html.c +176 -0
  355. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/foreign_content.c +386 -0
  356. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_body.c +2179 -0
  357. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_caption.c +164 -0
  358. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_cell.c +203 -0
  359. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_column_group.c +211 -0
  360. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_frameset.c +159 -0
  361. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_head.c +378 -0
  362. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_head_noscript.c +129 -0
  363. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_row.c +211 -0
  364. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_select.c +359 -0
  365. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_select_in_table.c +115 -0
  366. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_table.c +482 -0
  367. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_table_body.c +208 -0
  368. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_table_text.c +127 -0
  369. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_template.c +193 -0
  370. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/initial.c +422 -0
  371. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/text.c +70 -0
  372. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode.h +136 -0
  373. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/open_elements.c +337 -0
  374. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/open_elements.h +101 -0
  375. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/open_elements_res.h +427 -0
  376. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/template_insertion.c +10 -0
  377. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/template_insertion.h +100 -0
  378. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree.c +1795 -0
  379. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree.h +427 -0
  380. lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree_res.h +111 -0
  381. lexidown-0.1.0/vendor/lexbor/source/lexbor/ns/base.h +32 -0
  382. lexidown-0.1.0/vendor/lexbor/source/lexbor/ns/const.h +37 -0
  383. lexidown-0.1.0/vendor/lexbor/source/lexbor/ns/ns.c +155 -0
  384. lexidown-0.1.0/vendor/lexbor/source/lexbor/ns/ns.h +66 -0
  385. lexidown-0.1.0/vendor/lexbor/source/lexbor/ns/res.h +97 -0
  386. lexidown-0.1.0/vendor/lexbor/source/lexbor/ports/posix/lexbor/core/fs.c +236 -0
  387. lexidown-0.1.0/vendor/lexbor/source/lexbor/ports/posix/lexbor/core/memory.c +53 -0
  388. lexidown-0.1.0/vendor/lexbor/source/lexbor/ports/posix/lexbor/core/perf.c +214 -0
  389. lexidown-0.1.0/vendor/lexbor/source/lexbor/ports/windows_nt/lexbor/core/fs.c +239 -0
  390. lexidown-0.1.0/vendor/lexbor/source/lexbor/ports/windows_nt/lexbor/core/memory.c +53 -0
  391. lexidown-0.1.0/vendor/lexbor/source/lexbor/ports/windows_nt/lexbor/core/perf.c +81 -0
  392. lexidown-0.1.0/vendor/lexbor/source/lexbor/tag/base.h +32 -0
  393. lexidown-0.1.0/vendor/lexbor/source/lexbor/tag/const.h +230 -0
  394. lexidown-0.1.0/vendor/lexbor/source/lexbor/tag/res.h +568 -0
  395. lexidown-0.1.0/vendor/lexbor/source/lexbor/tag/tag.c +143 -0
  396. lexidown-0.1.0/vendor/lexbor/source/lexbor/tag/tag.h +121 -0
  397. lexidown-0.1.0/vendor/lexbor/version +1 -0
@@ -0,0 +1,179 @@
1
+ # Development
2
+
3
+ ## Setup and checks
4
+
5
+ Use Python 3.10 or newer, a C compiler, and Python development headers:
6
+
7
+ ```sh
8
+ python -m venv .venv
9
+ # Activate .venv using the command for your shell.
10
+ python -m pip install -e '.[dev]'
11
+ python -m unittest discover -s tests -v
12
+ ruff check .
13
+ ruff format --check .
14
+ ```
15
+
16
+ To apply Ruff fixes and formatting:
17
+
18
+ ```sh
19
+ ruff check --fix .
20
+ ruff format .
21
+ ```
22
+
23
+ The configuration in `pyproject.toml` uses 88-column formatting, double quotes,
24
+ isort import ordering, and common checks for errors, outdated syntax, likely
25
+ bugs, and simpler Python constructs. Vendored C sources are excluded. Ruff
26
+ checks Python files; Cython sources are checked by the Cython compiler. Reinstall
27
+ the editable package after changing `.pyx`, `.pxi`, `.pxd`, or bundled C files.
28
+
29
+ The reusable [lint workflow](.github/workflows/lint.yml) runs Ruff lint and
30
+ formatting checks and is called by the wheel workflow on pushes and pull requests.
31
+
32
+ ## Dependencies
33
+
34
+ | Dependency | Purpose |
35
+ | --- | --- |
36
+ | Runtime Python packages | None; the wheel includes the native engine and parser |
37
+ | Cython and setuptools | Build the extension from source |
38
+ | html5lib | Test-only parser for independent DOM inputs |
39
+ | Ruff | Python linting, import sorting, and formatting |
40
+ | Node.js and the root npm dev dependencies | Optional JavaScript comparisons |
41
+ | `benchmarks/requirements.txt` | Optional competing converters for benchmarks |
42
+ | build, Twine, and cibuildwheel | Distribution builds and validation |
43
+
44
+ `html5lib` deliberately constructs DOM inputs independently of Lexidown for
45
+ compatibility and non-mutation tests. Its `six` and `webencodings` dependencies
46
+ are test dependencies too. Lexidown never imports these during normal use.
47
+ `selectolax` is not needed: the Lexbor C sources are already bundled in `vendor/`.
48
+
49
+ ## Compatibility tests
50
+
51
+ The Python suite runs without Node.js. It includes all 147 upstream HTML fixtures
52
+ in both string and DOM modes, plus native parser, serialization, callback,
53
+ mutation, and optimization regressions.
54
+
55
+ The golden fixtures in `tests/fixtures/turndown.json` were extracted from
56
+ [Turndown's test document at aa84dfa](https://github.com/mixmark-io/turndown/blob/aa84dfa3e2361edea8c43acbfc2b7a9363494bfd/test/index.html).
57
+ Their upstream MIT attribution is retained in `THIRD_PARTY_NOTICES.md`.
58
+
59
+ For live comparisons against JavaScript, install the optional reference packages:
60
+
61
+ ```sh
62
+ npm ci
63
+ python scripts/differential.py
64
+ ```
65
+
66
+ `package-lock.json` pins Turndown 7.2.4 and Domino 2.2.0. The differential suite
67
+ compares outputs for option combinations, Unicode, malformed HTML, kept HTML,
68
+ plugins, and repeated conversions. Updating the compatibility target requires
69
+ reviewing the fixtures and differential results along with parser changes.
70
+
71
+ ## Benchmarks
72
+
73
+ Install the npm reference with `npm ci`. With saved HTML snapshots present, run:
74
+
75
+ ```sh
76
+ python scripts/benchmark.py --rounds 3 --samples 7 --warmups 5
77
+ ```
78
+
79
+ This compares Lexidown and JavaScript across eight pages and 13 option profiles.
80
+ For the additional Python converters:
81
+
82
+ ```sh
83
+ python -m pip install -r benchmarks/requirements.txt
84
+ python -m scripts.compare_converters --rounds 3 --samples 7 --warmups 5
85
+ ```
86
+
87
+ Use a separate environment for competitor packages if they are not needed for
88
+ other development work. They are not part of Lexidown's development extra.
89
+
90
+ `benchmarks/sources.json` records source URLs and SHA-256 hashes. HTML snapshots
91
+ live in `benchmarks/inputs/` and are excluded from version control. The scripts
92
+ verify these hashes before measuring. Downloading a page again can change the
93
+ corpus; record new hashes and rerun the comparison instead of mixing results.
94
+
95
+ Each round uses fresh worker processes, shuffled page order, warmups, and serial
96
+ timed conversions with normal garbage collection. Timing includes parsing,
97
+ service construction, and Markdown generation. Startup, imports, file reads,
98
+ and output hashing are excluded. Converted output is never cached.
99
+
100
+ The scripts record exact output matches, sample spread, versions, and source,
101
+ binary, and input hashes. See [the Turndown report](benchmarks/REPORT.md) and
102
+ [the converter comparison](benchmarks/COMPARISON.md) for results and methodology.
103
+ Treat speed as workload-specific; different converters implement different
104
+ output conventions.
105
+
106
+ ## Implementation
107
+
108
+ Lexbor parses HTML in C. Lexidown's own Cython code converts that DOM to Markdown.
109
+ Cython generates C at build time, and `setup.py` compiles it with the bundled
110
+ parser into one extension, `lexidown._native`.
111
+
112
+ 1. The parser constructs a native DOM. Cython node wrappers retain its owning
113
+ document; supplied Python DOMs are cloned into this representation.
114
+ 2. The engine normalizes whitespace and collects text boundaries and blank-node
115
+ metadata. Cached node names and list positions avoid repeated scans; mutations
116
+ invalidate affected caches.
117
+ 3. An explicit traversal stack applies the Turndown rules. Sibling output is
118
+ joined in chunks to avoid repeatedly copying accumulated Markdown. The engine
119
+ also implements kept-HTML serialization, JavaScript whitespace behavior, and
120
+ UTF-16 heading lengths.
121
+
122
+ Options and rule dictionaries remain Python objects. Built-in rules use compiled
123
+ calls, while Python callbacks and method overrides run within the same engine.
124
+ There is no alternate parser or converter fallback.
125
+
126
+ | File | Responsibility |
127
+ | --- | --- |
128
+ | [`_native.pyx`](src/lexidown/_native.pyx) | Service, rule dispatch, and traversal |
129
+ | [`_native_dom.pxi`](src/lexidown/_native_dom.pxi) | DOM, parsing, serialization, and whitespace |
130
+ | [`_native_rules.pxi`](src/lexidown/_native_rules.pxi) | Built-in Markdown rules |
131
+ | [`_lexbor.pxd`](src/lexidown/_lexbor.pxd) | Lexbor C declarations |
132
+ | [`setup.py`](setup.py) | Combined native build |
133
+
134
+ The bundled Lexbor 3.1.0 has local C patches to match Domino's older parsing
135
+ behavior. They cover legacy select/menuitem handling and quirks involving
136
+ textarea, tables, processing instructions, and foreign content. Exact changes,
137
+ upstream versions, and attribution are documented in
138
+ [`vendor/lexbor/README.md`](vendor/lexbor/README.md).
139
+
140
+ ## Distribution builds
141
+
142
+ Build a source archive and local wheel into an empty output directory:
143
+
144
+ ```sh
145
+ python -m pip install build==1.6.0 twine==7.0.0
146
+ python -m build --outdir dist/release
147
+ python -m twine check --strict dist/release/*
148
+ ```
149
+
150
+ Local wheels target the build machine. Use the
151
+ [wheel workflow](.github/workflows/wheels.yml) for portable distributions. It
152
+ builds a source archive, builds wheels from that archive, validates metadata,
153
+ and runs the full suite against each installed wheel outside the source tree.
154
+ `scripts/check_wheel.py` also verifies that the compiled extension and required
155
+ license notices are installed. Wheel tests need html5lib, but no Node.js.
156
+
157
+ The workflow configures standard CPython 3.10–3.14 for these targets:
158
+
159
+ | Platform | Architectures | Build baseline |
160
+ | --- | --- | --- |
161
+ | Linux with glibc | x86-64, ARM64 | manylinux, glibc 2.28 |
162
+ | Linux with musl | x86-64, ARM64 | musllinux, musl 1.2 |
163
+ | macOS | Intel x86-64, Apple Silicon ARM64 | macOS 11 |
164
+ | Windows | x64 | AMD64 |
165
+
166
+ There are 35 configured wheel builds. Each Python version needs its own wheel;
167
+ the extension does not use the stable `abi3` ABI. Windows ARM64, 32-bit systems,
168
+ PyPy, and free-threaded CPython are outside this matrix. A platform is validated
169
+ by its own successful build and installed-wheel tests.
170
+
171
+ ## Licensing
172
+
173
+ The conversion code retains Turndown's MIT license and attribution. Lexbor's
174
+ Apache-2.0 license, NOTICE, BSD-2-Clause notices, and local modification notices
175
+ must be preserved. The package metadata describes these component obligations
176
+ with `MIT AND Apache-2.0 AND BSD-2-Clause`.
177
+
178
+ See [LICENSE](LICENSE), [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md), and the
179
+ [Apache redistribution terms](https://www.apache.org/licenses/LICENSE-2.0).
lexidown-0.1.0/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 EvickaStudio
4
+ Copyright (c) 2017 Dom Christie
5
+ Copyright (c) 2014 Luc Thevenard <lucthevenard@gmail.com>
6
+ Parser adaptations: Copyright (c) 2006-2013 James Graham and other contributors
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ of this software and associated documentation files (the "Software"), to deal
10
+ in the Software without restriction, including without limitation the rights
11
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ copies of the Software, and to permit persons to whom the Software is
13
+ furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in all
16
+ copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ SOFTWARE.
@@ -0,0 +1,7 @@
1
+ include DEVELOPMENT.md package.json package-lock.json
2
+ recursive-include tests *.py *.json
3
+ recursive-include examples *.py
4
+ recursive-include scripts *.py *.cjs
5
+ recursive-include benchmarks *.md *.json *.txt
6
+ recursive-include src/lexidown *.pyx *.pxi *.pxd
7
+ recursive-include vendor/lexbor *.c *.h LICENSE BSD-LICENSE NOTICE version README.md
@@ -0,0 +1,259 @@
1
+ Metadata-Version: 2.4
2
+ Name: lexidown
3
+ Version: 0.1.0
4
+ Summary: Fast native HTML-to-Markdown for Python, compatible with Turndown
5
+ License-Expression: MIT AND Apache-2.0 AND BSD-2-Clause
6
+ Keywords: html,markdown,turndown,lexbor
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Classifier: Programming Language :: Python :: Implementation :: CPython
15
+ Classifier: Programming Language :: Cython
16
+ Classifier: Topic :: Text Processing :: Markup :: Markdown
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ License-File: vendor/lexbor/LICENSE
21
+ License-File: vendor/lexbor/NOTICE
22
+ License-File: vendor/lexbor/BSD-LICENSE
23
+ License-File: THIRD_PARTY_NOTICES.md
24
+ Provides-Extra: dev
25
+ Requires-Dist: Cython==3.3.0; extra == "dev"
26
+ Requires-Dist: html5lib==1.1; extra == "dev"
27
+ Requires-Dist: ruff==0.16.6; extra == "dev"
28
+ Dynamic: license-file
29
+
30
+ # Lexidown
31
+
32
+ **Fast native HTML-to-Markdown for Python, compatible with Turndown.**
33
+
34
+ Lexidown brings [Turndown](https://github.com/mixmark-io/turndown)'s conversion
35
+ behavior, options, and extensible service API to Python. Its Cython conversion
36
+ engine and bundled Lexbor C parser require no runtime dependencies.
37
+
38
+ ## Performance
39
+
40
+ Default settings, eight complete HTML pages, CPython 3.14.7 and Node.js 22.23.2
41
+ on an Intel Core i7-1260P:
42
+
43
+ | Converter | Implementation | Speed vs JS Turndown | Exact JS output |
44
+ | --- | --- | ---: | ---: |
45
+ | JavaScript Turndown | JavaScript + Domino | 1.00× | 8/8 |
46
+ | **Lexidown** | **Cython + Lexbor** | **2.94×** | **8/8** |
47
+ | [html2text](https://pypi.org/project/html2text/) | Python | 1.16× | 0/8 |
48
+ | [markdownify](https://pypi.org/project/markdownify/) | Python + BeautifulSoup | 0.61× | 0/8 |
49
+ | [html-to-markdown](https://pypi.org/project/html-to-markdown/) | Rust | 4.46× | 0/8 |
50
+ | [fast-h2m](https://pypi.org/project/fast-h2m/) | Rust | 3.72× | 0/8 |
51
+
52
+ Speed is the geometric mean of JavaScript time divided by converter time;
53
+ above 1 means faster. Each page has 21 timed conversions across three fresh
54
+ processes. Results depend on the input, and different output is not a quality
55
+ score. See the [full comparison](benchmarks/COMPARISON.md) for per-page timings,
56
+ versions, methodology, and additional configurations.
57
+
58
+ Lexidown also matches Turndown byte for byte across all **104 page/option
59
+ combinations** in the [compatibility benchmark](benchmarks/REPORT.md).
60
+
61
+ ## Installation
62
+
63
+ Requires Python 3.10 or newer.
64
+
65
+ ```sh
66
+ python -m pip install lexidown
67
+ ```
68
+
69
+ Platform wheels contain the native conversion engine and Lexbor. Installing a
70
+ matching wheel requires no compiler, Cython, Node.js, or separate HTML parser.
71
+ For platforms without a matching wheel, see [building from source](#building-from-source).
72
+
73
+ ## Usage
74
+
75
+ ```python
76
+ from lexidown import TurndownService
77
+
78
+ service = TurndownService({"headingStyle": "atx"})
79
+ markdown = service.turndown(
80
+ "<h1>Hello world!</h1><p>Made with <strong>Python</strong>.</p>"
81
+ )
82
+ assert markdown == "# Hello world!\n\nMade with **Python**."
83
+ ```
84
+
85
+ To discard scripts and styles along with their contents:
86
+
87
+ ```python
88
+ service = TurndownService().remove(["script", "style"])
89
+ assert service.turndown("<style>p { color: red }</style><p>Hello</p>") == "Hello"
90
+ ```
91
+
92
+ Like Turndown, the default conversion retains the text inside unsupported tags.
93
+ Inline styling attributes do not appear in converted Markdown.
94
+
95
+ For a runnable example comparing default output with custom formatting, HTML
96
+ cleanup, and a strikethrough rule, see [examples/formatting.py](examples/formatting.py).
97
+ After installing Lexidown, run it from the project directory:
98
+
99
+ ```sh
100
+ python examples/formatting.py
101
+ ```
102
+
103
+ ## Options
104
+
105
+ Pass a dictionary to `TurndownService`. Option names and defaults match
106
+ Turndown 7.2.4.
107
+
108
+ | Option | Default | Alternatives |
109
+ | --- | --- | --- |
110
+ | `headingStyle` | `"setext"` | `"atx"` |
111
+ | `hr` | `"* * *"` | Thematic break text |
112
+ | `bulletListMarker` | `"*"` | `"-"`, `"+"` |
113
+ | `codeBlockStyle` | `"indented"` | `"fenced"` |
114
+ | `fence` | `"```"` | `"~~~"` |
115
+ | `emDelimiter` | `"_"` | `"*"` |
116
+ | `strongDelimiter` | `"**"` | `"__"` |
117
+ | `linkStyle` | `"inlined"` | `"referenced"` |
118
+ | `linkReferenceStyle` | `"full"` | `"collapsed"`, `"shortcut"` |
119
+ | `br` | `" "` | Text inserted before a line-break newline |
120
+ | `preformattedCode` | `False` | `True` |
121
+
122
+ ### Advanced options
123
+
124
+ `blankReplacement`, `keepReplacement`, and `defaultReplacement` accept callbacks
125
+ with `(content, node, options)` arguments to customize blank elements, kept HTML,
126
+ and elements without a matching rule. `options` is a dictionary. Callbacks may
127
+ omit unused trailing arguments.
128
+
129
+ The `rules` option accepts an ordered dictionary of rules to replace the
130
+ built-in rule set.
131
+
132
+ ## Methods
133
+
134
+ ### `turndown(html_or_node)`
135
+
136
+ Convert an HTML string or DOM node to a Markdown string. A service can be reused
137
+ for multiple conversions.
138
+
139
+ ### `addRule(key, rule)`
140
+
141
+ Register a conversion rule. `add_rule` is also available as a Python-style alias.
142
+
143
+ ```python
144
+ service = TurndownService().addRule(
145
+ "strikethrough",
146
+ {
147
+ "filter": ["del", "s", "strike"],
148
+ "replacement": lambda content: "~~" + content + "~~",
149
+ },
150
+ )
151
+ assert service.turndown("<del>old</del>") == "~~old~~"
152
+ ```
153
+
154
+ ### `keep(filter)`
155
+
156
+ Preserve matching elements as HTML:
157
+
158
+ ```python
159
+ service = TurndownService().keep("ins")
160
+ assert service.turndown("<ins>new</ins>") == "<ins>new</ins>"
161
+ ```
162
+
163
+ ### `remove(filter)`
164
+
165
+ Discard matching elements and their contents. `keep` and `remove` accept the
166
+ same filters as rules. Added and built-in rules take precedence over both;
167
+ use `addRule` to override an element that already has a conversion rule.
168
+
169
+ ### `use(plugin)`
170
+
171
+ Apply a plugin function, or a list of plugin functions. Plugins receive the
172
+ service and can register rules or change options:
173
+
174
+ ```python
175
+ def strikethrough(service):
176
+ service.addRule(
177
+ "strikethrough",
178
+ {"filter": "del", "replacement": lambda content: "~~" + content + "~~"},
179
+ )
180
+
181
+
182
+ service = TurndownService().use(strikethrough)
183
+ assert service.turndown("<del>old</del>") == "~~old~~"
184
+ ```
185
+
186
+ `addRule`, `keep`, `remove`, and `use` return the service for chaining.
187
+ JavaScript plugins need to be translated into Python functions.
188
+
189
+ ## Extending with rules
190
+
191
+ A rule is a dictionary containing:
192
+
193
+ - `filter`: a lowercase tag name, a list of tag names, or a callback taking
194
+ `(node, options)` and returning whether the rule matches.
195
+ - `replacement`: a callback taking `(content, node, options)` and returning
196
+ Markdown. `content` is the converted content of the element's children.
197
+ - `append` (optional): a callback taking `options` and returning text to append
198
+ after conversion, useful for reference definitions. Use a closure or bound
199
+ method to maintain rule state.
200
+
201
+ Callbacks may omit unused trailing arguments. Rules are selected in this order:
202
+ blank rules, added rules (newest first), built-in rules, keep rules, remove rules,
203
+ then the default rule.
204
+
205
+ Rule callbacks receive DOM nodes with Turndown's camelCase properties, including
206
+ `nodeName`, `nodeType`, `textContent`, `outerHTML`, `parentNode`, `childNodes`,
207
+ `children`, `firstChild`, `nextSibling`, `previousSibling`, and `getAttribute`.
208
+ Conversion also exposes `isBlock`, `isCode`, `isBlank`, and
209
+ `flankingWhitespace` (a dictionary).
210
+
211
+ ### Escaping
212
+
213
+ Text is escaped to prevent Markdown syntax from changing its meaning. Override
214
+ `service.escape` to customize this behavior. Text inside code bypasses escaping.
215
+
216
+ ## DOM input
217
+
218
+ In addition to HTML strings, `turndown()` accepts native Lexidown DOM nodes and
219
+ `xml.dom.minidom` element, document, and fragment nodes. DOM inputs are cloned
220
+ before whitespace normalization, leaving the input tree unchanged.
221
+
222
+ ```python
223
+ from xml.dom.minidom import parseString
224
+
225
+ from lexidown import TurndownService
226
+
227
+ # minidom parses XML; use well-formed markup when constructing a DOM this way.
228
+ document = parseString("<p>Hello <strong>world</strong></p>")
229
+ assert TurndownService().turndown(document.documentElement) == "Hello **world**"
230
+ ```
231
+
232
+ ## Building from source
233
+
234
+ From a source checkout or unpacked source distribution:
235
+
236
+ ```sh
237
+ python -m pip install .
238
+ ```
239
+
240
+ A C compiler and Python development headers are required. The build installs
241
+ Cython automatically and compiles the included Lexbor sources. No parser sources
242
+ need to be downloaded separately.
243
+
244
+ See [Development](DEVELOPMENT.md) for tests, Ruff formatting, benchmarks,
245
+ implementation details, and wheel builds.
246
+
247
+ ## Development details
248
+
249
+ Turndown was ported to Python and optimized using **Codex + GPT-6 Astra Ultra**.
250
+
251
+ ## License
252
+
253
+ Lexidown's conversion code is [MIT licensed](LICENSE), with Turndown's original
254
+ credits retained. Bundled Lexbor is Apache-2.0 licensed and includes BSD-2-Clause
255
+ components. Their licenses and notices are included in source distributions and
256
+ wheels; the combined package expression is `MIT AND Apache-2.0 AND BSD-2-Clause`.
257
+
258
+ See [third-party notices](THIRD_PARTY_NOTICES.md) and
259
+ [Lexbor's provenance and modifications](vendor/lexbor/README.md).