| @@ -1,6 +1,8 @@ | |||||
| v0.7.1 (unreleased): | v0.7.1 (unreleased): | ||||
| - Modernized packaging. | |||||
| - Modernize packaging. | |||||
| - Explicitly support $WITH_EXTENSION/$WITHOUT_EXTENSION to control whether the | |||||
| C tokenizer should be built. | |||||
| v0.7.0 (released June 28, 2025): | v0.7.0 (released June 28, 2025): | ||||
| @@ -18,7 +18,13 @@ Installation | |||||
| The easiest way to install the parser is from `PyPI`_; you can install the | The easiest way to install the parser is from `PyPI`_; you can install the | ||||
| latest release with ``pip install mwparserfromhell``. | latest release with ``pip install mwparserfromhell``. | ||||
| Alternatively, get the latest development version: | |||||
| Prebuilt wheels are available on PyPI with a fast, compiled C tokenizer | |||||
| extension for most environments (Linux x86_64 and arm64, macOS x86_64 and | |||||
| arm64, Windows x86 and x86_64). If building from source and the C tokenizer | |||||
| cannot be built, you can fall back to the slower pure-Python implementation by | |||||
| setting the environment variable ``WITH_EXTENSION=0`` when installing. | |||||
| To get the latest development version (with `uv`_): | |||||
| .. code-block:: sh | .. code-block:: sh | ||||
| @@ -223,6 +229,7 @@ Python 3 code (using the API_ and the requests_ library): | |||||
| .. _Legoktm: https://en.wikipedia.org/wiki/User:Legoktm | .. _Legoktm: https://en.wikipedia.org/wiki/User:Legoktm | ||||
| .. _GitHub: https://github.com/earwig/mwparserfromhell | .. _GitHub: https://github.com/earwig/mwparserfromhell | ||||
| .. _PyPI: https://pypi.org/project/mwparserfromhell/ | .. _PyPI: https://pypi.org/project/mwparserfromhell/ | ||||
| .. _uv: https://docs.astral.sh/uv/ | |||||
| .. _pytest: https://docs.pytest.org/ | .. _pytest: https://docs.pytest.org/ | ||||
| .. _Word-ending links: https://www.mediawiki.org/wiki/Help:Links#linktrail | .. _Word-ending links: https://www.mediawiki.org/wiki/Help:Links#linktrail | ||||
| .. _EarwigBot: https://github.com/earwig/earwigbot | .. _EarwigBot: https://github.com/earwig/earwigbot | ||||
| @@ -7,7 +7,9 @@ v0.7.1 | |||||
| Unreleased | Unreleased | ||||
| (`changes <https://github.com/earwig/mwparserfromhell/compare/v0.7.0...main>`__): | (`changes <https://github.com/earwig/mwparserfromhell/compare/v0.7.0...main>`__): | ||||
| - Modernized packaging. | |||||
| - Modernize packaging. | |||||
| - Explicitly support ``$WITH_EXTENSION`` / ``$WITHOUT_EXTENSION`` to control | |||||
| whether the C tokenizer should be built. | |||||
| v0.7.0 | v0.7.0 | ||||
| ------ | ------ | ||||
| @@ -20,7 +20,13 @@ Installation | |||||
| The easiest way to install the parser is from `PyPI`_; you can install the | The easiest way to install the parser is from `PyPI`_; you can install the | ||||
| latest release with ``pip install mwparserfromhell``. | latest release with ``pip install mwparserfromhell``. | ||||
| Alternatively, get the latest development version:: | |||||
| Prebuilt wheels are available on PyPI with a fast, compiled C tokenizer | |||||
| extension for most environments (Linux x86_64 and arm64, macOS x86_64 and | |||||
| arm64, Windows x86 and x86_64). If building from source and the C tokenizer | |||||
| cannot be built, you can fall back to the slower pure-Python implementation by | |||||
| setting the environment variable ``WITH_EXTENSION=0`` when installing. | |||||
| To get the latest development version (with `uv`_):: | |||||
| git clone https://github.com/earwig/mwparserfromhell.git | git clone https://github.com/earwig/mwparserfromhell.git | ||||
| cd mwparserfromhell | cd mwparserfromhell | ||||
| @@ -32,7 +38,14 @@ The comprehensive test suite can be run with ``pytest``. If using ``uv``, pass | |||||
| uv run --reinstall-package mwparserfromhell pytest | uv run --reinstall-package mwparserfromhell pytest | ||||
| .. note:: | |||||
| To see if the fast C tokenizer is being used, check the value of | |||||
| ``mwparserfromhell.parser.use_c``. If ``True``, it's being used; if | |||||
| ``False``, the Python fallback is being used. | |||||
| .. _PyPI: https://pypi.org/project/mwparserfromhell/ | .. _PyPI: https://pypi.org/project/mwparserfromhell/ | ||||
| .. _uv: https://docs.astral.sh/uv/ | |||||
| .. _pytest: https://docs.pytest.org/ | .. _pytest: https://docs.pytest.org/ | ||||
| Contents | Contents | ||||
| @@ -38,6 +38,7 @@ dev = [ | |||||
| "psutil", | "psutil", | ||||
| "pytest", | "pytest", | ||||
| "pytest-cov", | "pytest-cov", | ||||
| "setuptools", | |||||
| "sphinx", | "sphinx", | ||||
| ] | ] | ||||
| @@ -45,36 +46,10 @@ dev = [ | |||||
| requires = ["setuptools >= 77.0.3", "setuptools-scm>=8"] | requires = ["setuptools >= 77.0.3", "setuptools-scm>=8"] | ||||
| build-backend = "setuptools.build_meta" | build-backend = "setuptools.build_meta" | ||||
| [[tool.setuptools.ext-modules]] | |||||
| name = "mwparserfromhell.parser._tokenizer" | |||||
| sources = [ | |||||
| "src/mwparserfromhell/parser/ctokenizer/avl_tree.c", | |||||
| "src/mwparserfromhell/parser/ctokenizer/definitions.c", | |||||
| "src/mwparserfromhell/parser/ctokenizer/tag_data.c", | |||||
| "src/mwparserfromhell/parser/ctokenizer/textbuffer.c", | |||||
| "src/mwparserfromhell/parser/ctokenizer/tok_parse.c", | |||||
| "src/mwparserfromhell/parser/ctokenizer/tok_support.c", | |||||
| "src/mwparserfromhell/parser/ctokenizer/tokenizer.c", | |||||
| "src/mwparserfromhell/parser/ctokenizer/tokens.c", | |||||
| ] | |||||
| depends = [ | |||||
| "src/mwparserfromhell/parser/ctokenizer/avl_tree.h", | |||||
| "src/mwparserfromhell/parser/ctokenizer/common.h", | |||||
| "src/mwparserfromhell/parser/ctokenizer/contexts.h", | |||||
| "src/mwparserfromhell/parser/ctokenizer/definitions.h", | |||||
| "src/mwparserfromhell/parser/ctokenizer/tag_data.h", | |||||
| "src/mwparserfromhell/parser/ctokenizer/textbuffer.h", | |||||
| "src/mwparserfromhell/parser/ctokenizer/tok_parse.h", | |||||
| "src/mwparserfromhell/parser/ctokenizer/tok_support.h", | |||||
| "src/mwparserfromhell/parser/ctokenizer/tokenizer.h", | |||||
| "src/mwparserfromhell/parser/ctokenizer/tokens.h", | |||||
| ] | |||||
| include-dirs = ["lib"] | |||||
| optional = true | |||||
| [tool.setuptools_scm] | [tool.setuptools_scm] | ||||
| [tool.cibuildwheel] | [tool.cibuildwheel] | ||||
| environment = { WITH_EXTENSION = "1" } | |||||
| environment-pass = ["RUNNER_OS"] | environment-pass = ["RUNNER_OS"] | ||||
| test-groups = ["dev"] | test-groups = ["dev"] | ||||
| test-command = [ | test-command = [ | ||||
| @@ -0,0 +1,100 @@ | |||||
| #! /usr/bin/env python | |||||
| # | |||||
| # Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
| # | |||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
| # of this software and associated documentation files (the "Software"), to deal | |||||
| # in the Software without restriction, including without limitation the rights | |||||
| # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
| # copies of the Software, and to permit persons to whom the Software is | |||||
| # furnished to do so, subject to the following conditions: | |||||
| # | |||||
| # The above copyright notice and this permission notice shall be included in | |||||
| # all copies or substantial portions of the Software. | |||||
| # | |||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
| # SOFTWARE. | |||||
| from __future__ import annotations | |||||
| import glob | |||||
| import os | |||||
| import sys | |||||
| from enum import Enum | |||||
| from setuptools import Extension, setup | |||||
| from setuptools.command.build_ext import build_ext | |||||
| class UseExtension(Enum): | |||||
| REQUIRED = 1 | |||||
| OPTIONAL = 2 | |||||
| IGNORED = 3 | |||||
| if "WITH_EXTENSION" in os.environ: | |||||
| if "WITHOUT_EXTENSION" in os.environ: | |||||
| raise RuntimeError("Cannot set both $WITH_EXTENSION and $WITHOUT_EXTENSION") | |||||
| value = os.environ["WITH_EXTENSION"].lower() | |||||
| if value in ("1", "true", "yes", "required"): | |||||
| use_extension = UseExtension.REQUIRED | |||||
| elif value == "optional": | |||||
| use_extension = UseExtension.OPTIONAL | |||||
| elif value in ("0", "false", "no"): | |||||
| use_extension = UseExtension.IGNORED | |||||
| else: | |||||
| raise RuntimeError( | |||||
| f"Unknown value for $WITH_EXTENSION; should be '1', '0', or 'optional', " | |||||
| f"but found {value!r}" | |||||
| ) | |||||
| elif "WITHOUT_EXTENSION" in os.environ: | |||||
| value = os.environ["WITHOUT_EXTENSION"].lower() | |||||
| if value in ("1", "true", "yes"): | |||||
| use_extension = UseExtension.IGNORED | |||||
| elif value in ("0", "false", "no"): | |||||
| use_extension = UseExtension.REQUIRED | |||||
| else: | |||||
| raise RuntimeError( | |||||
| f"Unknown value for $WITHOUT_EXTENSION; should be '1', or '0', " | |||||
| f"but found {value!r}" | |||||
| ) | |||||
| else: | |||||
| use_extension = UseExtension.REQUIRED | |||||
| if use_extension == UseExtension.IGNORED: | |||||
| ext_modules = [] | |||||
| else: | |||||
| tokenizer = Extension( | |||||
| "mwparserfromhell.parser._tokenizer", | |||||
| sources=sorted(glob.glob("src/mwparserfromhell/parser/ctokenizer/*.c")), | |||||
| depends=sorted(glob.glob("src/mwparserfromhell/parser/ctokenizer/*.h")), | |||||
| optional=use_extension == UseExtension.OPTIONAL, | |||||
| ) | |||||
| ext_modules = [tokenizer] | |||||
| def build_ext_patched(self): | |||||
| try: | |||||
| build_ext_original(self) | |||||
| except Exception: | |||||
| print( | |||||
| """ | |||||
| ********** | |||||
| Note: To avoid building the C tokenizer extension, set the environment variable \ | |||||
| `WITH_EXTENSION=0`. | |||||
| This will fall back to a pure-Python tokenizer. | |||||
| ********** | |||||
| """, | |||||
| file=sys.stderr, | |||||
| ) | |||||
| raise | |||||
| build_ext.run, build_ext_original = build_ext_patched, build_ext.run | |||||
| setup(ext_modules=ext_modules) | |||||
| @@ -361,6 +361,7 @@ dev = [ | |||||
| { name = "psutil" }, | { name = "psutil" }, | ||||
| { name = "pytest" }, | { name = "pytest" }, | ||||
| { name = "pytest-cov" }, | { name = "pytest-cov" }, | ||||
| { name = "setuptools" }, | |||||
| { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, | { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, | ||||
| { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, | { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, | ||||
| { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, | { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, | ||||
| @@ -373,6 +374,7 @@ dev = [ | |||||
| { name = "psutil" }, | { name = "psutil" }, | ||||
| { name = "pytest" }, | { name = "pytest" }, | ||||
| { name = "pytest-cov" }, | { name = "pytest-cov" }, | ||||
| { name = "setuptools" }, | |||||
| { name = "sphinx" }, | { name = "sphinx" }, | ||||
| ] | ] | ||||
| @@ -475,6 +477,15 @@ wheels = [ | |||||
| ] | ] | ||||
| [[package]] | [[package]] | ||||
| name = "setuptools" | |||||
| version = "80.9.0" | |||||
| source = { registry = "https://pypi.org/simple" } | |||||
| sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } | |||||
| wheels = [ | |||||
| { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, | |||||
| ] | |||||
| [[package]] | |||||
| name = "snowballstemmer" | name = "snowballstemmer" | ||||
| version = "3.0.1" | version = "3.0.1" | ||||
| source = { registry = "https://pypi.org/simple" } | source = { registry = "https://pypi.org/simple" } | ||||