[pypy-commit] pypy py3k: ensurepip from the latest cpython w/ the latest pip/setuptools
pjenvey
pypy.commits at gmail.com
Thu May 26 02:53:00 EDT 2016
Author: Philip Jenvey <pjenvey at underboss.org>
Branch: py3k
Changeset: r84698:e63dded27736
Date: 2016-05-25 23:49 -0700
http://bitbucket.org/pypy/pypy/changeset/e63dded27736/
Log: ensurepip from the latest cpython w/ the latest pip/setuptools
diff --git a/lib-python/3/ensurepip/__init__.py b/lib-python/3/ensurepip/__init__.py
new file mode 100644
--- /dev/null
+++ b/lib-python/3/ensurepip/__init__.py
@@ -0,0 +1,210 @@
+import os
+import os.path
+import pkgutil
+import sys
+import tempfile
+
+
+__all__ = ["version", "bootstrap"]
+
+
+_SETUPTOOLS_VERSION = "21.2.1"
+
+_PIP_VERSION = "8.1.2"
+
+# pip currently requires ssl support, so we try to provide a nicer
+# error message when that is missing (http://bugs.python.org/issue19744)
+_MISSING_SSL_MESSAGE = ("pip {} requires SSL/TLS".format(_PIP_VERSION))
+try:
+ import ssl
+except ImportError:
+ ssl = None
+ def _require_ssl_for_pip():
+ raise RuntimeError(_MISSING_SSL_MESSAGE)
+else:
+ def _require_ssl_for_pip():
+ pass
+
+_PROJECTS = [
+ ("setuptools", _SETUPTOOLS_VERSION),
+ ("pip", _PIP_VERSION),
+]
+
+
+def _run_pip(args, additional_paths=None):
+ # Add our bundled software to the sys.path so we can import it
+ if additional_paths is not None:
+ sys.path = additional_paths + sys.path
+
+ # Install the bundled software
+ import pip
+ pip.main(args)
+
+
+def version():
+ """
+ Returns a string specifying the bundled version of pip.
+ """
+ return _PIP_VERSION
+
+def _disable_pip_configuration_settings():
+ # We deliberately ignore all pip environment variables
+ # when invoking pip
+ # See http://bugs.python.org/issue19734 for details
+ keys_to_remove = [k for k in os.environ if k.startswith("PIP_")]
+ for k in keys_to_remove:
+ del os.environ[k]
+ # We also ignore the settings in the default pip configuration file
+ # See http://bugs.python.org/issue20053 for details
+ os.environ['PIP_CONFIG_FILE'] = os.devnull
+
+
+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).
+
+ Note that calling this function will alter both sys.path and os.environ.
+ """
+ if altinstall and default_pip:
+ raise ValueError("Cannot use altinstall and default_pip together")
+
+ _require_ssl_for_pip()
+ _disable_pip_configuration_settings()
+
+ # By default, installing pip and setuptools installs all of the
+ # following scripts (X.Y == running Python version):
+ #
+ # pip, pipX, pipX.Y, easy_install, easy_install-X.Y
+ #
+ # pip 1.5+ allows ensurepip to request that some of those be left out
+ if altinstall:
+ # omit pip, pipX and easy_install
+ os.environ["ENSUREPIP_OPTIONS"] = "altinstall"
+ elif not default_pip:
+ # omit pip and easy_install
+ os.environ["ENSUREPIP_OPTIONS"] = "install"
+
+ with tempfile.TemporaryDirectory() as tmpdir:
+ # Put our bundled wheels into a temporary directory and construct the
+ # additional paths that need added to sys.path
+ additional_paths = []
+ for project, version in _PROJECTS:
+ wheel_name = "{}-{}-py2.py3-none-any.whl".format(project, version)
+ whl = pkgutil.get_data(
+ "ensurepip",
+ "_bundled/{}".format(wheel_name),
+ )
+ with open(os.path.join(tmpdir, wheel_name), "wb") as fp:
+ fp.write(whl)
+
+ additional_paths.append(os.path.join(tmpdir, wheel_name))
+
+ # Construct the arguments to be passed to the pip command
+ args = ["install", "--no-index", "--find-links", tmpdir]
+ if root:
+ args += ["--root", root]
+ if upgrade:
+ args += ["--upgrade"]
+ if user:
+ args += ["--user"]
+ if verbosity:
+ args += ["-" + "v" * verbosity]
+
+ _run_pip(args + [p[0] for p in _PROJECTS], additional_paths)
+
+def _uninstall_helper(*, verbosity=0):
+ """Helper to support a clean default uninstall process on Windows
+
+ Note that calling this function may alter os.environ.
+ """
+ # Nothing to do if pip was never installed, or has been removed
+ try:
+ import pip
+ except ImportError:
+ return
+
+ # If the pip version doesn't match the bundled one, leave it alone
+ if pip.__version__ != _PIP_VERSION:
+ msg = ("ensurepip will only uninstall a matching version "
+ "({!r} installed, {!r} bundled)")
+ print(msg.format(pip.__version__, _PIP_VERSION), file=sys.stderr)
+ return
+
+ _require_ssl_for_pip()
+ _disable_pip_configuration_settings()
+
+ # Construct the arguments to be passed to the pip command
+ args = ["uninstall", "-y", "--disable-pip-version-check"]
+ if verbosity:
+ args += ["-" + "v" * verbosity]
+
+ _run_pip(args + [p[0] for p in reversed(_PROJECTS)])
+
+
+def _main(argv=None):
+ if ssl is None:
+ print("Ignoring ensurepip failure: {}".format(_MISSING_SSL_MESSAGE),
+ file=sys.stderr)
+ return
+
+ import argparse
+ parser = argparse.ArgumentParser(prog="python -m ensurepip")
+ parser.add_argument(
+ "--version",
+ action="version",
+ version="pip {}".format(version()),
+ help="Show the version of pip that is bundled with this Python.",
+ )
+ parser.add_argument(
+ "-v", "--verbose",
+ action="count",
+ default=0,
+ dest="verbosity",
+ help=("Give more output. Option is additive, and can be used up to 3 "
+ "times."),
+ )
+ parser.add_argument(
+ "-U", "--upgrade",
+ action="store_true",
+ default=False,
+ help="Upgrade pip and dependencies, even if already installed.",
+ )
+ parser.add_argument(
+ "--user",
+ action="store_true",
+ default=False,
+ help="Install using the user scheme.",
+ )
+ parser.add_argument(
+ "--root",
+ default=None,
+ help="Install everything relative to this alternate root directory.",
+ )
+ parser.add_argument(
+ "--altinstall",
+ action="store_true",
+ default=False,
+ help=("Make an alternate install, installing only the X.Y versioned"
+ "scripts (Default: pipX, pipX.Y, easy_install-X.Y)"),
+ )
+ parser.add_argument(
+ "--default-pip",
+ action="store_true",
+ default=False,
+ help=("Make a default pip install, installing the unqualified pip "
+ "and easy_install in addition to the versioned scripts"),
+ )
+
+ args = parser.parse_args(argv)
+
+ bootstrap(
+ root=args.root,
+ upgrade=args.upgrade,
+ user=args.user,
+ verbosity=args.verbosity,
+ altinstall=args.altinstall,
+ default_pip=args.default_pip,
+ )
diff --git a/lib-python/3/ensurepip/__main__.py b/lib-python/3/ensurepip/__main__.py
new file mode 100644
--- /dev/null
+++ b/lib-python/3/ensurepip/__main__.py
@@ -0,0 +1,4 @@
+import ensurepip
+
+if __name__ == "__main__":
+ ensurepip._main()
diff --git a/lib-python/3/ensurepip/_bundled/pip-8.1.2-py2.py3-none-any.whl b/lib-python/3/ensurepip/_bundled/pip-8.1.2-py2.py3-none-any.whl
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cc49227a0c7e13757f4863a9b7ace1eb56c3ce61
GIT binary patch
[cut]
diff --git a/lib-python/3/ensurepip/_bundled/setuptools-21.2.1-py2.py3-none-any.whl b/lib-python/3/ensurepip/_bundled/setuptools-21.2.1-py2.py3-none-any.whl
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..fe36464f79ba87960c33f3bdff817deb9e4e5f7c
GIT binary patch
[cut]
diff --git a/lib-python/3/ensurepip/_uninstall.py b/lib-python/3/ensurepip/_uninstall.py
new file mode 100644
--- /dev/null
+++ b/lib-python/3/ensurepip/_uninstall.py
@@ -0,0 +1,30 @@
+"""Basic pip uninstallation support, helper for the Windows uninstaller"""
+
+import argparse
+import ensurepip
+
+
+def _main(argv=None):
+ parser = argparse.ArgumentParser(prog="python -m ensurepip._uninstall")
+ parser.add_argument(
+ "--version",
+ action="version",
+ version="pip {}".format(ensurepip.version()),
+ help="Show the version of pip this will attempt to uninstall.",
+ )
+ parser.add_argument(
+ "-v", "--verbose",
+ action="count",
+ default=0,
+ dest="verbosity",
+ help=("Give more output. Option is additive, and can be used up to 3 "
+ "times."),
+ )
+
+ args = parser.parse_args(argv)
+
+ ensurepip._uninstall_helper(verbosity=args.verbosity)
+
+
+if __name__ == "__main__":
+ _main()
diff --git a/lib-python/3/test/test_ensurepip.py b/lib-python/3/test/test_ensurepip.py
new file mode 100644
--- /dev/null
+++ b/lib-python/3/test/test_ensurepip.py
@@ -0,0 +1,360 @@
+import unittest
+import unittest.mock
+import test.support
+import os
+import os.path
+import contextlib
+import sys
+
+import ensurepip
+import ensurepip._uninstall
+
+# pip currently requires ssl support, so we ensure we handle
+# it being missing (http://bugs.python.org/issue19744)
+ensurepip_no_ssl = test.support.import_fresh_module("ensurepip",
+ blocked=["ssl"])
+try:
+ import ssl
+except ImportError:
+ def requires_usable_pip(f):
+ deco = unittest.skip(ensurepip._MISSING_SSL_MESSAGE)
+ return deco(f)
+else:
+ def requires_usable_pip(f):
+ return f
+
+class TestEnsurePipVersion(unittest.TestCase):
+
+ def test_returns_version(self):
+ self.assertEqual(ensurepip._PIP_VERSION, ensurepip.version())
+
+class EnsurepipMixin:
+
+ def setUp(self):
+ run_pip_patch = unittest.mock.patch("ensurepip._run_pip")
+ self.run_pip = run_pip_patch.start()
+ self.addCleanup(run_pip_patch.stop)
+
+ # Avoid side effects on the actual os module
+ real_devnull = os.devnull
+ os_patch = unittest.mock.patch("ensurepip.os")
+ patched_os = os_patch.start()
+ self.addCleanup(os_patch.stop)
+ patched_os.devnull = real_devnull
+ patched_os.path = os.path
+ self.os_environ = patched_os.environ = os.environ.copy()
+
+
+class TestBootstrap(EnsurepipMixin, unittest.TestCase):
+
+ @requires_usable_pip
+ def test_basic_bootstrapping(self):
+ ensurepip.bootstrap()
+
+ self.run_pip.assert_called_once_with(
+ [
+ "install", "--no-index", "--find-links",
+ unittest.mock.ANY, "setuptools", "pip",
+ ],
+ unittest.mock.ANY,
+ )
+
+ additional_paths = self.run_pip.call_args[0][1]
+ self.assertEqual(len(additional_paths), 2)
+
+ @requires_usable_pip
+ def test_bootstrapping_with_root(self):
+ ensurepip.bootstrap(root="/foo/bar/")
+
+ self.run_pip.assert_called_once_with(
+ [
+ "install", "--no-index", "--find-links",
+ unittest.mock.ANY, "--root", "/foo/bar/",
+ "setuptools", "pip",
+ ],
+ unittest.mock.ANY,
+ )
+
+ @requires_usable_pip
+ def test_bootstrapping_with_user(self):
+ ensurepip.bootstrap(user=True)
+
+ self.run_pip.assert_called_once_with(
+ [
+ "install", "--no-index", "--find-links",
+ unittest.mock.ANY, "--user", "setuptools", "pip",
+ ],
+ unittest.mock.ANY,
+ )
+
+ @requires_usable_pip
+ def test_bootstrapping_with_upgrade(self):
+ ensurepip.bootstrap(upgrade=True)
+
+ self.run_pip.assert_called_once_with(
+ [
+ "install", "--no-index", "--find-links",
+ unittest.mock.ANY, "--upgrade", "setuptools", "pip",
+ ],
+ unittest.mock.ANY,
+ )
+
+ @requires_usable_pip
+ def test_bootstrapping_with_verbosity_1(self):
+ ensurepip.bootstrap(verbosity=1)
+
+ self.run_pip.assert_called_once_with(
+ [
+ "install", "--no-index", "--find-links",
+ unittest.mock.ANY, "-v", "setuptools", "pip",
+ ],
+ unittest.mock.ANY,
+ )
+
+ @requires_usable_pip
+ def test_bootstrapping_with_verbosity_2(self):
+ ensurepip.bootstrap(verbosity=2)
+
+ self.run_pip.assert_called_once_with(
+ [
+ "install", "--no-index", "--find-links",
+ unittest.mock.ANY, "-vv", "setuptools", "pip",
+ ],
+ unittest.mock.ANY,
+ )
+
+ @requires_usable_pip
+ def test_bootstrapping_with_verbosity_3(self):
+ ensurepip.bootstrap(verbosity=3)
+
+ self.run_pip.assert_called_once_with(
+ [
+ "install", "--no-index", "--find-links",
+ unittest.mock.ANY, "-vvv", "setuptools", "pip",
+ ],
+ unittest.mock.ANY,
+ )
+
+ @requires_usable_pip
+ def test_bootstrapping_with_regular_install(self):
+ ensurepip.bootstrap()
+ self.assertEqual(self.os_environ["ENSUREPIP_OPTIONS"], "install")
+
+ @requires_usable_pip
+ def test_bootstrapping_with_alt_install(self):
+ ensurepip.bootstrap(altinstall=True)
+ self.assertEqual(self.os_environ["ENSUREPIP_OPTIONS"], "altinstall")
+
+ @requires_usable_pip
+ def test_bootstrapping_with_default_pip(self):
+ ensurepip.bootstrap(default_pip=True)
+ self.assertNotIn("ENSUREPIP_OPTIONS", self.os_environ)
+
+ def test_altinstall_default_pip_conflict(self):
+ with self.assertRaises(ValueError):
+ ensurepip.bootstrap(altinstall=True, default_pip=True)
+ self.assertFalse(self.run_pip.called)
+
+ @requires_usable_pip
+ def test_pip_environment_variables_removed(self):
+ # ensurepip deliberately ignores all pip environment variables
+ # See http://bugs.python.org/issue19734 for details
+ self.os_environ["PIP_THIS_SHOULD_GO_AWAY"] = "test fodder"
+ ensurepip.bootstrap()
+ self.assertNotIn("PIP_THIS_SHOULD_GO_AWAY", self.os_environ)
+
+ @requires_usable_pip
+ def test_pip_config_file_disabled(self):
+ # ensurepip deliberately ignores the pip config file
+ # See http://bugs.python.org/issue20053 for details
+ ensurepip.bootstrap()
+ self.assertEqual(self.os_environ["PIP_CONFIG_FILE"], os.devnull)
+
+ at contextlib.contextmanager
+def fake_pip(version=ensurepip._PIP_VERSION):
+ if version is None:
+ pip = None
+ else:
+ class FakePip():
+ __version__ = version
+ pip = FakePip()
+ sentinel = object()
+ orig_pip = sys.modules.get("pip", sentinel)
+ sys.modules["pip"] = pip
+ try:
+ yield pip
+ finally:
+ if orig_pip is sentinel:
+ del sys.modules["pip"]
+ else:
+ sys.modules["pip"] = orig_pip
+
+class TestUninstall(EnsurepipMixin, unittest.TestCase):
+
+ def test_uninstall_skipped_when_not_installed(self):
+ with fake_pip(None):
+ ensurepip._uninstall_helper()
+ self.assertFalse(self.run_pip.called)
+
+ def test_uninstall_skipped_with_warning_for_wrong_version(self):
+ with fake_pip("not a valid version"):
+ with test.support.captured_stderr() as stderr:
+ ensurepip._uninstall_helper()
+ warning = stderr.getvalue().strip()
+ self.assertIn("only uninstall a matching version", warning)
+ self.assertFalse(self.run_pip.called)
+
+
+ @requires_usable_pip
+ def test_uninstall(self):
+ with fake_pip():
+ ensurepip._uninstall_helper()
+
+ self.run_pip.assert_called_once_with(
+ [
+ "uninstall", "-y", "--disable-pip-version-check", "pip",
+ "setuptools",
+ ]
+ )
+
+ @requires_usable_pip
+ def test_uninstall_with_verbosity_1(self):
+ with fake_pip():
+ ensurepip._uninstall_helper(verbosity=1)
+
+ self.run_pip.assert_called_once_with(
+ [
+ "uninstall", "-y", "--disable-pip-version-check", "-v", "pip",
+ "setuptools",
+ ]
+ )
+
+ @requires_usable_pip
+ def test_uninstall_with_verbosity_2(self):
+ with fake_pip():
+ ensurepip._uninstall_helper(verbosity=2)
+
+ self.run_pip.assert_called_once_with(
+ [
+ "uninstall", "-y", "--disable-pip-version-check", "-vv", "pip",
+ "setuptools",
+ ]
+ )
+
+ @requires_usable_pip
+ def test_uninstall_with_verbosity_3(self):
+ with fake_pip():
+ ensurepip._uninstall_helper(verbosity=3)
+
+ self.run_pip.assert_called_once_with(
+ [
+ "uninstall", "-y", "--disable-pip-version-check", "-vvv",
+ "pip", "setuptools",
+ ]
+ )
+
+ @requires_usable_pip
+ def test_pip_environment_variables_removed(self):
+ # ensurepip deliberately ignores all pip environment variables
+ # See http://bugs.python.org/issue19734 for details
+ self.os_environ["PIP_THIS_SHOULD_GO_AWAY"] = "test fodder"
+ with fake_pip():
+ ensurepip._uninstall_helper()
+ self.assertNotIn("PIP_THIS_SHOULD_GO_AWAY", self.os_environ)
+
+ @requires_usable_pip
+ def test_pip_config_file_disabled(self):
+ # ensurepip deliberately ignores the pip config file
+ # See http://bugs.python.org/issue20053 for details
+ with fake_pip():
+ ensurepip._uninstall_helper()
+ self.assertEqual(self.os_environ["PIP_CONFIG_FILE"], os.devnull)
+
+
+class TestMissingSSL(EnsurepipMixin, unittest.TestCase):
+
+ def setUp(self):
+ sys.modules["ensurepip"] = ensurepip_no_ssl
+ @self.addCleanup
+ def restore_module():
+ sys.modules["ensurepip"] = ensurepip
+ super().setUp()
+
+ def test_bootstrap_requires_ssl(self):
+ self.os_environ["PIP_THIS_SHOULD_STAY"] = "test fodder"
+ with self.assertRaisesRegex(RuntimeError, "requires SSL/TLS"):
+ ensurepip_no_ssl.bootstrap()
+ self.assertFalse(self.run_pip.called)
+ self.assertIn("PIP_THIS_SHOULD_STAY", self.os_environ)
+
+ def test_uninstall_requires_ssl(self):
+ self.os_environ["PIP_THIS_SHOULD_STAY"] = "test fodder"
+ with self.assertRaisesRegex(RuntimeError, "requires SSL/TLS"):
+ with fake_pip():
+ ensurepip_no_ssl._uninstall_helper()
+ self.assertFalse(self.run_pip.called)
+ self.assertIn("PIP_THIS_SHOULD_STAY", self.os_environ)
+
+ def test_main_exits_early_with_warning(self):
+ with test.support.captured_stderr() as stderr:
+ ensurepip_no_ssl._main(["--version"])
+ warning = stderr.getvalue().strip()
+ self.assertTrue(warning.endswith("requires SSL/TLS"), warning)
+ self.assertFalse(self.run_pip.called)
+
+# Basic testing of the main functions and their argument parsing
+
+EXPECTED_VERSION_OUTPUT = "pip " + ensurepip._PIP_VERSION
+
+class TestBootstrappingMainFunction(EnsurepipMixin, unittest.TestCase):
+
+ @requires_usable_pip
+ def test_bootstrap_version(self):
+ with test.support.captured_stdout() as stdout:
+ with self.assertRaises(SystemExit):
+ ensurepip._main(["--version"])
+ result = stdout.getvalue().strip()
+ self.assertEqual(result, EXPECTED_VERSION_OUTPUT)
+ self.assertFalse(self.run_pip.called)
+
+ @requires_usable_pip
+ def test_basic_bootstrapping(self):
+ ensurepip._main([])
+
+ self.run_pip.assert_called_once_with(
+ [
+ "install", "--no-index", "--find-links",
+ unittest.mock.ANY, "setuptools", "pip",
+ ],
+ unittest.mock.ANY,
+ )
+
+ additional_paths = self.run_pip.call_args[0][1]
+ self.assertEqual(len(additional_paths), 2)
+
+class TestUninstallationMainFunction(EnsurepipMixin, unittest.TestCase):
+
+ def test_uninstall_version(self):
+ with test.support.captured_stdout() as stdout:
+ with self.assertRaises(SystemExit):
+ ensurepip._uninstall._main(["--version"])
+ result = stdout.getvalue().strip()
+ self.assertEqual(result, EXPECTED_VERSION_OUTPUT)
+ self.assertFalse(self.run_pip.called)
+
+ @requires_usable_pip
+ def test_basic_uninstall(self):
+ with fake_pip():
+ ensurepip._uninstall._main([])
+
+ self.run_pip.assert_called_once_with(
+ [
+ "uninstall", "-y", "--disable-pip-version-check", "pip",
+ "setuptools",
+ ]
+ )
+
+
+
+if __name__ == "__main__":
+ unittest.main()
More information about the pypy-commit
mailing list