• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import unittest
2import os
3import os.path
4import contextlib
5import sys
6import test._mock_backport as mock
7import test.test_support
8
9import ensurepip
10import ensurepip._uninstall
11
12
13class TestEnsurePipVersion(unittest.TestCase):
14
15    def test_returns_version(self):
16        self.assertEqual(ensurepip._PIP_VERSION, ensurepip.version())
17
18
19class EnsurepipMixin:
20
21    def setUp(self):
22        run_pip_patch = mock.patch("ensurepip._run_pip")
23        self.run_pip = run_pip_patch.start()
24        self.run_pip.return_value = 0
25        self.addCleanup(run_pip_patch.stop)
26
27        # Avoid side effects on the actual os module
28        real_devnull = os.devnull
29        os_patch = mock.patch("ensurepip.os")
30        patched_os = os_patch.start()
31        self.addCleanup(os_patch.stop)
32        patched_os.devnull = real_devnull
33        patched_os.path = os.path
34        self.os_environ = patched_os.environ = os.environ.copy()
35
36
37class TestBootstrap(EnsurepipMixin, unittest.TestCase):
38
39    def test_basic_bootstrapping(self):
40        ensurepip.bootstrap()
41
42        self.run_pip.assert_called_once_with(
43            [
44                "install", "--no-index", "--find-links",
45                mock.ANY, "setuptools", "pip",
46            ],
47            mock.ANY,
48        )
49
50        additional_paths = self.run_pip.call_args[0][1]
51        self.assertEqual(len(additional_paths), 2)
52
53    def test_bootstrapping_with_root(self):
54        ensurepip.bootstrap(root="/foo/bar/")
55
56        self.run_pip.assert_called_once_with(
57            [
58                "install", "--no-index", "--find-links",
59                mock.ANY, "--root", "/foo/bar/",
60                "setuptools", "pip",
61            ],
62            mock.ANY,
63        )
64
65    def test_bootstrapping_with_user(self):
66        ensurepip.bootstrap(user=True)
67
68        self.run_pip.assert_called_once_with(
69            [
70                "install", "--no-index", "--find-links",
71                mock.ANY, "--user", "setuptools", "pip",
72            ],
73            mock.ANY,
74        )
75
76    def test_bootstrapping_with_upgrade(self):
77        ensurepip.bootstrap(upgrade=True)
78
79        self.run_pip.assert_called_once_with(
80            [
81                "install", "--no-index", "--find-links",
82                mock.ANY, "--upgrade", "setuptools", "pip",
83            ],
84            mock.ANY,
85        )
86
87    def test_bootstrapping_with_verbosity_1(self):
88        ensurepip.bootstrap(verbosity=1)
89
90        self.run_pip.assert_called_once_with(
91            [
92                "install", "--no-index", "--find-links",
93                mock.ANY, "-v", "setuptools", "pip",
94            ],
95            mock.ANY,
96        )
97
98    def test_bootstrapping_with_verbosity_2(self):
99        ensurepip.bootstrap(verbosity=2)
100
101        self.run_pip.assert_called_once_with(
102            [
103                "install", "--no-index", "--find-links",
104                mock.ANY, "-vv", "setuptools", "pip",
105            ],
106            mock.ANY,
107        )
108
109    def test_bootstrapping_with_verbosity_3(self):
110        ensurepip.bootstrap(verbosity=3)
111
112        self.run_pip.assert_called_once_with(
113            [
114                "install", "--no-index", "--find-links",
115                mock.ANY, "-vvv", "setuptools", "pip",
116            ],
117            mock.ANY,
118        )
119
120    def test_bootstrapping_with_regular_install(self):
121        ensurepip.bootstrap()
122        self.assertEqual(self.os_environ["ENSUREPIP_OPTIONS"], "install")
123
124    def test_bootstrapping_with_alt_install(self):
125        ensurepip.bootstrap(altinstall=True)
126        self.assertEqual(self.os_environ["ENSUREPIP_OPTIONS"], "altinstall")
127
128    def test_bootstrapping_with_default_pip(self):
129        ensurepip.bootstrap(default_pip=True)
130        self.assertNotIn("ENSUREPIP_OPTIONS", self.os_environ)
131
132    def test_altinstall_default_pip_conflict(self):
133        with self.assertRaises(ValueError):
134            ensurepip.bootstrap(altinstall=True, default_pip=True)
135        self.assertFalse(self.run_pip.called)
136
137    def test_pip_environment_variables_removed(self):
138        # ensurepip deliberately ignores all pip environment variables
139        # See http://bugs.python.org/issue19734 for details
140        self.os_environ["PIP_THIS_SHOULD_GO_AWAY"] = "test fodder"
141        ensurepip.bootstrap()
142        self.assertNotIn("PIP_THIS_SHOULD_GO_AWAY", self.os_environ)
143
144    def test_pip_config_file_disabled(self):
145        # ensurepip deliberately ignores the pip config file
146        # See http://bugs.python.org/issue20053 for details
147        ensurepip.bootstrap()
148        self.assertEqual(self.os_environ["PIP_CONFIG_FILE"], os.devnull)
149
150
151@contextlib.contextmanager
152def fake_pip(version=ensurepip._PIP_VERSION):
153    if version is None:
154        pip = None
155    else:
156        class FakePip():
157            __version__ = version
158        pip = FakePip()
159    sentinel = object()
160    orig_pip = sys.modules.get("pip", sentinel)
161    sys.modules["pip"] = pip
162    try:
163        yield pip
164    finally:
165        if orig_pip is sentinel:
166            del sys.modules["pip"]
167        else:
168            sys.modules["pip"] = orig_pip
169
170
171class TestUninstall(EnsurepipMixin, unittest.TestCase):
172
173    def test_uninstall_skipped_when_not_installed(self):
174        with fake_pip(None):
175            ensurepip._uninstall_helper()
176        self.assertFalse(self.run_pip.called)
177
178    def test_uninstall_skipped_with_warning_for_wrong_version(self):
179        with fake_pip("not a valid version"):
180            with test.test_support.captured_stderr() as stderr:
181                ensurepip._uninstall_helper()
182        warning = stderr.getvalue().strip()
183        self.assertIn("only uninstall a matching version", warning)
184        self.assertFalse(self.run_pip.called)
185
186    def test_uninstall(self):
187        with fake_pip():
188            ensurepip._uninstall_helper()
189
190        self.run_pip.assert_called_once_with(
191            [
192                "uninstall", "-y", "--disable-pip-version-check", "pip",
193                "setuptools",
194            ]
195        )
196
197    def test_uninstall_with_verbosity_1(self):
198        with fake_pip():
199            ensurepip._uninstall_helper(verbosity=1)
200
201        self.run_pip.assert_called_once_with(
202            [
203                "uninstall", "-y", "--disable-pip-version-check", "-v", "pip",
204                "setuptools",
205            ]
206        )
207
208    def test_uninstall_with_verbosity_2(self):
209        with fake_pip():
210            ensurepip._uninstall_helper(verbosity=2)
211
212        self.run_pip.assert_called_once_with(
213            [
214                "uninstall", "-y", "--disable-pip-version-check", "-vv", "pip",
215                "setuptools",
216            ]
217        )
218
219    def test_uninstall_with_verbosity_3(self):
220        with fake_pip():
221            ensurepip._uninstall_helper(verbosity=3)
222
223        self.run_pip.assert_called_once_with(
224            [
225                "uninstall", "-y", "--disable-pip-version-check", "-vvv",
226                "pip", "setuptools",
227            ]
228        )
229
230    def test_pip_environment_variables_removed(self):
231        # ensurepip deliberately ignores all pip environment variables
232        # See http://bugs.python.org/issue19734 for details
233        self.os_environ["PIP_THIS_SHOULD_GO_AWAY"] = "test fodder"
234        with fake_pip():
235            ensurepip._uninstall_helper()
236        self.assertNotIn("PIP_THIS_SHOULD_GO_AWAY", self.os_environ)
237
238    def test_pip_config_file_disabled(self):
239        # ensurepip deliberately ignores the pip config file
240        # See http://bugs.python.org/issue20053 for details
241        with fake_pip():
242            ensurepip._uninstall_helper()
243        self.assertEqual(self.os_environ["PIP_CONFIG_FILE"], os.devnull)
244
245
246# Basic testing of the main functions and their argument parsing
247
248EXPECTED_VERSION_OUTPUT = "pip " + ensurepip._PIP_VERSION
249
250
251class TestBootstrappingMainFunction(EnsurepipMixin, unittest.TestCase):
252
253    def test_bootstrap_version(self):
254        with test.test_support.captured_stderr() as stderr:
255            with self.assertRaises(SystemExit):
256                ensurepip._main(["--version"])
257        result = stderr.getvalue().strip()
258        self.assertEqual(result, EXPECTED_VERSION_OUTPUT)
259        self.assertFalse(self.run_pip.called)
260
261    def test_basic_bootstrapping(self):
262        exit_code = ensurepip._main([])
263
264        self.run_pip.assert_called_once_with(
265            [
266                "install", "--no-index", "--find-links",
267                mock.ANY, "setuptools", "pip",
268            ],
269            mock.ANY,
270        )
271
272        additional_paths = self.run_pip.call_args[0][1]
273        self.assertEqual(len(additional_paths), 2)
274        self.assertEqual(exit_code, 0)
275
276    def test_bootstrapping_error_code(self):
277        self.run_pip.return_value = 2
278        exit_code = ensurepip._main([])
279        self.assertEqual(exit_code, 2)
280
281
282
283class TestUninstallationMainFunction(EnsurepipMixin, unittest.TestCase):
284
285    def test_uninstall_version(self):
286        with test.test_support.captured_stderr() as stderr:
287            with self.assertRaises(SystemExit):
288                ensurepip._uninstall._main(["--version"])
289        result = stderr.getvalue().strip()
290        self.assertEqual(result, EXPECTED_VERSION_OUTPUT)
291        self.assertFalse(self.run_pip.called)
292
293    def test_basic_uninstall(self):
294        with fake_pip():
295            exit_code = ensurepip._uninstall._main([])
296
297        self.run_pip.assert_called_once_with(
298            [
299                "uninstall", "-y", "--disable-pip-version-check", "pip",
300                "setuptools",
301            ]
302        )
303
304        self.assertEqual(exit_code, 0)
305
306    def test_uninstall_error_code(self):
307        with fake_pip():
308            self.run_pip.return_value = 2
309            exit_code = ensurepip._uninstall._main([])
310        self.assertEqual(exit_code, 2)
311
312if __name__ == "__main__":
313    test.test_support.run_unittest(__name__)
314