Merge pull request #271597 from adisbladis/python-runtime-build-time-sep

python3.pkgs.buildPythonPackage: Separate runtime & build time dependencies
This commit is contained in:
Martin Weinelt 2024-02-20 05:05:20 +01:00 committed by GitHub
commit fa83add1b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 84 additions and 56 deletions

View file

@ -116,11 +116,11 @@ buildPythonPackage rec {
rm testing/test_argcomplete.py rm testing/test_argcomplete.py
''; '';
nativeBuildInputs = [ build-system = [
setuptools-scm setuptools-scm
]; ];
propagatedBuildInputs = [ dependencies = [
attrs attrs
py py
setuptools setuptools
@ -172,7 +172,7 @@ following are specific to `buildPythonPackage`:
variable in wrapped programs. variable in wrapped programs.
* `pyproject`: Whether the pyproject format should be used. When set to `true`, * `pyproject`: Whether the pyproject format should be used. When set to `true`,
`pypaBuildHook` will be used, and you can add the required build dependencies `pypaBuildHook` will be used, and you can add the required build dependencies
from `build-system.requires` to `nativeBuildInputs`. Note that the pyproject from `build-system.requires` to `build-system`. Note that the pyproject
format falls back to using `setuptools`, so you can use `pyproject = true` format falls back to using `setuptools`, so you can use `pyproject = true`
even if the package only has a `setup.py`. When set to `false`, you can even if the package only has a `setup.py`. When set to `false`, you can
use the existing [hooks](#setup-hooks0 or provide your own logic to build the use the existing [hooks](#setup-hooks0 or provide your own logic to build the
@ -206,17 +206,22 @@ build inputs (see "Specifying dependencies"). The following are of special
interest for Python packages, either because these are primarily used, or interest for Python packages, either because these are primarily used, or
because their behaviour is different: because their behaviour is different:
* `nativeBuildInputs ? []`: Build-time only dependencies. Typically executables * `nativeBuildInputs ? []`: Build-time only dependencies. Typically executables.
as well as the items listed in `setup_requires`. * `build-system ? []`: Build-time only Python dependencies. Items listed in `build-system.requires`/`setup_requires`.
* `buildInputs ? []`: Build and/or run-time dependencies that need to be * `buildInputs ? []`: Build and/or run-time dependencies that need to be
compiled for the host machine. Typically non-Python libraries which are being compiled for the host machine. Typically non-Python libraries which are being
linked. linked.
* `nativeCheckInputs ? []`: Dependencies needed for running the [`checkPhase`](#ssec-check-phase). These * `nativeCheckInputs ? []`: Dependencies needed for running the [`checkPhase`](#ssec-check-phase). These
are added to [`nativeBuildInputs`](#var-stdenv-nativeBuildInputs) when [`doCheck = true`](#var-stdenv-doCheck). Items listed in are added to [`nativeBuildInputs`](#var-stdenv-nativeBuildInputs) when [`doCheck = true`](#var-stdenv-doCheck). Items listed in
`tests_require` go here. `tests_require` go here.
* `propagatedBuildInputs ? []`: Aside from propagating dependencies, * `dependencies ? []`: Aside from propagating dependencies,
`buildPythonPackage` also injects code into and wraps executables with the `buildPythonPackage` also injects code into and wraps executables with the
paths included in this list. Items listed in `install_requires` go here. paths included in this list. Items listed in `install_requires` go here.
* `optional-dependencies ? { }`: Optional feature flagged dependencies. Items listed in `extras_requires` go here.
Aside from propagating dependencies,
`buildPythonPackage` also injects code into and wraps executables with the
paths included in this list. Items listed in `extras_requires` go here.
##### Overriding Python packages {#overriding-python-packages} ##### Overriding Python packages {#overriding-python-packages}
@ -299,11 +304,12 @@ python3Packages.buildPythonApplication rec {
hash = "sha256-Pe229rT0aHwA98s+nTHQMEFKZPo/yw6sot8MivFDvAw="; hash = "sha256-Pe229rT0aHwA98s+nTHQMEFKZPo/yw6sot8MivFDvAw=";
}; };
nativeBuildInputs = with python3Packages; [ build-system = with python3Packages; [
setuptools setuptools
wheel
]; ];
propagatedBuildInputs = with python3Packages; [ dependencies = with python3Packages; [
tornado tornado
python-daemon python-daemon
]; ];
@ -462,11 +468,11 @@ are used in [`buildPythonPackage`](#buildpythonpackage-function).
- `eggBuildHook` to skip building for eggs. - `eggBuildHook` to skip building for eggs.
- `eggInstallHook` to install eggs. - `eggInstallHook` to install eggs.
- `pipBuildHook` to build a wheel using `pip` and PEP 517. Note a build system - `pipBuildHook` to build a wheel using `pip` and PEP 517. Note a build system
(e.g. `setuptools` or `flit`) should still be added as `nativeBuildInput`. (e.g. `setuptools` or `flit`) should still be added as `build-system`.
- `pypaBuildHook` to build a wheel using - `pypaBuildHook` to build a wheel using
[`pypa/build`](https://pypa-build.readthedocs.io/en/latest/index.html) and [`pypa/build`](https://pypa-build.readthedocs.io/en/latest/index.html) and
PEP 517/518. Note a build system (e.g. `setuptools` or `flit`) should still PEP 517/518. Note a build system (e.g. `setuptools` or `flit`) should still
be added as `nativeBuildInput`. be added as `build-system`.
- `pipInstallHook` to install wheels. - `pipInstallHook` to install wheels.
- `pytestCheckHook` to run tests with `pytest`. See [example usage](#using-pytestcheckhook). - `pytestCheckHook` to run tests with `pytest`. See [example usage](#using-pytestcheckhook).
- `pythonCatchConflictsHook` to check whether a Python package is not already existing. - `pythonCatchConflictsHook` to check whether a Python package is not already existing.
@ -881,7 +887,7 @@ buildPythonPackage rec {
hash = "sha256-CP3V73yWSArRHBLUct4hrNMjWZlvaaUlkpm1QP66RWA="; hash = "sha256-CP3V73yWSArRHBLUct4hrNMjWZlvaaUlkpm1QP66RWA=";
}; };
nativeBuildInputs = [ build-system = [
setuptools setuptools
wheel wheel
]; ];
@ -941,7 +947,7 @@ with import <nixpkgs> {};
hash = "sha256-CP3V73yWSArRHBLUct4hrNMjWZlvaaUlkpm1QP66RWA="; hash = "sha256-CP3V73yWSArRHBLUct4hrNMjWZlvaaUlkpm1QP66RWA=";
}; };
nativeBuildInputs = [ build-system = [
python311.pkgs.setuptools python311.pkgs.setuptools
python311.pkgs.wheel python311.pkgs.wheel
]; ];
@ -977,13 +983,15 @@ that we introduced with the `let` expression.
#### Handling dependencies {#handling-dependencies} #### Handling dependencies {#handling-dependencies}
Our example, `toolz`, does not have any dependencies on other Python packages or Our example, `toolz`, does not have any dependencies on other Python packages or system libraries.
system libraries. According to the manual, [`buildPythonPackage`](#buildpythonpackage-function) uses the [`buildPythonPackage`](#buildpythonpackage-function) uses the the following arguments in the following circumstances:
arguments [`buildInputs`](#var-stdenv-buildInputs) and [`propagatedBuildInputs`](#var-stdenv-propagatedBuildInputs) to specify dependencies. If
something is exclusively a build-time dependency, then the dependency should be - `dependencies` - For Python runtime dependencies.
included in [`buildInputs`](#var-stdenv-buildInputs), but if it is (also) a runtime dependency, then it - `build-system` - For Python build-time requirements.
should be added to [`propagatedBuildInputs`](#var-stdenv-propagatedBuildInputs). Test dependencies are considered - [`buildInputs`](#var-stdenv-buildInputs) - For non-Python build-time requirements.
build-time dependencies and passed to [`nativeCheckInputs`](#var-stdenv-nativeCheckInputs). - [`nativeCheckInputs`](#var-stdenv-nativeCheckInputs) - For test dependencies
Dependencies can belong to multiple arguments, for example if something is both a build time requirement & a runtime dependency.
The following example shows which arguments are given to [`buildPythonPackage`](#buildpythonpackage-function) in The following example shows which arguments are given to [`buildPythonPackage`](#buildpythonpackage-function) in
order to build [`datashape`](https://github.com/blaze/datashape). order to build [`datashape`](https://github.com/blaze/datashape).
@ -1013,12 +1021,12 @@ buildPythonPackage rec {
hash = "sha256-FLLvdm1MllKrgTGC6Gb0k0deZeVYvtCCLji/B7uhong="; hash = "sha256-FLLvdm1MllKrgTGC6Gb0k0deZeVYvtCCLji/B7uhong=";
}; };
nativeBuildInputs = [ build-system = [
setuptools setuptools
wheel wheel
]; ];
propagatedBuildInputs = [ dependencies = [
multipledispatch multipledispatch
numpy numpy
python-dateutil python-dateutil
@ -1041,7 +1049,7 @@ buildPythonPackage rec {
We can see several runtime dependencies, `numpy`, `multipledispatch`, and We can see several runtime dependencies, `numpy`, `multipledispatch`, and
`python-dateutil`. Furthermore, we have [`nativeCheckInputs`](#var-stdenv-nativeCheckInputs) with `pytest`. `python-dateutil`. Furthermore, we have [`nativeCheckInputs`](#var-stdenv-nativeCheckInputs) with `pytest`.
`pytest` is a test runner and is only used during the [`checkPhase`](#ssec-check-phase) and is `pytest` is a test runner and is only used during the [`checkPhase`](#ssec-check-phase) and is
therefore not added to [`propagatedBuildInputs`](#var-stdenv-propagatedBuildInputs). therefore not added to `dependencies`.
In the previous case we had only dependencies on other Python packages to consider. In the previous case we had only dependencies on other Python packages to consider.
Occasionally you have also system libraries to consider. E.g., `lxml` provides Occasionally you have also system libraries to consider. E.g., `lxml` provides
@ -1068,7 +1076,7 @@ buildPythonPackage rec {
hash = "sha256-s9NiusRxFydHzaNRMjjxFcvWxfi45jGb9ql6eJJyQJk="; hash = "sha256-s9NiusRxFydHzaNRMjjxFcvWxfi45jGb9ql6eJJyQJk=";
}; };
nativeBuildInputs = [ build-system = [
setuptools setuptools
wheel wheel
]; ];
@ -1125,7 +1133,7 @@ buildPythonPackage rec {
hash = "sha256-9ru2r6kwhUCaskiFoaPNuJCfCVoUL01J40byvRt4kHQ="; hash = "sha256-9ru2r6kwhUCaskiFoaPNuJCfCVoUL01J40byvRt4kHQ=";
}; };
nativeBuildInputs = [ build-system = [
setuptools setuptools
wheel wheel
]; ];
@ -1136,7 +1144,7 @@ buildPythonPackage rec {
fftwLongDouble fftwLongDouble
]; ];
propagatedBuildInputs = [ dependencies = [
numpy numpy
scipy scipy
]; ];
@ -1459,9 +1467,7 @@ mode is activated.
In the following example, we create a simple environment that has a Python 3.11 In the following example, we create a simple environment that has a Python 3.11
version of our package in it, as well as its dependencies and other packages we version of our package in it, as well as its dependencies and other packages we
like to have in the environment, all specified with [`propagatedBuildInputs`](#var-stdenv-propagatedBuildInputs). like to have in the environment, all specified with `dependencies`.
Indeed, we can just add any package we like to have in our environment to
[`propagatedBuildInputs`](#var-stdenv-propagatedBuildInputs).
```nix ```nix
with import <nixpkgs> {}; with import <nixpkgs> {};
@ -1470,9 +1476,11 @@ with python311Packages;
buildPythonPackage rec { buildPythonPackage rec {
name = "mypackage"; name = "mypackage";
src = ./path/to/package/source; src = ./path/to/package/source;
propagatedBuildInputs = [ dependencies = [
pytest pytest
numpy numpy
];
propagatedBuildInputs = [
pkgs.libsndfile pkgs.libsndfile
]; ];
} }
@ -1519,7 +1527,7 @@ buildPythonPackage rec {
hash = "sha256-CP3V73yWSArRHBLUct4hrNMjWZlvaaUlkpm1QP66RWA="; hash = "sha256-CP3V73yWSArRHBLUct4hrNMjWZlvaaUlkpm1QP66RWA=";
}; };
nativeBuildInputs = [ build-system = [
setuptools setuptools
wheel wheel
]; ];
@ -1903,8 +1911,8 @@ configure alternatives](#sec-overlays-alternatives-blas-lapack)".
In a `setup.py` or `setup.cfg` it is common to declare dependencies: In a `setup.py` or `setup.cfg` it is common to declare dependencies:
* `setup_requires` corresponds to [`nativeBuildInputs`](#var-stdenv-nativeBuildInputs) * `setup_requires` corresponds to `build-system`
* `install_requires` corresponds to [`propagatedBuildInputs`](#var-stdenv-propagatedBuildInputs) * `install_requires` corresponds to `dependencies`
* `tests_require` corresponds to [`nativeCheckInputs`](#var-stdenv-nativeCheckInputs) * `tests_require` corresponds to [`nativeCheckInputs`](#var-stdenv-nativeCheckInputs)
### How to enable interpreter optimizations? {#optimizations} ### How to enable interpreter optimizations? {#optimizations}
@ -1928,12 +1936,10 @@ in mypython
Some packages define optional dependencies for additional features. With Some packages define optional dependencies for additional features. With
`setuptools` this is called `extras_require` and `flit` calls it `setuptools` this is called `extras_require` and `flit` calls it
`extras-require`, while PEP 621 calls these `optional-dependencies`. A `extras-require`, while PEP 621 calls these `optional-dependencies`.
method for supporting this is by declaring the extras of a package in its
`passthru`, e.g. in case of the package `dask`
```nix ```nix
passthru.optional-dependencies = { optional-dependencies = {
complete = [ distributed ]; complete = [ distributed ];
}; };
``` ```
@ -1941,11 +1947,13 @@ passthru.optional-dependencies = {
and letting the package requiring the extra add the list to its dependencies and letting the package requiring the extra add the list to its dependencies
```nix ```nix
propagatedBuildInputs = [ dependencies = [
... ...
] ++ dask.optional-dependencies.complete; ] ++ dask.optional-dependencies.complete;
``` ```
This method is using `passthru`, meaning that changing `optional-dependencies` of a package won't cause it to rebuild.
Note this method is preferred over adding parameters to builders, as that can Note this method is preferred over adding parameters to builders, as that can
result in packages depending on different variants and thereby causing result in packages depending on different variants and thereby causing
collisions. collisions.

View file

@ -45,6 +45,14 @@
# C can import package A propagated by B # C can import package A propagated by B
, propagatedBuildInputs ? [] , propagatedBuildInputs ? []
# Python module dependencies.
# These are named after PEP-621.
, dependencies ? []
, optional-dependencies ? {}
# Python PEP-517 build systems.
, build-system ? []
# DEPRECATED: use propagatedBuildInputs # DEPRECATED: use propagatedBuildInputs
, pythonPath ? [] , pythonPath ? []
@ -97,8 +105,6 @@
, meta ? {} , meta ? {}
, passthru ? {}
, doCheck ? config.doCheckByDefault or false , doCheck ? config.doCheckByDefault or false
, disabledTestPaths ? [] , disabledTestPaths ? []
@ -193,10 +199,28 @@ let
"setuptools" "wheel" "setuptools" "wheel"
]; ];
passthru =
attrs.passthru or { }
// {
updateScript = let
filename = builtins.head (lib.splitString ":" self.meta.position);
in attrs.passthru.updateScript or [ update-python-libraries filename ];
}
// lib.optionalAttrs (dependencies != []) {
inherit dependencies;
}
// lib.optionalAttrs (optional-dependencies != {}) {
inherit optional-dependencies;
}
// lib.optionalAttrs (build-system != []) {
inherit build-system;
};
# Keep extra attributes from `attrs`, e.g., `patchPhase', etc. # Keep extra attributes from `attrs`, e.g., `patchPhase', etc.
self = toPythonModule (stdenv.mkDerivation ((builtins.removeAttrs attrs [ self = toPythonModule (stdenv.mkDerivation ((builtins.removeAttrs attrs [
"disabled" "checkPhase" "checkInputs" "nativeCheckInputs" "doCheck" "doInstallCheck" "dontWrapPythonPrograms" "catchConflicts" "pyproject" "format" "disabled" "checkPhase" "checkInputs" "nativeCheckInputs" "doCheck" "doInstallCheck" "dontWrapPythonPrograms" "catchConflicts" "pyproject" "format"
"disabledTestPaths" "outputs" "stdenv" "disabledTestPaths" "outputs" "stdenv"
"dependencies" "optional-dependencies" "build-system"
]) // { ]) // {
name = namePrefix + name_; name = namePrefix + name_;
@ -256,11 +280,11 @@ let
pythonNamespacesHook pythonNamespacesHook
] ++ lib.optionals withDistOutput [ ] ++ lib.optionals withDistOutput [
pythonOutputDistHook pythonOutputDistHook
] ++ nativeBuildInputs; ] ++ nativeBuildInputs ++ build-system;
buildInputs = validatePythonMatches "buildInputs" (buildInputs ++ pythonPath); buildInputs = validatePythonMatches "buildInputs" (buildInputs ++ pythonPath);
propagatedBuildInputs = validatePythonMatches "propagatedBuildInputs" (propagatedBuildInputs ++ [ propagatedBuildInputs = validatePythonMatches "propagatedBuildInputs" (propagatedBuildInputs ++ dependencies ++ [
# we propagate python even for packages transformed with 'toPythonApplication' # we propagate python even for packages transformed with 'toPythonApplication'
# this pollutes the PATH but avoids rebuilds # this pollutes the PATH but avoids rebuilds
# see https://github.com/NixOS/nixpkgs/issues/170887 for more context # see https://github.com/NixOS/nixpkgs/issues/170887 for more context
@ -292,6 +316,8 @@ let
outputs = outputs ++ lib.optional withDistOutput "dist"; outputs = outputs ++ lib.optional withDistOutput "dist";
inherit passthru;
meta = { meta = {
# default to python's platforms # default to python's platforms
platforms = python.meta.platforms; platforms = python.meta.platforms;
@ -305,13 +331,7 @@ let
disabledTestPaths = lib.escapeShellArgs disabledTestPaths; disabledTestPaths = lib.escapeShellArgs disabledTestPaths;
})); }));
passthru.updateScript = let in lib.extendDerivation
filename = builtins.head (lib.splitString ":" self.meta.position); (disabled -> throw "${name} not supported for interpreter ${python.executable}")
in attrs.passthru.updateScript or [ update-python-libraries filename ]; passthru
in self
if disabled then
throw "${name} not supported for interpreter ${python.executable}"
else
self.overrideAttrs (attrs: {
passthru = lib.recursiveUpdate passthru attrs.passthru;
})

View file

@ -20,8 +20,8 @@ buildPythonPackage rec {
export HOME=$TMPDIR export HOME=$TMPDIR
''; '';
nativeBuildInputs = [ poetry-core ]; build-system = [ poetry-core ];
propagatedBuildInputs = [ python-dateutil pytzdata ] dependencies = [ python-dateutil pytzdata ]
++ lib.optional (pythonOlder "3.5") typing ++ lib.optional (pythonOlder "3.5") typing
++ lib.optionals (pythonOlder "3.8") [ importlib-metadata ]; ++ lib.optionals (pythonOlder "3.8") [ importlib-metadata ];

View file

@ -30,7 +30,7 @@ buildPythonPackage rec {
hash = "sha256-lCxadY+Y15Dq7Ropy27vx/+w0c968Fw9J5Flbb1q0eE="; hash = "sha256-lCxadY+Y15Dq7Ropy27vx/+w0c968Fw9J5Flbb1q0eE=";
}; };
propagatedBuildInputs = [ dependencies = [
brotlicffi brotlicffi
certifi certifi
charset-normalizer charset-normalizer
@ -38,7 +38,7 @@ buildPythonPackage rec {
urllib3 urllib3
]; ];
passthru.optional-dependencies = { optional-dependencies = {
security = []; security = [];
socks = [ socks = [
pysocks pysocks
@ -53,7 +53,7 @@ buildPythonPackage rec {
pytest-xdist pytest-xdist
pytestCheckHook pytestCheckHook
] ]
++ passthru.optional-dependencies.socks; ++ optional-dependencies.socks;
disabledTests = [ disabledTests = [
# Disable tests that require network access and use httpbin # Disable tests that require network access and use httpbin