Skip to content
69 changes: 36 additions & 33 deletions doc/languages-frameworks/python.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ buildPythonPackage rec {
pluggy
];

nativeCheckInputs = [ hypothesis ];
nativeInstallCheckInputs = [ hypothesis ];

meta = {
changelog = "https://github.com/pytest-dev/pytest/releases/tag/${version}";
Expand All @@ -153,8 +153,11 @@ The `buildPythonPackage` mainly does four things:
environment variable and add dependent libraries to script's `sys.path`.
* In the [`installCheck`](#ssec-installCheck-phase) phase, `${python.interpreter} -m pytest` is run.

By default tests are run because [`doCheck = true`](#var-stdenv-doCheck). Test dependencies, like
e.g. the test runner, should be added to [`nativeCheckInputs`](#var-stdenv-nativeCheckInputs).
By default, tests are run because [`doInstallCheck = true`](#var-stdenv-doInstallCheck). Test dependencies, such as
the test runner, should be added to [`nativeInstallCheckInputs`](#var-stdenv-nativeInstallCheckInputs).

Python packages and applications constructed with `buildPythonPackage` or `buildPythonApplication` don't have [`checkPhase`](#ssec-check-phase).
While `doCheck`, `checkPhase`, `checkInputs`, and `nativeCheckInputs` are aliases to the `installCheck`-related attributes for historical reason, we encourage using the `installCheck`-related attributes.

By default `meta.platforms` is set to the same value
as the interpreter unless overridden otherwise.
Expand Down Expand Up @@ -217,8 +220,8 @@ because their behaviour is different:
* `buildInputs ? []`: Build and/or run-time dependencies that need to be
compiled for the host machine. Typically non-Python libraries which are being
linked.
* `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
* `nativeInstallCheckInputs ? []`: Dependencies needed for running the [`installCheckPhase`](#ssec-installCheck-phase). These
are added to [`nativeBuildInputs`](#var-stdenv-nativeBuildInputs) when [`doInstallCheck = true`](#var-stdenv-doInstallCheck). Items listed in
`tests_require` go here.
* `dependencies ? []`: Aside from propagating dependencies,
`buildPythonPackage` also injects code into and wraps executables with the
Expand Down Expand Up @@ -928,7 +931,7 @@ buildPythonPackage rec {
build-system = [ setuptools ];

# has no tests
doCheck = false;
doInstallCheck = false;

pythonImportsCheck = [
"toolz.itertoolz"
Expand All @@ -950,7 +953,7 @@ it accepts a set. In this case the set is a recursive set, `rec`. One of the
arguments is the name of the package, which consists of a basename (generally
following the name on PyPI) and a version. Another argument, `src` specifies the
source, which in this case is fetched from PyPI using the helper function
`fetchPypi`. The argument `doCheck` is used to set whether tests should be run
`fetchPypi`. The argument `doInstallCheck` is used to set whether tests should be run
when building the package. Since there are no tests, we rely on [`pythonImportsCheck`](#using-pythonimportscheck)
to test whether the package can be imported. Furthermore, we specify some meta
information. The output of the function is a derivation.
Expand Down Expand Up @@ -985,7 +988,7 @@ with import <nixpkgs> { };
build-system = [ python313.pkgs.setuptools ];

# has no tests
doCheck = false;
doInstallCheck = false;

meta = {
homepage = "https://github.com/pytoolz/toolz/";
Expand Down Expand Up @@ -1024,7 +1027,7 @@ Our example, `toolz`, does not have any dependencies on other Python packages or
- `dependencies` - For Python runtime dependencies.
- `build-system` - For Python build-time requirements.
- [`buildInputs`](#var-stdenv-buildInputs) - For non-Python build-time requirements.
- [`nativeCheckInputs`](#var-stdenv-nativeCheckInputs) - For test dependencies
- [`nativeInstallCheckInputs`](#var-stdenv-nativeInstallCheckInputs) - For test dependencies

Dependencies can belong to multiple arguments, for example if something is both a build time requirement & a runtime dependency.

Expand Down Expand Up @@ -1067,7 +1070,7 @@ buildPythonPackage rec {
python-dateutil
];

nativeCheckInputs = [ pytestCheckHook ];
nativeInstallCheckInputs = [ pytestCheckHook ];

meta = {
changelog = "https://github.com/blaze/datashape/releases/tag/${version}";
Expand All @@ -1079,8 +1082,8 @@ buildPythonPackage rec {
```

We can see several runtime dependencies, `numpy`, `multipledispatch`, and
`python-dateutil`. Furthermore, we have [`nativeCheckInputs`](#var-stdenv-nativeCheckInputs) with `pytestCheckHook`.
`pytestCheckHook` is a test runner hook and is only used during the [`checkPhase`](#ssec-check-phase) and is
`python-dateutil`. Furthermore, we have [`nativeInstallCheckInputs`](#var-stdenv-nativeInstallCheckInputs) with `pytestCheckHook`.
`pytestCheckHook` is a test runner hook and is only used during the [`installCheckPhase`](#ssec-installCheck-phase) and is
therefore not added to `dependencies`.

In the previous case we had only dependencies on other Python packages to consider.
Expand Down Expand Up @@ -1189,7 +1192,7 @@ buildPythonPackage rec {
'';

# Tests cannot import pyfftw. pyfftw works fine though.
doCheck = false;
doInstallCheck = false;

pythonImportsCheck = [ "pyfftw" ];

Expand All @@ -1205,17 +1208,17 @@ buildPythonPackage rec {
}
```

Note also the line [`doCheck = false;`](#var-stdenv-doCheck), we explicitly disabled running the test-suite.
Note also the line [`doInstallCheck = false;`](#var-stdenv-doInstallCheck), we explicitly disabled running the test-suite.

#### Testing Python Packages {#testing-python-packages}

It is highly encouraged to have testing as part of the package build. This
helps to avoid situations where the package was able to build and install,
but is not usable at runtime.
Your package should provide its own [`checkPhase`](#ssec-check-phase).
Your package should provide its own [`installCheckPhase`](#ssec-installCheck-phase).

::: {.note}
The [`checkPhase`](#ssec-check-phase) for python maps to the `installCheckPhase` on a
The [`installCheckPhase`](#ssec-installCheck-phase) for python maps to the `installCheckPhase` on a
normal derivation. This is due to many python packages not behaving well
to the pre-installed version of the package. Version info, and natively
compiled extensions generally only exist in the install directory, and
Expand Down Expand Up @@ -1243,13 +1246,13 @@ test run would be:

```nix
{
nativeCheckInputs = [ pytest ];
checkPhase = ''
runHook preCheck
nativeInstallCheckInputs = [ pytest ];
installCheckPhase = ''
runHook preInstallCheck

pytest

runHook postCheck
runHook postInstallCheck
'';
}
```
Expand All @@ -1269,7 +1272,7 @@ We highly recommend `pytestCheckHook` for an easier and more structural setup.
#### Using pytestCheckHook {#using-pytestcheckhook}

`pytestCheckHook` is a convenient hook which will set up (or configure)
a [`checkPhase`](#ssec-check-phase) to run `pytest`. This is also beneficial
a [`installCheckPhase`](#ssec-installCheck-phase) to run `pytest`. This is also beneficial
when a package may need many items disabled to run the test suite.
Most packages use `pytest` or `unittest`, which is compatible with `pytest`,
so you will most likely use `pytestCheckHook`.
Expand Down Expand Up @@ -1307,7 +1310,7 @@ The following example demonstrates usage of various `pytestCheckHook` attributes

```nix
{
nativeCheckInputs = [ pytestCheckHook ];
nativeInstallCheckInputs = [ pytestCheckHook ];

# Allow running the following test paths and test objects.
enabledTestPaths = [
Expand Down Expand Up @@ -1413,14 +1416,14 @@ roughly translates to:

```nix
{
postCheck = ''
postInstallCheck = ''
PYTHONPATH=$out/${python.sitePackages}:$PYTHONPATH
python -c "import requests; import urllib"
'';
}
```

However, this is done in its own phase, and not dependent on whether [`doCheck = true;`](#var-stdenv-doCheck).
However, this is done in its own phase, and not dependent on whether [`doInstallCheck = true;`](#var-stdenv-doInstallCheck).

This can also be useful in verifying that the package doesn't assume commonly
present packages (e.g. `setuptools`).
Expand Down Expand Up @@ -1501,11 +1504,11 @@ automatically add `pythonRelaxDepsHook` if either `pythonRelaxDeps` or

#### Using unittestCheckHook {#using-unittestcheckhook}

`unittestCheckHook` is a hook which will set up (or configure) a [`checkPhase`](#ssec-check-phase) to run `python -m unittest discover`:
`unittestCheckHook` is a hook which behaves like setting up (or configuring) a [`installCheckPhase`](#ssec-installCheck-phase) to run `python -m unittest discover`:

```nix
{
nativeCheckInputs = [ unittestCheckHook ];
nativeInstallCheckInputs = [ unittestCheckHook ];

unittestFlags = [
"-s"
Expand Down Expand Up @@ -1995,7 +1998,7 @@ In a `setup.py` or `setup.cfg` it is common to declare dependencies:

* `setup_requires` corresponds to `build-system`
* `install_requires` corresponds to `dependencies`
* `tests_require` corresponds to [`nativeCheckInputs`](#var-stdenv-nativeCheckInputs)
* `tests_require` corresponds to [`nativeInstallCheckInputs`](#var-stdenv-nativeInstallCheckInputs)

### How to enable interpreter optimizations? {#optimizations}

Expand Down Expand Up @@ -2082,18 +2085,18 @@ Both are also exported in `nix-shell`.
It is recommended to test packages as part of the build process.
Source distributions (`sdist`) often include test files, but not always.

The best practice today is to pass a test hook (e.g. pytestCheckHook, unittestCheckHook) into nativeCheckInputs.
This will reconfigure the checkPhase to make use of that particular test framework.
Occasionally packages don't make use of a common test framework, which may then require a custom checkPhase.
The best practice today is to pass a test hook (e.g. `pytestCheckHook`, `unittestCheckHook`) into [`nativeInstallCheckInputs`](#var-stdenv-nativeInstallCheckInputs).
This will reconfigure the [`installCheckPhase`](#ssec-installCheck-phase) to make use of that particular test framework.
Occasionally packages don't make use of a common test framework, which may then require a custom [`installCheckPhase`](#ssec-installCheck-phase).

#### Common issues {#common-issues}

* Tests that attempt to access `$HOME` can be fixed by using `writableTmpDirAsHomeHook` in
`nativeCheckInputs`, which sets up a writable temporary directory as the home directory. Alternatively,
you can achieve the same effect manually (e.g. in `preCheck`) with: `export HOME=$(mktemp -d)`.
`nativeInstallCheckInputs`, which sets up a writable temporary directory as the home directory. Alternatively,
you can achieve the same effect manually (e.g. in `preInstallCheck`) with: `export HOME=$(mktemp -d)`.
* Compiling with Cython causes tests to fail with a `ModuleNotLoadedError`.
This can be fixed with two changes in the derivation: 1) replacing `pytest` with
`pytestCheckHook` and 2) adding a `preCheck` containing `cd $out` to run
`pytestCheckHook` and 2) adding a `preInstallCheck` containing `cd $out` to run
tests within the built output.

## Contributing {#contributing}
Expand Down
4 changes: 4 additions & 0 deletions nixos/doc/manual/release-notes/rl-2411.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,10 @@
This change does not affect the override interface of most Python packages, as [`<pkg>.override`](https://nixos.org/manual/nixpkgs/unstable/#sec-pkg-override) provided by `callPackage` shadows such a locally-defined `override` attribute.
The `<pkg>.overrideDerivation` attribute of Python packages called with `callPackage` will also remain available after this change.

- The `buildPythonPackage` and `buildPythonApplication` functions now consume the `installCheckPhase` attributes (`doInstallCheck`, `installCheckPhase`, `installCheckInputs`, `nativeInstallCheckInputs`) directly.
`checkPhase` attributes (`doCheck`, `checkPhase`, `checkInputs`, `nativeCheckInputs`) have been aliases to their `installCheckPhase` counterparts since at least 2016 and their usage is now deprecated.


- All Cinnamon and XApp packages have been moved to top-level (i.e., `cinnamon.nemo` is now `nemo`).

- All GNOME packages have been moved to top-level (i.e., `gnome.nautilus` is now `nautilus`).
Expand Down
4 changes: 1 addition & 3 deletions pkgs/by-name/er/erosmb/package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,9 @@ python3.pkgs.buildPythonApplication rec {
six
];

# Project has no tests
doCheck = false;

doInstallCheck = true;

# Project has no tests
installCheckPhase = ''
runHook preInstallCheck
$out/bin/erosmb --help
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function _pytestIncludeExcludeExpr() {

function pytestCheckPhase() {
echo "Executing pytestCheckPhase"
runHook preCheck
runHook preInstallCheck

# Compose arguments
local -a flagsArray=(-m pytest)
Expand Down Expand Up @@ -90,11 +90,11 @@ EOF
echoCmd 'pytest flags' "${flagsArray[@]}"
@pythonCheckInterpreter@ "${flagsArray[@]}"

runHook postCheck
runHook postInstallCheck
echo "Finished executing pytestCheckPhase"
}

if [ -z "${dontUsePytestCheck-}" ] && [ -z "${installCheckPhase-}" ]; then
echo "Using pytestCheckPhase"
appendToVar preDistPhases pytestCheckPhase
installCheckPhase=pytestCheckPhase
fi
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ echo "Sourcing unittest-check-hook"

unittestCheckPhase() {
echo "Executing unittestCheckPhase"
runHook preCheck
runHook preInstallCheck

local -a flagsArray=()

Expand All @@ -16,11 +16,11 @@ unittestCheckPhase() {
echoCmd 'unittest flags' "${flagsArray[@]}"
@pythonCheckInterpreter@ -m unittest discover "${flagsArray[@]}"

runHook postCheck
runHook postInstallCheck
echo "Finished executing unittestCheckPhase"
}

if [[ -z "${dontUseUnittestCheck-}" ]] && [[ -z "${installCheckPhase-}" ]]; then
echo "Using unittestCheckPhase"
appendToVar preDistPhases unittestCheckPhase
installCheckPhase=unittestCheckPhase
fi
34 changes: 23 additions & 11 deletions pkgs/development/interpreters/python/mk-python-derivation.nix
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,11 @@ let
cleanAttrs = flip removeAttrs [
"disabled"
"checkPhase"
"preCheck"
"postCheck"
"checkInputs"
"nativeCheckInputs"
"doCheck"
"doInstallCheck"
"pyproject"
"format"
"stdenv"
Expand All @@ -121,10 +122,10 @@ in
# Run-time dependencies for the package
buildInputs ? [ ],

# Dependencies needed for running the checkPhase.
# These are added to buildInputs when doCheck = true.
checkInputs ? [ ],
nativeCheckInputs ? [ ],
# Dependencies needed for running the installCheckPhase.
# These are added to buildInputs when doInstallCheck = true.
installCheckInputs ? [ ],
nativeInstallCheckInputs ? [ ],

# propagate build dependencies so in case we have A -> B -> C,
# C can import package A propagated by B
Expand Down Expand Up @@ -190,7 +191,7 @@ in

meta ? { },

doCheck ? true,
doInstallCheck ? attrs.doCheck or true,

# Allow passing in a custom stdenv to buildPython*
stdenv ? python.stdenv,
Expand Down Expand Up @@ -359,7 +360,7 @@ let
)
]
++ optionals (stdenv.buildPlatform == stdenv.hostPlatform) [
# This is a test, however, it should be ran independent of the checkPhase and checkInputs
# This is a test, however, it should be ran independent of the installCheckPhase and installCheckInputs
pythonImportsCheckHook
]
++ optionals (python.pythonAtLeast "3.3") [
Expand Down Expand Up @@ -391,9 +392,9 @@ let

# Python packages don't have a checkPhase, only an installCheckPhase
doCheck = false;
doInstallCheck = attrs.doCheck or true;
nativeInstallCheckInputs = nativeCheckInputs ++ attrs.nativeInstallCheckInputs or [ ];
installCheckInputs = checkInputs ++ attrs.installCheckInputs or [ ];
inherit doInstallCheck;
nativeInstallCheckInputs = attrs.nativeCheckInputs or [ ] ++ nativeInstallCheckInputs;
installCheckInputs = attrs.checkInputs or [ ] ++ installCheckInputs;

inherit dontWrapPythonPrograms;

Expand Down Expand Up @@ -434,7 +435,18 @@ let
// optionalAttrs (attrs ? checkPhase) {
# If given use the specified checkPhase, otherwise use the setup hook.
# Longer-term we should get rid of `checkPhase` and use `installCheckPhase`.
installCheckPhase = attrs.checkPhase;
installCheckPhase =
attrs.installCheckPhase or (lib.replaceStrings
[ "runHook preCheck\n" "runHook postCheck\n" ]
[ "runHook preInstallCheck\n" "runHook postInstallCheck\n" ]
attrs.checkPhase
);
}
// optionalAttrs (attrs ? preCheck) {
preInstallCheck = attrs.preInstallCheck or attrs.preCheck;
}
// optionalAttrs (attrs ? postCheck) {
postInstallCheck = attrs.postInstallCheck or attrs.postCheck;
}
//
lib.mapAttrs
Expand Down
Loading