1# Tests for the Python interpreters, package sets and environments.
2#
3# Each Python interpreter has a `passthru.tests` which is the attribute set
4# returned by this function. For example, for Python 3 the tests are run with
5#
6# $ nix-build -A python3.tests
7#
8{ stdenv
9, python
10, runCommand
11, substituteAll
12, lib
13, callPackage
14, pkgs
15}:
16
17let
18 # Test whether the interpreter behaves in the different types of environments
19 # we aim to support.
20 environmentTests = let
21 envs = let
22 inherit python;
23 pythonEnv = python.withPackages(ps: with ps; [ ]);
24 pythonVirtualEnv = if python.isPy3k
25 then
26 python.withPackages(ps: with ps; [ virtualenv ])
27 else
28 python.buildEnv.override {
29 extraLibs = with python.pkgs; [ virtualenv ];
30 # Collisions because of namespaces __init__.py
31 ignoreCollisions = true;
32 };
33 in {
34 # Plain Python interpreter
35 plain = rec {
36 env = python;
37 interpreter = env.interpreter;
38 is_venv = "False";
39 is_nixenv = "False";
40 is_virtualenv = "False";
41 };
42 } // lib.optionalAttrs (!python.isPyPy) {
43 # Use virtualenv from a Nix env.
44 nixenv-virtualenv = rec {
45 env = runCommand "${python.name}-virtualenv" {} ''
46 ${pythonVirtualEnv.interpreter} -m virtualenv $out
47 '';
48 interpreter = "${env}/bin/${python.executable}";
49 is_venv = "False";
50 is_nixenv = "True";
51 is_virtualenv = "True";
52 };
53 } // lib.optionalAttrs (python.implementation != "graal") {
54 # Python Nix environment (python.buildEnv)
55 nixenv = rec {
56 env = pythonEnv;
57 interpreter = env.interpreter;
58 is_venv = "False";
59 is_nixenv = "True";
60 is_virtualenv = "False";
61 };
62 } // lib.optionalAttrs (python.isPy3k && (!python.isPyPy)) rec {
63 # Venv built using plain Python
64 # Python 2 does not support venv
65 # TODO: PyPy executable name is incorrect, it should be pypy-c or pypy-3c instead of pypy and pypy3.
66 plain-venv = rec {
67 env = runCommand "${python.name}-venv" {} ''
68 ${python.interpreter} -m venv $out
69 '';
70 interpreter = "${env}/bin/${python.executable}";
71 is_venv = "True";
72 is_nixenv = "False";
73 is_virtualenv = "False";
74 };
75
76 } // lib.optionalAttrs (python.pythonAtLeast "3.8") {
77 # Venv built using Python Nix environment (python.buildEnv)
78 # TODO: Cannot create venv from a nix env
79 # Error: Command '['/nix/store/ddc8nqx73pda86ibvhzdmvdsqmwnbjf7-python3-3.7.6-venv/bin/python3.7', '-Im', 'ensurepip', '--upgrade', '--default-pip']' returned non-zero exit status 1.
80 nixenv-venv = rec {
81 env = runCommand "${python.name}-venv" {} ''
82 ${pythonEnv.interpreter} -m venv $out
83 '';
84 interpreter = "${env}/bin/${pythonEnv.executable}";
85 is_venv = "True";
86 is_nixenv = "True";
87 is_virtualenv = "False";
88 };
89 };
90
91 testfun = name: attrs: runCommand "${python.name}-tests-${name}" ({
92 inherit (python) pythonVersion;
93 } // attrs) ''
94 cp -r ${./tests/test_environments} tests
95 chmod -R +w tests
96 substituteAllInPlace tests/test_python.py
97 ${attrs.interpreter} -m unittest discover --verbose tests #/test_python.py
98 mkdir $out
99 touch $out/success
100 '';
101
102 in lib.mapAttrs testfun envs;
103
104 # Integration tests involving the package set.
105 # All PyPy package builds are broken at the moment
106 integrationTests = lib.optionalAttrs (!python.isPyPy) (
107 lib.optionalAttrs (python.isPy3k && !stdenv.isDarwin) { # darwin has no split-debug
108 cpython-gdb = callPackage ./tests/test_cpython_gdb {
109 interpreter = python;
110 };
111 } // lib.optionalAttrs (python.pythonAtLeast "3.7") rec {
112 # Before the addition of NIX_PYTHONPREFIX mypy was broken with typed packages
113 nix-pythonprefix-mypy = callPackage ./tests/test_nix_pythonprefix {
114 interpreter = python;
115 };
116 }
117 );
118
119 # Tests to ensure overriding works as expected.
120 overrideTests = let
121 extension = self: super: {
122 foobar = super.numpy;
123 };
124 in {
125 test-packageOverrides = let
126 myPython = let
127 self = python.override {
128 packageOverrides = extension;
129 inherit self;
130 };
131 in self;
132 in assert myPython.pkgs.foobar == myPython.pkgs.numpy; myPython.withPackages(ps: with ps; [ foobar ]);
133 # overrideScope is broken currently
134 # test-overrideScope = let
135 # myPackages = python.pkgs.overrideScope extension;
136 # in assert myPackages.foobar == myPackages.numpy; myPackages.python.withPackages(ps: with ps; [ foobar ]);
137 } // lib.optionalAttrs (python ? pythonAttr) {
138 # Test applying overrides using pythonPackagesOverlays.
139 test-pythonPackagesExtensions = let
140 pkgs_ = pkgs.extend(final: prev: {
141 pythonPackagesExtensions = prev.pythonPackagesExtensions ++ [
142 (python-final: python-prev: {
143 foo = python-prev.setuptools;
144 })
145 ];
146 });
147 in pkgs_.${python.pythonAttr}.pkgs.foo;
148 };
149
150 condaTests = let
151 requests = callPackage ({
152 autoPatchelfHook,
153 fetchurl,
154 pythonCondaPackages,
155 }:
156 python.pkgs.buildPythonPackage {
157 pname = "requests";
158 version = "2.24.0";
159 format = "other";
160 src = fetchurl {
161 url = "https://repo.anaconda.com/pkgs/main/noarch/requests-2.24.0-py_0.tar.bz2";
162 sha256 = "02qzaf6gwsqbcs69pix1fnjxzgnngwzvrsy65h1d521g750mjvvp";
163 };
164 nativeBuildInputs = [ autoPatchelfHook ] ++ (with python.pkgs; [
165 condaUnpackHook condaInstallHook
166 ]);
167 buildInputs = [
168 pythonCondaPackages.condaPatchelfLibs
169 ];
170 propagatedBuildInputs = with python.pkgs; [
171 chardet idna urllib3 certifi
172 ];
173 }
174 ) {};
175 pythonWithRequests = requests.pythonModule.withPackages (ps: [ requests ]);
176 in
177 {
178 condaExamplePackage = runCommand "import-requests" {} ''
179 ${pythonWithRequests.interpreter} -c "import requests" > $out
180 '';
181 };
182
183in lib.optionalAttrs (stdenv.hostPlatform == stdenv.buildPlatform ) (environmentTests // integrationTests // overrideTests // condaTests)