test_ensurepip.py 12 KB

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