1 import contextlib
2 import os
3 import os.path
4 import sys
5 import tempfile
6 import test.support
7 import unittest
8 import unittest.mock
9
10 import ensurepip
11 import ensurepip._uninstall
12
13
14 class ESC[4;38;5;81mTestPackages(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
15 def touch(self, directory, filename):
16 fullname = os.path.join(directory, filename)
17 open(fullname, "wb").close()
18
19 def test_version(self):
20 # Test version()
21 with tempfile.TemporaryDirectory() as tmpdir:
22 self.touch(tmpdir, "pip-1.2.3b1-py2.py3-none-any.whl")
23 with (unittest.mock.patch.object(ensurepip, '_PACKAGES', None),
24 unittest.mock.patch.object(ensurepip, '_WHEEL_PKG_DIR', tmpdir)):
25 self.assertEqual(ensurepip.version(), '1.2.3b1')
26
27 def test_get_packages_no_dir(self):
28 # Test _get_packages() without a wheel package directory
29 with (unittest.mock.patch.object(ensurepip, '_PACKAGES', None),
30 unittest.mock.patch.object(ensurepip, '_WHEEL_PKG_DIR', None)):
31 packages = ensurepip._get_packages()
32
33 # when bundled wheel packages are used, we get _PIP_VERSION
34 self.assertEqual(ensurepip._PIP_VERSION, ensurepip.version())
35
36 # use bundled wheel packages
37 self.assertIsNotNone(packages['pip'].wheel_name)
38
39 def test_get_packages_with_dir(self):
40 # Test _get_packages() with a wheel package directory
41 pip_filename = "pip-20.2.2-py2.py3-none-any.whl"
42
43 with tempfile.TemporaryDirectory() as tmpdir:
44 self.touch(tmpdir, pip_filename)
45 # not used, make sure that it's ignored
46 self.touch(tmpdir, "wheel-0.34.2-py2.py3-none-any.whl")
47
48 with (unittest.mock.patch.object(ensurepip, '_PACKAGES', None),
49 unittest.mock.patch.object(ensurepip, '_WHEEL_PKG_DIR', tmpdir)):
50 packages = ensurepip._get_packages()
51
52 self.assertEqual(packages['pip'].version, '20.2.2')
53 self.assertEqual(packages['pip'].wheel_path,
54 os.path.join(tmpdir, pip_filename))
55
56 # wheel package is ignored
57 self.assertEqual(sorted(packages), ['pip'])
58
59
60 class ESC[4;38;5;81mEnsurepipMixin:
61
62 def setUp(self):
63 run_pip_patch = unittest.mock.patch("ensurepip._run_pip")
64 self.run_pip = run_pip_patch.start()
65 self.run_pip.return_value = 0
66 self.addCleanup(run_pip_patch.stop)
67
68 # Avoid side effects on the actual os module
69 real_devnull = os.devnull
70 os_patch = unittest.mock.patch("ensurepip.os")
71 patched_os = os_patch.start()
72 # But expose os.listdir() used by _find_packages()
73 patched_os.listdir = os.listdir
74 self.addCleanup(os_patch.stop)
75 patched_os.devnull = real_devnull
76 patched_os.path = os.path
77 self.os_environ = patched_os.environ = os.environ.copy()
78
79
80 class ESC[4;38;5;81mTestBootstrap(ESC[4;38;5;149mEnsurepipMixin, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
81
82 def test_basic_bootstrapping(self):
83 ensurepip.bootstrap()
84
85 self.run_pip.assert_called_once_with(
86 [
87 "install", "--no-cache-dir", "--no-index", "--find-links",
88 unittest.mock.ANY, "pip",
89 ],
90 unittest.mock.ANY,
91 )
92
93 additional_paths = self.run_pip.call_args[0][1]
94 self.assertEqual(len(additional_paths), 1)
95
96 def test_bootstrapping_with_root(self):
97 ensurepip.bootstrap(root="/foo/bar/")
98
99 self.run_pip.assert_called_once_with(
100 [
101 "install", "--no-cache-dir", "--no-index", "--find-links",
102 unittest.mock.ANY, "--root", "/foo/bar/",
103 "pip",
104 ],
105 unittest.mock.ANY,
106 )
107
108 def test_bootstrapping_with_user(self):
109 ensurepip.bootstrap(user=True)
110
111 self.run_pip.assert_called_once_with(
112 [
113 "install", "--no-cache-dir", "--no-index", "--find-links",
114 unittest.mock.ANY, "--user", "pip",
115 ],
116 unittest.mock.ANY,
117 )
118
119 def test_bootstrapping_with_upgrade(self):
120 ensurepip.bootstrap(upgrade=True)
121
122 self.run_pip.assert_called_once_with(
123 [
124 "install", "--no-cache-dir", "--no-index", "--find-links",
125 unittest.mock.ANY, "--upgrade", "pip",
126 ],
127 unittest.mock.ANY,
128 )
129
130 def test_bootstrapping_with_verbosity_1(self):
131 ensurepip.bootstrap(verbosity=1)
132
133 self.run_pip.assert_called_once_with(
134 [
135 "install", "--no-cache-dir", "--no-index", "--find-links",
136 unittest.mock.ANY, "-v", "pip",
137 ],
138 unittest.mock.ANY,
139 )
140
141 def test_bootstrapping_with_verbosity_2(self):
142 ensurepip.bootstrap(verbosity=2)
143
144 self.run_pip.assert_called_once_with(
145 [
146 "install", "--no-cache-dir", "--no-index", "--find-links",
147 unittest.mock.ANY, "-vv", "pip",
148 ],
149 unittest.mock.ANY,
150 )
151
152 def test_bootstrapping_with_verbosity_3(self):
153 ensurepip.bootstrap(verbosity=3)
154
155 self.run_pip.assert_called_once_with(
156 [
157 "install", "--no-cache-dir", "--no-index", "--find-links",
158 unittest.mock.ANY, "-vvv", "pip",
159 ],
160 unittest.mock.ANY,
161 )
162
163 def test_bootstrapping_with_regular_install(self):
164 ensurepip.bootstrap()
165 self.assertEqual(self.os_environ["ENSUREPIP_OPTIONS"], "install")
166
167 def test_bootstrapping_with_alt_install(self):
168 ensurepip.bootstrap(altinstall=True)
169 self.assertEqual(self.os_environ["ENSUREPIP_OPTIONS"], "altinstall")
170
171 def test_bootstrapping_with_default_pip(self):
172 ensurepip.bootstrap(default_pip=True)
173 self.assertNotIn("ENSUREPIP_OPTIONS", self.os_environ)
174
175 def test_altinstall_default_pip_conflict(self):
176 with self.assertRaises(ValueError):
177 ensurepip.bootstrap(altinstall=True, default_pip=True)
178 self.assertFalse(self.run_pip.called)
179
180 def test_pip_environment_variables_removed(self):
181 # ensurepip deliberately ignores all pip environment variables
182 # See http://bugs.python.org/issue19734 for details
183 self.os_environ["PIP_THIS_SHOULD_GO_AWAY"] = "test fodder"
184 ensurepip.bootstrap()
185 self.assertNotIn("PIP_THIS_SHOULD_GO_AWAY", self.os_environ)
186
187 def test_pip_config_file_disabled(self):
188 # ensurepip deliberately ignores the pip config file
189 # See http://bugs.python.org/issue20053 for details
190 ensurepip.bootstrap()
191 self.assertEqual(self.os_environ["PIP_CONFIG_FILE"], os.devnull)
192
193 @contextlib.contextmanager
194 def fake_pip(version=ensurepip.version()):
195 if version is None:
196 pip = None
197 else:
198 class ESC[4;38;5;81mFakePip():
199 __version__ = version
200 pip = FakePip()
201 sentinel = object()
202 orig_pip = sys.modules.get("pip", sentinel)
203 sys.modules["pip"] = pip
204 try:
205 yield pip
206 finally:
207 if orig_pip is sentinel:
208 del sys.modules["pip"]
209 else:
210 sys.modules["pip"] = orig_pip
211
212 class ESC[4;38;5;81mTestUninstall(ESC[4;38;5;149mEnsurepipMixin, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
213
214 def test_uninstall_skipped_when_not_installed(self):
215 with fake_pip(None):
216 ensurepip._uninstall_helper()
217 self.assertFalse(self.run_pip.called)
218
219 def test_uninstall_skipped_with_warning_for_wrong_version(self):
220 with fake_pip("not a valid version"):
221 with test.support.captured_stderr() as stderr:
222 ensurepip._uninstall_helper()
223 warning = stderr.getvalue().strip()
224 self.assertIn("only uninstall a matching version", warning)
225 self.assertFalse(self.run_pip.called)
226
227
228 def test_uninstall(self):
229 with fake_pip():
230 ensurepip._uninstall_helper()
231
232 self.run_pip.assert_called_once_with(
233 [
234 "uninstall", "-y", "--disable-pip-version-check", "pip",
235 ]
236 )
237
238 def test_uninstall_with_verbosity_1(self):
239 with fake_pip():
240 ensurepip._uninstall_helper(verbosity=1)
241
242 self.run_pip.assert_called_once_with(
243 [
244 "uninstall", "-y", "--disable-pip-version-check", "-v", "pip",
245 ]
246 )
247
248 def test_uninstall_with_verbosity_2(self):
249 with fake_pip():
250 ensurepip._uninstall_helper(verbosity=2)
251
252 self.run_pip.assert_called_once_with(
253 [
254 "uninstall", "-y", "--disable-pip-version-check", "-vv", "pip",
255 ]
256 )
257
258 def test_uninstall_with_verbosity_3(self):
259 with fake_pip():
260 ensurepip._uninstall_helper(verbosity=3)
261
262 self.run_pip.assert_called_once_with(
263 [
264 "uninstall", "-y", "--disable-pip-version-check", "-vvv",
265 "pip"
266 ]
267 )
268
269 def test_pip_environment_variables_removed(self):
270 # ensurepip deliberately ignores all pip environment variables
271 # See http://bugs.python.org/issue19734 for details
272 self.os_environ["PIP_THIS_SHOULD_GO_AWAY"] = "test fodder"
273 with fake_pip():
274 ensurepip._uninstall_helper()
275 self.assertNotIn("PIP_THIS_SHOULD_GO_AWAY", self.os_environ)
276
277 def test_pip_config_file_disabled(self):
278 # ensurepip deliberately ignores the pip config file
279 # See http://bugs.python.org/issue20053 for details
280 with fake_pip():
281 ensurepip._uninstall_helper()
282 self.assertEqual(self.os_environ["PIP_CONFIG_FILE"], os.devnull)
283
284
285 # Basic testing of the main functions and their argument parsing
286
287 EXPECTED_VERSION_OUTPUT = "pip " + ensurepip.version()
288
289 class ESC[4;38;5;81mTestBootstrappingMainFunction(ESC[4;38;5;149mEnsurepipMixin, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
290
291 def test_bootstrap_version(self):
292 with test.support.captured_stdout() as stdout:
293 with self.assertRaises(SystemExit):
294 ensurepip._main(["--version"])
295 result = stdout.getvalue().strip()
296 self.assertEqual(result, EXPECTED_VERSION_OUTPUT)
297 self.assertFalse(self.run_pip.called)
298
299 def test_basic_bootstrapping(self):
300 exit_code = ensurepip._main([])
301
302 self.run_pip.assert_called_once_with(
303 [
304 "install", "--no-cache-dir", "--no-index", "--find-links",
305 unittest.mock.ANY, "pip",
306 ],
307 unittest.mock.ANY,
308 )
309
310 additional_paths = self.run_pip.call_args[0][1]
311 self.assertEqual(len(additional_paths), 1)
312 self.assertEqual(exit_code, 0)
313
314 def test_bootstrapping_error_code(self):
315 self.run_pip.return_value = 2
316 exit_code = ensurepip._main([])
317 self.assertEqual(exit_code, 2)
318
319
320 class ESC[4;38;5;81mTestUninstallationMainFunction(ESC[4;38;5;149mEnsurepipMixin, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
321
322 def test_uninstall_version(self):
323 with test.support.captured_stdout() as stdout:
324 with self.assertRaises(SystemExit):
325 ensurepip._uninstall._main(["--version"])
326 result = stdout.getvalue().strip()
327 self.assertEqual(result, EXPECTED_VERSION_OUTPUT)
328 self.assertFalse(self.run_pip.called)
329
330 def test_basic_uninstall(self):
331 with fake_pip():
332 exit_code = ensurepip._uninstall._main([])
333
334 self.run_pip.assert_called_once_with(
335 [
336 "uninstall", "-y", "--disable-pip-version-check", "pip",
337 ]
338 )
339
340 self.assertEqual(exit_code, 0)
341
342 def test_uninstall_error_code(self):
343 with fake_pip():
344 self.run_pip.return_value = 2
345 exit_code = ensurepip._uninstall._main([])
346 self.assertEqual(exit_code, 2)
347
348
349 if __name__ == "__main__":
350 unittest.main()