Move to a PEP 517 pyproject.toml build, drop setup.py - #73
Open
IvanaGyro wants to merge 3 commits into
Open
Conversation
Closes fonttools#53. The only thing setup.py still needed dynamic logic for was appending unicodedata2/pypy_ctype.c to the source list on PyPy. That file existed solely to supply _Py_ctype_toupper[256] for Py_TOUPPER(), which CPython exports from libpython but PyPy's cpyext headers do not define. Replace it with capability detection in _unicodedata2_compat.h. That header is included after Python.h, which is what makes "#ifdef Py_TOUPPER" meaningful: CPython defines the macro via cpython/pyctype.h (pulled in by Python.h on every non-limited-API build, 3.8 through main), PyPy does not. When it is missing we define UNICODEDATA2_TOUPPER() over a private, static 256-byte table with the same mapping. Detecting the macro rather than _Py_ctype_toupper matters: the latter is a linker symbol the preprocessor cannot see. The table is deliberately not named _Py_ctype_toupper, so nothing is added to CPython's reserved namespace, and it stays static so it is not exported from the extension. Both branches of the macro apply Py_CHARMASK() to their argument, as Py_TOUPPER() always did, so the call sites no longer mask it themselves. The mask is idempotent and the compiler already folded the duplicate: the .text section is byte identical either way, on both interpreters. Uppercasing stays table-based on both implementations, so name lookup performance is unchanged; on PyPy the table is now a same-translation-unit static const rather than an extern from a separate object file. With no implementation-specific source, the extension can be declared statically via setuptools' ext-modules table, and setup.py can go. The build now needs setuptools >= 77.0.1 (declarative ext-modules landed in 74.1, PEP 639 license metadata in 77; 77.0.0 was never published). Also: - long_description keeps its rendered form: setup.py injected a "Changelog" setext heading between README.md and CHANGELOG.md, so CHANGELOG.md now carries that heading itself and the two files are concatenated via tool.setuptools.dynamic. - deploy job builds the sdist with "python -m build --sdist". - tox envlist points at PyPy versions that still exist. - CIBW_ENABLE=pypy restores PyPy wheels: cibuildwheel 3.0 moved PyPy into an opt-in enable group, so 17.0.0 and 17.0.1 shipped none, where 16.0.0 and earlier shipped 11-12. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Fyhi9MNffq3B9JLVSukhEa
CIBW_TEST_EXTRAS installs this extra into every cibuildwheel test
environment, then CIBW_TEST_COMMAND runs plain "pytest {project}/tests".
Nothing in the repo sets addopts, passes -n, or passes --cov: there is no
conftest.py, no pytest.ini, no setup.cfg, and neither pyproject.toml nor
tox.ini configures pytest. So pytest-xdist loaded as a plugin and sat
idle, and coverage was never reachable at all -- pytest integration would
need pytest-cov, and nothing invokes "coverage run" either.
Both were installed for every wheel in the matrix across three operating
systems, including the aarch64 jobs that run under QEMU emulation.
pytest-randomly stays: it is a plugin that shuffles test order simply by
being installed, so it does affect the run.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fyhi9MNffq3B9JLVSukhEa
tox is an orchestrator, not a test dependency: it builds the environment that installs the package, so it does not belong in the testing extra, which CIBW_TEST_EXTRAS installs into every wheel test environment and which tox itself does not even use (tox.ini declares deps = pytest). Dependency groups are the right home. They are local-only: nothing about them reaches the published wheel or sdist metadata, so this adds no install-time cost or risk for anyone depending on unicodedata2, while giving contributors "pip install --group dev" (pip >= 25.1) alongside the existing "pip install tox". Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Fyhi9MNffq3B9JLVSukhEa
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #53.
The only thing setup.py still needed dynamic logic for was appending unicodedata2/pypy_ctype.c to the source list on PyPy. That file existed solely to supply _Py_ctype_toupper[256] for Py_TOUPPER(), which CPython exports from libpython but PyPy's cpyext headers do not define.
Replace it with capability detection in _unicodedata2_compat.h. That header is included after Python.h, which is what makes "#ifdef Py_TOUPPER" meaningful: CPython defines the macro via cpython/pyctype.h (pulled in by Python.h on every non-limited-API build, 3.8 through main), PyPy does not. When it is missing we define UNICODEDATA2_TOUPPER() over a private, static 256-byte table with the same mapping. Detecting the macro rather than _Py_ctype_toupper matters: the latter is a linker symbol the preprocessor cannot see. The table is deliberately not named _Py_ctype_toupper, so nothing is added to CPython's reserved namespace, and it stays static so it is not exported from the extension.
Both branches of the macro apply Py_CHARMASK() to their argument, as Py_TOUPPER() always did, so the call sites no longer mask it themselves. The mask is idempotent and the compiler already folded the duplicate: the .text section is byte identical either way, on both interpreters.
Uppercasing stays table-based on both implementations, so name lookup performance is unchanged; on PyPy the table is now a same-translation-unit static const rather than an extern from a separate object file.
With no implementation-specific source, the extension can be declared statically via setuptools' ext-modules table, and setup.py can go. The build now needs setuptools >= 77.0.1 (declarative ext-modules landed in 74.1, PEP 639 license metadata in 77; 77.0.0 was never published).
Also: