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