Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use python-version-specific requirements.txt instead of single generic requirements.txt #150

Merged
merged 10 commits into from Dec 1, 2020

Conversation

anthrotype
Copy link
Member

We have a bunch of dependencies that are only needed on specific python versions, mostly backports like dataclasses, importlib_resources; others like pytype aren't compatible on latest pythons, etc.

Right now we use conditional environment markers in either setup.py's install_requires or in dev-requirements.in to include/exclude named packages for specific py-versions (e.g. dataclasses; python_version < '3.7'.
Then we periodically run pip-compile (from pip-tools suite) to "freeze" these abstract, top-level dependencies and produce two generic requirements.txt and dev-requirements.txt.

The problem is pip-compile resolves the environment markers based on the python it is run with, so it produces different results depending on whether it is run on python3.6 or python3.9... That means we often have to go and edit manually the generated requirements.txt file generated from pip-compile, which defeats the purpose of it being "generated".

Unfortunately pip-compile does not support generating a combined requirements.txt file where differences between python versions/platforms/archs are reconciled through respective environment markers (cf. jazzband/pip-tools#826).

Pip-tools devs currently recommend running pip-compile on each targeted python environment, i.e. generating as many concrete requirements.txt files as are the python environments one wishes to support: jazzband/pip-tools#651

A distinct but related issue is that currently pip-compile's support for setup.py is limited in that it ignores environment markers, whereas it does use them when compiling from a requirements.in file.

The following PR attempts to fix thes problems.

  1. top-level abstract dependencies (either runtime or develop-time) are moved to external requirements.in files; setup.py is changed to read off them so they stay in sync and there's only one place to edit;
  2. tox config is modified so that running tox -e py39 will install deps from python3.9-requirements.txt, whereas tox -e py36 will install from the python3.6-requirements.txt, and so forth.
  3. add a script requirements/pip-compile-all.sh to regenerate all requirements.txt for each supported python, whenever any of the top-level dependencies is added/removed/changed. No more need to tweak them manually.

... containing all top-level (*.in) and concrete requirements.txt, the
latter parametrized by python version.

We have a bunch of dependencies that are only needed on specific python
versions (e.g. backports like dataclasses, importlib_resources, etc).
We use conditional environment markers for those in the top-level
requirements.in files.
pip-compile does not yet support generating a combined requirements.txt
file where differences across python versions/platforms/archs are
reconciled using environmet markers:
jazzband/pip-tools#826

They currently recommend running pip-compile on each targeted python
environment generating as many concrete requirements.txt files
jazzband/pip-tools#651

So this is what I have done here.
…irements.in files

pip-compile setup.py doesn't support environment markers ('; python_version < 3.7'), whereas it does when compiling from requirements.in files.
So we read top-level dependencies from external *.in files and stick
them in the setup.py install_requires and extras_require keywords.

The 'dev' extra is new. One can now do `pip install -e .[dev]` and have
all the development requirements installed alongside the current module.
This is handy for developers bootstrapping a new venv to work on.
It's a common practice among python developers.
This is handy when any top-level deps change. The shell script is just a shorthand for the longish tox command
tox -e py39-dev will install in editable mode (for when one is in a rush).
One can also use this as a way to quickly bootstrap a development virtual environment.
E.g. after running the above command, a new venv is created in .tox/py39-dev with all
the pinned requirements installed and the current module installed in editable mode,
ready for editing source files and running directly the pytest command therein
The generic invocation 'tox -e py' does not work any more, because we now install dependencies using python-version-specific requirements.txt files and we need to know the exact python major.minor version.
Thus we set TOXENV environment variable such that invoking 'tox'
without additional -e options will run only the specified python
version.
tox.ini Show resolved Hide resolved
@rsheeter
Copy link
Collaborator

It feels like it approaches being simpler to hand-write the requirements file with the correct version bounds and use CI to detect when you screw up

Using tox -l command to list them, so we have a single source of truth for what are the versions that we support. And we only need to change one file when, e.g., python3.10 is out.
otherwise pip-compile will write full absolute paths in the generated requirements.txt..
@anthrotype anthrotype merged commit e39dca9 into master Dec 1, 2020
@anthrotype anthrotype deleted the requirements branch December 1, 2020 16:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants