bpo-31351: Set return code in ensurepip when pip fails (GH-3626)
Previously ensurepip would always report success, even if the pip installation failed.
This commit is contained in:
parent
a96c96f5da
commit
9adda0cdf8
|
@ -78,6 +78,9 @@ options:
|
||||||
|
|
||||||
Providing both of the script selection options will trigger an exception.
|
Providing both of the script selection options will trigger an exception.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.7.0
|
||||||
|
The exit status is non-zero if the command fails.
|
||||||
|
|
||||||
|
|
||||||
Module API
|
Module API
|
||||||
----------
|
----------
|
||||||
|
|
|
@ -25,7 +25,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():
|
||||||
|
@ -53,6 +53,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=False,
|
||||||
|
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:
|
||||||
|
@ -99,7 +114,7 @@ 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)
|
||||||
|
|
||||||
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
|
||||||
|
@ -126,7 +141,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):
|
||||||
|
@ -180,7 +195,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,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import ensurepip
|
import ensurepip
|
||||||
|
import sys
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
ensurepip._main()
|
sys.exit(ensurepip._main())
|
||||||
|
|
|
@ -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):
|
||||||
|
|
||||||
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())
|
||||||
|
|
|
@ -20,6 +20,7 @@ class EnsurepipMixin:
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
run_pip_patch = unittest.mock.patch("ensurepip._run_pip")
|
run_pip_patch = unittest.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
|
||||||
|
@ -255,7 +256,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(
|
||||||
[
|
[
|
||||||
|
@ -267,6 +268,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):
|
||||||
|
|
||||||
|
@ -280,7 +288,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(
|
||||||
[
|
[
|
||||||
|
@ -289,6 +297,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__":
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
python -m ensurepip now exits with non-zero exit code if pip bootstrapping
|
||||||
|
has failed.
|
Loading…
Reference in New Issue