Index: b/Lib/ensurepip/__init__.py =================================================================== --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -1,3 +1,4 @@ +import glob import os import os.path import pkgutil @@ -8,13 +9,9 @@ import tempfile __all__ = ["version", "bootstrap"] -_SETUPTOOLS_VERSION = "28.8.0" - -_PIP_VERSION = "9.0.1" - # 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)) +_MISSING_SSL_MESSAGE = ("pip requires SSL/TLS") try: import ssl except ImportError: @@ -26,8 +23,9 @@ else: pass _PROJECTS = [ - ("setuptools", _SETUPTOOLS_VERSION), - ("pip", _PIP_VERSION), + "setuptools", + "pip", + "pkg_resources", ] @@ -45,7 +43,10 @@ def version(): """ Returns a string specifying the bundled version of pip. """ - return _PIP_VERSION + wheel_names = glob.glob('/usr/share/python-wheels/pip-*.whl') + assert len(wheel_names) == 1, wheel_names + return os.path.basename(wheel_names[0]).split('-')[1] + def _disable_pip_configuration_settings(): # We deliberately ignore all pip environment variables @@ -87,20 +88,44 @@ def bootstrap(*, root=None, upgrade=Fals # omit pip and easy_install os.environ["ENSUREPIP_OPTIONS"] = "install" + # Debian: The bundled wheels are useless to us because we must use ones + # crafted from source code in the archive. As we build the virtual + # environment, copy the wheels from the system location into the virtual + # environment, and place those wheels on sys.path. + def copy_wheels(wheels, destdir, paths): + for project in wheels: + wheel_names = glob.glob( + '/usr/share/python-wheels/{}-*.whl'.format(project)) + if len(wheel_names) == 0: + raise RuntimeError('missing dependency wheel %s' % project) + assert len(wheel_names) == 1, wheel_names + wheel_name = os.path.basename(wheel_names[0]) + path = os.path.join('/usr/share/python-wheels', wheel_name) + with open(path, 'rb') as fp: + whl = fp.read() + dest = os.path.join(destdir, wheel_name) + with open(dest, 'wb') as fp: + fp.write(whl) + paths.append(dest) + with tempfile.TemporaryDirectory() as tmpdir: + # This directory is a "well known directory" which Debian has patched + # pip to look in when attempting to locate wheels to use to satisfy + # the dependencies that pip normally bundles but Debian has debundled. + # This is critically important and if this directory changes then both + # python-pip and python-virtualenv needs updated to match. + venv_wheel_dir = os.path.join(sys.prefix, 'share', 'python-wheels') + os.makedirs(venv_wheel_dir, exist_ok=True) + dependencies = [ + os.path.basename(whl).split('-')[0] + for whl in glob.glob('/usr/share/python-wheels/*.whl') + ] + copy_wheels(dependencies, venv_wheel_dir, sys.path) + # 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)) + copy_wheels(_PROJECTS, tmpdir, additional_paths) # Construct the arguments to be passed to the pip command args = ["install", "--no-index", "--find-links", tmpdir] @@ -113,7 +138,7 @@ def bootstrap(*, root=None, upgrade=Fals if verbosity: args += ["-" + "v" * verbosity] - _run_pip(args + [p[0] for p in _PROJECTS], additional_paths) + _run_pip(args + _PROJECTS, additional_paths) def _uninstall_helper(*, verbosity=0): """Helper to support a clean default uninstall process on Windows @@ -127,7 +152,8 @@ def _uninstall_helper(*, verbosity=0): return # If the pip version doesn't match the bundled one, leave it alone - if pip.__version__ != _PIP_VERSION: + # Disabled for Debian, always using the version from the python3-pip package. + if False and 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) @@ -141,7 +167,7 @@ def _uninstall_helper(*, verbosity=0): if verbosity: args += ["-" + "v" * verbosity] - _run_pip(args + [p[0] for p in reversed(_PROJECTS)]) + _run_pip(args + reversed(_PROJECTS)) def _main(argv=None):