alibi-detect
alibi-detect copied to clipboard
Publish releases to PyPI via a GitHub Action (and replace `setup.py` with `pyproject.toml`)
This PR implements a release process via github actions. Performing a release will be as simple as pushing a new version release tag e.g.
git tag v0.11.0
git push v0.11.0
This will be particularly useful now that we are discussing doing more frequent patch releases!
A large part of this PR involves replacing the setup.py
and setup.cfg
files with a pyproject.toml
file. This isn't strictly neccesary but provides a more deterministic build. This simplifies the github action but also allows us to better emulate releases manually for testing (e.g. to PyPI testing).
setup.py to pyproject.toml
Historically, a Python package has been defined by the existence of the setup.py
(and setup.cfg
) file. This file is executed to install and build the package (e.g. setup.py install
and setup.py sdist
). This approach suffers from a chicken and egg problem; setup.py
has dependencies e.g. setuptools
and wheels
, but we cannot define these deps without executing setup.py
.
This prevents properly deterministic builds; for example the conda-forge alibi-detect
0.9.1 build failed because 0.9.1 was built and published to pypi using a different version of setuptools
(the install and build were performed inside a conda env with an old default setuptools
version), which did not package LICENCE.txt
within the build.
This issue is solved by replacing setup.py
/.cfg
with a pyproject.toml
file. This file explicitly specifies the required build backend (e.g. setuptools
, poetry
, flit
), as well as defining all deps (previously in setup.py
) and other config options (previously in setup.cfg
). pip install
's are also then carried out in an isolated environment, giving more deterministic installs as well as builds.
Background reading
A typical pyproject.toml
looks something like the following:
[build-system]
requires = [ # PEP 518 - what deps are required to build
"setuptools>=61.0.0, <63.0.0",
"wheel>=0.36.0, <0.38.0"
]
build-backend = "setuptools.build_meta" # PEP 517 - what function to call to build
# PEP 621 - Project metadata
[project]
name = "alibi-detect"
description = "Algorithms for outlier, adversarial and drift detection."
The structure of this file (and how it is used) are covered by a number of recent PEP's:
-
PEP 518: This specifies how minimum build system requirements should be specified in a
pyproject.toml
(via thebuild-system.requires
table). -
PEP 517: This specifies how the Python object to be used for the build should be specified (via the
build-system.build-backend
table). This PEP also defines how thepyproject.toml
should facilitate installs i.e. that we should be able to simply runpip install .
when apyproject.toml
is present instead ofsetup.py
. -
PEP 621: This specifies how the package's core metadata is defined (via the
project
table). Note that not all build tools are officially compliant with this, for examplepoetry
uses a different "table" to set the[requires-python](https://peps.python.org/pep-0621/#requires-python)
table. -
PEP 631: To do with syntax for deps version specifications. Merged into PEP 621.
-
PEP 639: Related to improving licence specification. Not yet approved.
-
PEP 660: Similar to PEP 517, but for editable installs i.e.
pip install -e .
. Support for this is patchy:- pip: Supported (regardless of whether backend is PEP 660 complient) since 21.3.1. See https://github.com/SeldonIO/alibi-detect/pull/495#issuecomment-1311526938.
-
setuptools: Not yet supported. Although still OK as long as as
pip
is recent enough (see above point). - flit: Supported since 3.4.
- poetry: Supported since 1.1.0.
Considerations
-
With older
setuptools
asetup.py
stub file (literally just containingsetup()
) was required in order for editable installs to still work (i.e.pip install -e .
). However, the presence of asetup.py
might encourage use of legacy commands such aspython.py sdist
. After testing these commands, I've found that they don't play nicely with the new setup. For example, the[tool.setuptools.packages.find]
settings are ignored, and we end up withdocs
etc in thetar.gz
file. I've therefore removed thesetup.py
entirely.pip install -e
still works as long aspip >=21.3
is used. -
There are some config settings such as
flake8
ones which I haven't figured out how to transition topyproject.toml
. Therefore for now we still have asetup.cfg
too. -
We still need to choose the actual build backend. IMO
poetry
adds too much complication; thepyproject.toml
becomes quite "non-standard", and I wonder if its dual use as virtual enviroment tool might cause more issues than it solves for now (e.g. see https://github.com/python-poetry/poetry/issues/496). For now, I've stuck withsetuptools
, but as recommended by them, this does mean installing PyPA Build to do the actual build (not the pip install!). (see Makefile in PR). IMO flit is worth exploring too. -
There is one downside to the new isolated build approach; if a user doesn't have the specified
setuptools
andwheel
in their local cache of wheels, they won't be able to perform an install (or build) offline.
Progress and TODO's
The install and build seem to work locally, and examining the sdist tar.gz
archive, it looks like we have the right stuff in there. Remaining TODO's are:
- [x] Check what happens (especially wrt to editable install) if the
setup.py
is removed - See https://github.com/SeldonIO/alibi-detect/pull/495#issuecomment-1311526938. - [x] Check that publishing proceeds as expected by doing a test release to https://test.pypi.org/ - Pushing a
v0.10.5-alpha
tag to my fork results in this: https://test.pypi.org/project/alibi-detect/0.10.5a0/, which looks OK. - [ ] Update release process wiki. Check if any other docs, CI etc need updating.
- [ ] Add logic to publish to Test PyPI if alpha, beta or rc tag, otherwise publish to PyPI if pure semver tag.
- [ ] Decide if we are happy to remove
setup.py
and move topyproject.toml
. If not, we can only usepyproject.toml
to setbuild-system
(see https://github.com/SeldonIO/alibi-detect/pull/495#issuecomment-1311526938).
Note to self: Investigate why tests are not being excluded from sdist
. Same for doc
and examples
.
Additional note on editable installs
The current setup in this PR supports pip install -e .
when using pip >= 21.1.0
. For older versions the following is given:
ERROR: File "setup.py" not found. Directory cannot be installed in editable mode: /home/ascillitoe/Software/alibi-detect
(A "pyproject.toml" file was found, but editable mode currently requires a setup.py based build.)
WARNING: You are using pip version 21.0; however, version 22.3.1 is available.
You should consider upgrading via the '/home/ascillitoe/Software/alibi-detect/venv_tmp/bin/python -m pip install --upgrade pip' command.
pip 21.1
was released on Apr 24, 2021, so not exactly bleeding edge anymore. We could also add pip install --upgrade pip
to our installation instructions. Alternatively, another option is to keep our setup.py
and only use pyproject.toml
to specify our build-system
, like thinc does (PR incoming).