pyzeo ships a Cython-based C++ extension. Following Cython's
recommendation,
the pre-generated src/pyzeo/extension.cpp is committed and shipped so end
users do not need Cython to build from source (setup.py sets
USE_CYTHON = False).
Releases are published manually with twine: bump the version, regenerate
the C++ from the .pyx, build wheels via the wheels GitHub Actions workflow,
then upload the sdist + wheels to PyPI.
python -m pip install --upgrade pip
pip install --upgrade setuptools wheel cython build twinesetuptools, wheel and cython are needed to regenerate the C++ source and
compile (step 1); build and twine are used to package and upload (steps 3,
5). On Python 3.12+ distutils was removed from the standard library, so an
up-to-date setuptools (which vendors a distutils shim) is required.
- A C++ compiler and the Python development headers (step 1 compiles the
extension after regenerating the C++). On Debian/Ubuntu, for the Python
version you build with:
sudo apt install pythonX.Y-dev(e.g.python3.13-dev). Without them the build fails withfatal error: Python.h: No such file or directory. - A PyPI account with upload rights to
pyzeo, configured via~/.pypircor a token passed totwine. - The
ghCLI authenticated to this repo (used to trigger the workflow and download artifacts).
This keeps the shipped extension.cpp in sync with the current extension.pyx
and a current Cython.
# 1. In setup.py set: USE_CYTHON = True
python setup.py build_ext --inplace --force
# 2. Revert setup.py back to: USE_CYTHON = FalseCommit the regenerated src/pyzeo/extension.cpp. The .so produced is
gitignored — do not commit it.
Edit version in pyproject.toml. Update the requires-python value and the
Programming Language :: Python :: 3.x classifiers if the supported Python
range changed (also update the matrix in .github/workflows/test.yml and
CIBW_SKIP in .github/workflows/wheels.yml accordingly).
python -m build --sdist
tar tzf dist/pyzeo-<version>.tar.gz | grep extension.cpp # must be presentOptional but recommended — prove a from-source install compiles without Cython:
python -m venv /tmp/pyzeo-test && source /tmp/pyzeo-test/bin/activate
pip install dist/pyzeo-<version>.tar.gz
cd tests && pytest
deactivateNote: only
extension.cpp(not.pyx/.pxd) ships in the sdist. The bundled.cc/.h/.hh/Eigen files come from thesetup.pyExtension sources +MANIFEST.in.
Push the version bump + regenerated .cpp to main (or a release branch and
merge it). Then trigger the wheels workflow — it is workflow_dispatch-only:
gh workflow run wheels.yml --ref main(Or via the GitHub Actions UI → "wheels" → Run workflow.)
When the run finishes, download the built wheels into dist/ (one artifact per
OS: ubuntu, macos-13, macos-14):
gh run download <run-id> -n cibw-wheels-* -D dist/twine check dist/pyzeo-<version>* # validate sdist + wheels
twine upload dist/pyzeo-<version>.tar.gz dist/pyzeo-<version>-*.whlUpload the sdist and all wheels together. To rehearse first, upload to TestPyPI:
twine upload --repository testpypi dist/pyzeo-<version>*.
git tag v<version>
git push origin v<version>
gh release create v<version> --generate-notes # optional GitHub Release- Regenerated
extension.cpp(USE_CYTHON True → build → False) and committed it - Bumped
versioninpyproject.toml(+ classifiers / Python range if changed) -
python -m build --sdist; confirmedextension.cppis in the tarball - (Optional) clean-venv install of the sdist +
pytestpasses - Pushed to
main, rangh workflow run wheels.yml, downloaded artifacts -
twine check+twine uploadof sdist and all wheels - Tagged
v<version>and pushed the tag