]> granicus.if.org Git - python/commitdiff
[2.7] bpo-31351: Set return code in ensurepip when pip fails (GH-3734)
authorIgor Filatov <iafilatov@users.noreply.github.com>
Mon, 25 Sep 2017 01:03:24 +0000 (04:03 +0300)
committerNick Coghlan <ncoghlan@gmail.com>
Mon, 25 Sep 2017 01:03:24 +0000 (11:03 +1000)
Previously ensurepip would always report success, even if the
pip installation failed.

(cherry picked from commit 9adda0cdf89432386b7a04444a6199b580d287a1)

Doc/library/ensurepip.rst
Lib/ensurepip/__init__.py
Lib/ensurepip/__main__.py
Lib/ensurepip/_uninstall.py
Lib/test/test_ensurepip.py
Misc/NEWS.d/next/Library/2017-09-17-15-24-25.bpo-31351.yQdKv-.rst [new file with mode: 0644]

index a6358e44b15d96cc0b079437e74b79f6168968b3..b6f7120947c4a5a3d924616f250cd733ddf58890 100644 (file)
@@ -76,6 +76,9 @@ options:
 * ``--no-default-pip``: if a non-default installation is request, the ``pip``
   script will *not* be installed.
 
+.. versionchanged:: 2.7.15
+   The exit status is non-zero if the command fails.
+
 
 Module API
 ----------
index c2abed84ef0c5d917779512f1df31d59dd5808a3..ea2a5e6348228a9cd4e33039d13221921f2c9723 100644 (file)
@@ -29,7 +29,7 @@ def _run_pip(args, additional_paths=None):
 
     # Install the bundled software
     import pip
-    pip.main(args)
+    return pip.main(args)
 
 
 def version():
@@ -58,6 +58,21 @@ def bootstrap(root=None, upgrade=False, user=False,
     Bootstrap pip into the current Python installation (or the given root
     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.
     """
     if altinstall and default_pip:
@@ -105,11 +120,10 @@ def bootstrap(root=None, upgrade=False, user=False,
         if 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:
         shutil.rmtree(tmpdir, ignore_errors=True)
 
-
 def _uninstall_helper(verbosity=0):
     """Helper to support a clean default uninstall process on Windows
 
@@ -135,7 +149,7 @@ def _uninstall_helper(verbosity=0):
     if 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):
@@ -196,7 +210,7 @@ def _main(argv=None):
 
     args = parser.parse_args(argv)
 
-    bootstrap(
+    return _bootstrap(
         root=args.root,
         upgrade=args.upgrade,
         user=args.user,
index 77527d7a3513596852d1eb0ff8d8935a98e0cc26..03eef0dd94d0b77764cbd737f69945d04e4b1bc6 100644 (file)
@@ -1,4 +1,5 @@
 import ensurepip
+import sys
 
 if __name__ == "__main__":
-    ensurepip._main()
+    sys.exit(ensurepip._main())
index 750365ec4d04da980399c94c35668935482b9cce..b257904328d2f5dc3b5f07e7d6c883fb9e2055a9 100644 (file)
@@ -2,6 +2,7 @@
 
 import argparse
 import ensurepip
+import sys
 
 
 def _main(argv=None):
@@ -23,8 +24,8 @@ def _main(argv=None):
 
     args = parser.parse_args(argv)
 
-    ensurepip._uninstall_helper(verbosity=args.verbosity)
+    return ensurepip._uninstall_helper(verbosity=args.verbosity)
 
 
 if __name__ == "__main__":
-    _main()
+    sys.exit(_main())
index 3316fcfaa16d2654aa3ac73627f22695b6ab706b..cb9d10a10497fa4adbec52dba5c1981c93a1e6d1 100644 (file)
@@ -21,6 +21,7 @@ class EnsurepipMixin:
     def setUp(self):
         run_pip_patch = mock.patch("ensurepip._run_pip")
         self.run_pip = run_pip_patch.start()
+        self.run_pip.return_value = 0
         self.addCleanup(run_pip_patch.stop)
 
         # Avoid side effects on the actual os module
@@ -258,7 +259,7 @@ class TestBootstrappingMainFunction(EnsurepipMixin, unittest.TestCase):
         self.assertFalse(self.run_pip.called)
 
     def test_basic_bootstrapping(self):
-        ensurepip._main([])
+        exit_code = ensurepip._main([])
 
         self.run_pip.assert_called_once_with(
             [
@@ -270,6 +271,13 @@ class TestBootstrappingMainFunction(EnsurepipMixin, unittest.TestCase):
 
         additional_paths = self.run_pip.call_args[0][1]
         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):
@@ -284,7 +292,7 @@ class TestUninstallationMainFunction(EnsurepipMixin, unittest.TestCase):
 
     def test_basic_uninstall(self):
         with fake_pip():
-            ensurepip._uninstall._main([])
+            exit_code = ensurepip._uninstall._main([])
 
         self.run_pip.assert_called_once_with(
             [
@@ -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__":
     test.test_support.run_unittest(__name__)
diff --git a/Misc/NEWS.d/next/Library/2017-09-17-15-24-25.bpo-31351.yQdKv-.rst b/Misc/NEWS.d/next/Library/2017-09-17-15-24-25.bpo-31351.yQdKv-.rst
new file mode 100644 (file)
index 0000000..20f2c1b
--- /dev/null
@@ -0,0 +1,2 @@
+python -m ensurepip now exits with non-zero exit code if pip bootstrapping
+has failed.