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.
- lexidown-0.1.0/DEVELOPMENT.md +179 -0
- lexidown-0.1.0/LICENSE +24 -0
- lexidown-0.1.0/MANIFEST.in +7 -0
- lexidown-0.1.0/PKG-INFO +259 -0
- lexidown-0.1.0/README.md +230 -0
- lexidown-0.1.0/THIRD_PARTY_NOTICES.md +65 -0
- lexidown-0.1.0/benchmarks/COMPARISON.md +74 -0
- lexidown-0.1.0/benchmarks/REPORT.md +282 -0
- lexidown-0.1.0/benchmarks/comparison-example.json +13 -0
- lexidown-0.1.0/benchmarks/comparison-results.json +7920 -0
- lexidown-0.1.0/benchmarks/requirements.txt +10 -0
- lexidown-0.1.0/benchmarks/results.json +25933 -0
- lexidown-0.1.0/benchmarks/sources.json +108 -0
- lexidown-0.1.0/examples/formatting.py +85 -0
- lexidown-0.1.0/package-lock.json +35 -0
- lexidown-0.1.0/package.json +9 -0
- lexidown-0.1.0/pyproject.toml +68 -0
- lexidown-0.1.0/scripts/benchmark.py +531 -0
- lexidown-0.1.0/scripts/benchmark_js.cjs +39 -0
- lexidown-0.1.0/scripts/benchmark_python.py +50 -0
- lexidown-0.1.0/scripts/check_wheel.py +49 -0
- lexidown-0.1.0/scripts/compare_converters.py +519 -0
- lexidown-0.1.0/scripts/differential.py +458 -0
- lexidown-0.1.0/scripts/js_oracle.cjs +41 -0
- lexidown-0.1.0/scripts/prepare_release.py +112 -0
- lexidown-0.1.0/setup.cfg +4 -0
- lexidown-0.1.0/setup.py +36 -0
- lexidown-0.1.0/src/lexidown/__init__.py +7 -0
- lexidown-0.1.0/src/lexidown/_lexbor.pxd +110 -0
- lexidown-0.1.0/src/lexidown/_native.pyx +405 -0
- lexidown-0.1.0/src/lexidown/_native_dom.pxi +939 -0
- lexidown-0.1.0/src/lexidown/_native_rules.pxi +324 -0
- lexidown-0.1.0/src/lexidown/commonmark.py +5 -0
- lexidown-0.1.0/src/lexidown/dom.py +20 -0
- lexidown-0.1.0/src/lexidown/html_parser.py +10 -0
- lexidown-0.1.0/src/lexidown/native_parser.py +5 -0
- lexidown-0.1.0/src/lexidown/service.py +11 -0
- lexidown-0.1.0/src/lexidown/utilities.py +35 -0
- lexidown-0.1.0/src/lexidown.egg-info/PKG-INFO +259 -0
- lexidown-0.1.0/src/lexidown.egg-info/SOURCES.txt +395 -0
- lexidown-0.1.0/src/lexidown.egg-info/dependency_links.txt +1 -0
- lexidown-0.1.0/src/lexidown.egg-info/requires.txt +5 -0
- lexidown-0.1.0/src/lexidown.egg-info/top_level.txt +1 -0
- lexidown-0.1.0/tests/__init__.py +0 -0
- lexidown-0.1.0/tests/fixtures/turndown.json +1082 -0
- lexidown-0.1.0/tests/test_benchmark.py +33 -0
- lexidown-0.1.0/tests/test_comparison.py +40 -0
- lexidown-0.1.0/tests/test_compiled_dom.py +104 -0
- lexidown-0.1.0/tests/test_compiled_rules.py +60 -0
- lexidown-0.1.0/tests/test_dom_optimization.py +46 -0
- lexidown-0.1.0/tests/test_native_parser.py +147 -0
- lexidown-0.1.0/tests/test_optimization.py +60 -0
- lexidown-0.1.0/tests/test_parser_optimization.py +36 -0
- lexidown-0.1.0/tests/test_release.py +134 -0
- lexidown-0.1.0/tests/test_serialization.py +83 -0
- lexidown-0.1.0/tests/test_turndown.py +429 -0
- lexidown-0.1.0/vendor/lexbor/BSD-LICENSE +32 -0
- lexidown-0.1.0/vendor/lexbor/LICENSE +223 -0
- lexidown-0.1.0/vendor/lexbor/NOTICE +16 -0
- lexidown-0.1.0/vendor/lexbor/README.md +56 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/array.c +208 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/array.h +100 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/array_obj.c +216 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/array_obj.h +134 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/avl.c +525 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/avl.h +92 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/base.h +108 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/bst.c +471 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/bst.h +108 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/bst_map.c +238 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/bst_map.h +87 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/conv.c +351 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/conv.h +62 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/core.h +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/def.h +59 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/diyfp.c +181 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/diyfp.h +285 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/dobject.c +187 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/dobject.h +88 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/dtoa.c +401 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/dtoa.h +54 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/fs.h +60 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/hash.c +477 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/hash.h +218 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/in.c +266 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/in.h +172 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/lexbor.h +43 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/mem.c +228 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/mem.h +141 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/mraw.c +421 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/mraw.h +114 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/perf.h +40 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/plog.c +73 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/plog.h +102 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/print.c +168 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/print.h +39 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/sbst.h +59 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/serialize.c +27 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/serialize.h +32 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/shs.c +118 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/shs.h +82 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/str.c +667 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/str.h +257 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/str_res.h +360 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/strtod.c +348 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/strtod.h +54 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/swar.h +97 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/types.h +39 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/utils.c +43 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/core/utils.h +36 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/base.h +32 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/collection.c +97 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/collection.h +112 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/dom.h +29 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/exception.c +372 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/exception.h +99 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interface.c +110 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interface.h +86 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/attr.c +534 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/attr.h +141 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/attr_const.h +99 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/attr_res.h +263 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/cdata_section.c +55 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/cdata_section.h +38 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/character_data.c +116 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/character_data.h +51 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/comment.c +64 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/comment.h +42 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/document.c +593 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/document.h +336 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/document_fragment.c +36 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/document_fragment.h +36 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/document_type.c +232 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/document_type.h +135 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/element.c +896 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/element.h +361 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/event_target.c +32 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/event_target.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/node.c +1866 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/node.h +396 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/processing_instruction.c +92 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/processing_instruction.h +66 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/shadow_root.c +36 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/shadow_root.h +44 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/text.c +63 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/dom/interfaces/text.h +42 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/attribute_steps.c +159 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/attribute_steps.h +49 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/attribute_steps_res.h +427 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/base.h +44 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/common.c +115 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/common.h +32 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/element_steps.c +220 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/element_steps.h +47 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/element_steps_res.h +427 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/encoding.c +670 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/encoding.h +243 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/html.h +109 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interface.c +168 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interface.h +189 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interface_res.h +5456 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/anchor_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/anchor_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/area_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/area_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/audio_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/audio_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/base_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/base_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/body_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/body_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/br_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/br_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/button_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/button_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/canvas_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/canvas_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/d_list_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/d_list_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/data_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/data_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/data_list_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/data_list_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/details_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/details_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/dialog_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/dialog_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/directory_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/directory_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/div_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/div_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/document.c +512 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/document.h +313 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/element.c +145 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/element.h +137 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/embed_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/embed_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/field_set_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/field_set_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/font_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/font_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/form_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/form_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/frame_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/frame_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/frame_set_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/frame_set_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/head_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/head_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/heading_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/heading_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/hr_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/hr_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/html_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/html_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/iframe_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/iframe_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/image_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/image_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/input_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/input_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/label_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/label_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/legend_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/legend_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/li_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/li_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/link_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/link_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/map_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/map_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/marquee_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/marquee_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/media_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/media_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/menu_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/menu_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/meta_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/meta_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/meter_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/meter_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/mod_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/mod_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/o_list_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/o_list_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/object_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/object_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/opt_group_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/opt_group_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/option_element.c +260 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/option_element.h +71 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/output_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/output_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/paragraph_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/paragraph_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/param_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/param_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/picture_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/picture_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/pre_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/pre_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/progress_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/progress_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/quote_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/quote_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/script_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/script_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/search_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/search_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/select_element.c +318 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/select_element.h +52 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/selectedcontent_element.c +110 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/selectedcontent_element.h +45 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/slot_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/slot_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/source_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/source_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/span_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/span_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/style_element.c +36 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/style_element.h +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_caption_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_caption_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_cell_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_cell_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_col_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_col_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_row_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_row_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_section_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/table_section_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/template_element.c +45 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/template_element.h +38 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/text_area_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/text_area_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/time_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/time_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/title_element.c +138 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/title_element.h +42 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/track_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/track_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/u_list_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/u_list_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/unknown_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/unknown_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/video_element.c +35 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/video_element.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/window.c +36 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/interfaces/window.h +34 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/node.c +14 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/node.h +73 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/parser.c +498 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/parser.h +192 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/serialize.c +2041 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/serialize.h +94 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/serialize_ext.c +2129 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/serialize_ext.h +672 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tag.c +30 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tag.h +83 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tag_res.h +1913 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/token.c +388 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/token.h +147 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/token_attr.c +44 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/token_attr.h +67 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/error.c +106 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/error.h +153 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/res.h +4950 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state.c +2585 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state.h +251 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_comment.c +496 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_comment.h +27 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_doctype.c +1659 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_doctype.h +27 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_rawtext.c +303 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_rawtext.h +32 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_rcdata.c +311 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_rcdata.h +32 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_script.c +1209 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer/state_script.h +32 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer.c +812 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tokenizer.h +389 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/active_formatting.c +241 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/active_formatting.h +117 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/error.c +94 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/error.h +130 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/after_after_body.c +73 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/after_after_frameset.c +74 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/after_body.c +93 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/after_frameset.c +98 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/after_head.c +232 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/before_head.c +154 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/before_html.c +176 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/foreign_content.c +386 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_body.c +2179 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_caption.c +164 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_cell.c +203 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_column_group.c +211 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_frameset.c +159 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_head.c +378 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_head_noscript.c +129 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_row.c +211 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_select.c +359 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_select_in_table.c +115 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_table.c +482 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_table_body.c +208 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_table_text.c +127 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_template.c +193 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/initial.c +422 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode/text.c +70 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/insertion_mode.h +136 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/open_elements.c +337 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/open_elements.h +101 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/open_elements_res.h +427 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/template_insertion.c +10 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree/template_insertion.h +100 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree.c +1795 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree.h +427 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/html/tree_res.h +111 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/ns/base.h +32 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/ns/const.h +37 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/ns/ns.c +155 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/ns/ns.h +66 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/ns/res.h +97 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/ports/posix/lexbor/core/fs.c +236 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/ports/posix/lexbor/core/memory.c +53 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/ports/posix/lexbor/core/perf.c +214 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/ports/windows_nt/lexbor/core/fs.c +239 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/ports/windows_nt/lexbor/core/memory.c +53 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/ports/windows_nt/lexbor/core/perf.c +81 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/tag/base.h +32 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/tag/const.h +230 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/tag/res.h +568 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/tag/tag.c +143 -0
- lexidown-0.1.0/vendor/lexbor/source/lexbor/tag/tag.h +121 -0
- 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
|
lexidown-0.1.0/PKG-INFO
ADDED
|
@@ -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).
|