Commit cf7197ae authored by Igor Filatov's avatar Igor Filatov Committed by Nick Coghlan

[2.7] bpo-31351: Set return code in ensurepip when pip fails (GH-3734)

Previously ensurepip would always report success, even if the
pip installation failed.

(cherry picked from commit 9adda0cd)
parent da86874a
...@@ -76,6 +76,9 @@ options: ...@@ -76,6 +76,9 @@ options:
* ``--no-default-pip``: if a non-default installation is request, the ``pip`` * ``--no-default-pip``: if a non-default installation is request, the ``pip``
script will *not* be installed. script will *not* be installed.
.. versionchanged:: 2.7.15
The exit status is non-zero if the command fails.
Module API Module API
---------- ----------
......
...@@ -29,7 +29,7 @@ def _run_pip(args, additional_paths=None): ...@@ -29,7 +29,7 @@ def _run_pip(args, additional_paths=None):
# Install the bundled software # Install the bundled software
import pip import pip
pip.main(args) return pip.main(args)
def version(): def version():
...@@ -58,6 +58,21 @@ def bootstrap(root=None, upgrade=False, user=False, ...@@ -58,6 +58,21 @@ def bootstrap(root=None, upgrade=False, user=False,
Bootstrap pip into the current Python installation (or the given root Bootstrap pip into the current Python installation (or the given root
directory). directory).
Note that calling this function will alter both sys.path and os.environ.
"""
# Discard the return value
_bootstrap(root=root, upgrade=upgrade, user=user,
altinstall=altinstall, default_pip=default_pip,
verbosity=verbosity)
def _bootstrap(root=None, upgrade=False, user=False,
altinstall=False, default_pip=True,
verbosity=0):
"""
Bootstrap pip into the current Python installation (or the given root
directory). Returns pip command status code.
Note that calling this function will alter both sys.path and os.environ. Note that calling this function will alter both sys.path and os.environ.
""" """
if altinstall and default_pip: if altinstall and default_pip:
...@@ -105,11 +120,10 @@ def bootstrap(root=None, upgrade=False, user=False, ...@@ -105,11 +120,10 @@ def bootstrap(root=None, upgrade=False, user=False,
if verbosity: if verbosity:
args += ["-" + "v" * verbosity] args += ["-" + "v" * verbosity]
_run_pip(args + [p[0] for p in _PROJECTS], additional_paths) return _run_pip(args + [p[0] for p in _PROJECTS], additional_paths)
finally: finally:
shutil.rmtree(tmpdir, ignore_errors=True) shutil.rmtree(tmpdir, ignore_errors=True)
def _uninstall_helper(verbosity=0): def _uninstall_helper(verbosity=0):
"""Helper to support a clean default uninstall process on Windows """Helper to support a clean default uninstall process on Windows
...@@ -135,7 +149,7 @@ def _uninstall_helper(verbosity=0): ...@@ -135,7 +149,7 @@ def _uninstall_helper(verbosity=0):
if verbosity: if verbosity:
args += ["-" + "v" * verbosity] args += ["-" + "v" * verbosity]
_run_pip(args + [p[0] for p in reversed(_PROJECTS)]) return _run_pip(args + [p[0] for p in reversed(_PROJECTS)])
def _main(argv=None): def _main(argv=None):
...@@ -196,7 +210,7 @@ def _main(argv=None): ...@@ -196,7 +210,7 @@ def _main(argv=None):
args = parser.parse_args(argv) args = parser.parse_args(argv)
bootstrap( return _bootstrap(
root=args.root, root=args.root,
upgrade=args.upgrade, upgrade=args.upgrade,
user=args.user, user=args.user,
......
import ensurepip import ensurepip
import sys
if __name__ == "__main__": if __name__ == "__main__":
ensurepip._main() sys.exit(ensurepip._main())
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import argparse import argparse
import ensurepip import ensurepip
import sys
def _main(argv=None): def _main(argv=None):
...@@ -23,8 +24,8 @@ def _main(argv=None): ...@@ -23,8 +24,8 @@ def _main(argv=None):
args = parser.parse_args(argv) args = parser.parse_args(argv)
ensurepip._uninstall_helper(verbosity=args.verbosity) return ensurepip._uninstall_helper(verbosity=args.verbosity)
if __name__ == "__main__": if __name__ == "__main__":
_main() sys.exit(_main())
...@@ -21,6 +21,7 @@ class EnsurepipMixin: ...@@ -21,6 +21,7 @@ class EnsurepipMixin:
def setUp(self): def setUp(self):
run_pip_patch = mock.patch("ensurepip._run_pip") run_pip_patch = mock.patch("ensurepip._run_pip")
self.run_pip = run_pip_patch.start() self.run_pip = run_pip_patch.start()
self.run_pip.return_value = 0
self.addCleanup(run_pip_patch.stop) self.addCleanup(run_pip_patch.stop)
# Avoid side effects on the actual os module # Avoid side effects on the actual os module
...@@ -258,7 +259,7 @@ class TestBootstrappingMainFunction(EnsurepipMixin, unittest.TestCase): ...@@ -258,7 +259,7 @@ class TestBootstrappingMainFunction(EnsurepipMixin, unittest.TestCase):
self.assertFalse(self.run_pip.called) self.assertFalse(self.run_pip.called)
def test_basic_bootstrapping(self): def test_basic_bootstrapping(self):
ensurepip._main([]) exit_code = ensurepip._main([])
self.run_pip.assert_called_once_with( self.run_pip.assert_called_once_with(
[ [
...@@ -270,6 +271,13 @@ class TestBootstrappingMainFunction(EnsurepipMixin, unittest.TestCase): ...@@ -270,6 +271,13 @@ class TestBootstrappingMainFunction(EnsurepipMixin, unittest.TestCase):
additional_paths = self.run_pip.call_args[0][1] additional_paths = self.run_pip.call_args[0][1]
self.assertEqual(len(additional_paths), 2) self.assertEqual(len(additional_paths), 2)
self.assertEqual(exit_code, 0)
def test_bootstrapping_error_code(self):
self.run_pip.return_value = 2
exit_code = ensurepip._main([])
self.assertEqual(exit_code, 2)
class TestUninstallationMainFunction(EnsurepipMixin, unittest.TestCase): class TestUninstallationMainFunction(EnsurepipMixin, unittest.TestCase):
...@@ -284,7 +292,7 @@ class TestUninstallationMainFunction(EnsurepipMixin, unittest.TestCase): ...@@ -284,7 +292,7 @@ class TestUninstallationMainFunction(EnsurepipMixin, unittest.TestCase):
def test_basic_uninstall(self): def test_basic_uninstall(self):
with fake_pip(): with fake_pip():
ensurepip._uninstall._main([]) exit_code = ensurepip._uninstall._main([])
self.run_pip.assert_called_once_with( self.run_pip.assert_called_once_with(
[ [
...@@ -293,6 +301,13 @@ class TestUninstallationMainFunction(EnsurepipMixin, unittest.TestCase): ...@@ -293,6 +301,13 @@ class TestUninstallationMainFunction(EnsurepipMixin, unittest.TestCase):
] ]
) )
self.assertEqual(exit_code, 0)
def test_uninstall_error_code(self):
with fake_pip():
self.run_pip.return_value = 2
exit_code = ensurepip._uninstall._main([])
self.assertEqual(exit_code, 2)
if __name__ == "__main__": if __name__ == "__main__":
test.test_support.run_unittest(__name__) test.test_support.run_unittest(__name__)
python -m ensurepip now exits with non-zero exit code if pip bootstrapping
has failed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment