(root)/
Python-3.11.7/
Lib/
ensurepip/
__init__.py
       1  import collections
       2  import os
       3  import os.path
       4  import subprocess
       5  import sys
       6  import sysconfig
       7  import tempfile
       8  from importlib import resources
       9  
      10  
      11  __all__ = ["version", "bootstrap"]
      12  _PACKAGE_NAMES = ('setuptools', 'pip')
      13  _SETUPTOOLS_VERSION = "65.5.0"
      14  _PIP_VERSION = "23.2.1"
      15  _PROJECTS = [
      16      ("setuptools", _SETUPTOOLS_VERSION, "py3"),
      17      ("pip", _PIP_VERSION, "py3"),
      18  ]
      19  
      20  # Packages bundled in ensurepip._bundled have wheel_name set.
      21  # Packages from WHEEL_PKG_DIR have wheel_path set.
      22  _Package = collections.namedtuple('Package',
      23                                    ('version', 'wheel_name', 'wheel_path'))
      24  
      25  # Directory of system wheel packages. Some Linux distribution packaging
      26  # policies recommend against bundling dependencies. For example, Fedora
      27  # installs wheel packages in the /usr/share/python-wheels/ directory and don't
      28  # install the ensurepip._bundled package.
      29  _WHEEL_PKG_DIR = sysconfig.get_config_var('WHEEL_PKG_DIR')
      30  
      31  
      32  def _find_packages(path):
      33      packages = {}
      34      try:
      35          filenames = os.listdir(path)
      36      except OSError:
      37          # Ignore: path doesn't exist or permission error
      38          filenames = ()
      39      # Make the code deterministic if a directory contains multiple wheel files
      40      # of the same package, but don't attempt to implement correct version
      41      # comparison since this case should not happen.
      42      filenames = sorted(filenames)
      43      for filename in filenames:
      44          # filename is like 'pip-21.2.4-py3-none-any.whl'
      45          if not filename.endswith(".whl"):
      46              continue
      47          for name in _PACKAGE_NAMES:
      48              prefix = name + '-'
      49              if filename.startswith(prefix):
      50                  break
      51          else:
      52              continue
      53  
      54          # Extract '21.2.4' from 'pip-21.2.4-py3-none-any.whl'
      55          version = filename.removeprefix(prefix).partition('-')[0]
      56          wheel_path = os.path.join(path, filename)
      57          packages[name] = _Package(version, None, wheel_path)
      58      return packages
      59  
      60  
      61  def _get_packages():
      62      global _PACKAGES, _WHEEL_PKG_DIR
      63      if _PACKAGES is not None:
      64          return _PACKAGES
      65  
      66      packages = {}
      67      for name, version, py_tag in _PROJECTS:
      68          wheel_name = f"{name}-{version}-{py_tag}-none-any.whl"
      69          packages[name] = _Package(version, wheel_name, None)
      70      if _WHEEL_PKG_DIR:
      71          dir_packages = _find_packages(_WHEEL_PKG_DIR)
      72          # only used the wheel package directory if all packages are found there
      73          if all(name in dir_packages for name in _PACKAGE_NAMES):
      74              packages = dir_packages
      75      _PACKAGES = packages
      76      return packages
      77  _PACKAGES = None
      78  
      79  
      80  def _run_pip(args, additional_paths=None):
      81      # Run the bootstrapping in a subprocess to avoid leaking any state that happens
      82      # after pip has executed. Particularly, this avoids the case when pip holds onto
      83      # the files in *additional_paths*, preventing us to remove them at the end of the
      84      # invocation.
      85      code = f"""
      86  import runpy
      87  import sys
      88  sys.path = {additional_paths or []} + sys.path
      89  sys.argv[1:] = {args}
      90  runpy.run_module("pip", run_name="__main__", alter_sys=True)
      91  """
      92  
      93      cmd = [
      94          sys.executable,
      95          '-W',
      96          'ignore::DeprecationWarning',
      97          '-c',
      98          code,
      99      ]
     100      if sys.flags.isolated:
     101          # run code in isolated mode if currently running isolated
     102          cmd.insert(1, '-I')
     103      return subprocess.run(cmd, check=True).returncode
     104  
     105  
     106  def version():
     107      """
     108      Returns a string specifying the bundled version of pip.
     109      """
     110      return _get_packages()['pip'].version
     111  
     112  
     113  def _disable_pip_configuration_settings():
     114      # We deliberately ignore all pip environment variables
     115      # when invoking pip
     116      # See http://bugs.python.org/issue19734 for details
     117      keys_to_remove = [k for k in os.environ if k.startswith("PIP_")]
     118      for k in keys_to_remove:
     119          del os.environ[k]
     120      # We also ignore the settings in the default pip configuration file
     121      # See http://bugs.python.org/issue20053 for details
     122      os.environ['PIP_CONFIG_FILE'] = os.devnull
     123  
     124  
     125  def bootstrap(*, root=None, upgrade=False, user=False,
     126                altinstall=False, default_pip=False,
     127                verbosity=0):
     128      """
     129      Bootstrap pip into the current Python installation (or the given root
     130      directory).
     131  
     132      Note that calling this function will alter both sys.path and os.environ.
     133      """
     134      # Discard the return value
     135      _bootstrap(root=root, upgrade=upgrade, user=user,
     136                 altinstall=altinstall, default_pip=default_pip,
     137                 verbosity=verbosity)
     138  
     139  
     140  def _bootstrap(*, root=None, upgrade=False, user=False,
     141                altinstall=False, default_pip=False,
     142                verbosity=0):
     143      """
     144      Bootstrap pip into the current Python installation (or the given root
     145      directory). Returns pip command status code.
     146  
     147      Note that calling this function will alter both sys.path and os.environ.
     148      """
     149      if altinstall and default_pip:
     150          raise ValueError("Cannot use altinstall and default_pip together")
     151  
     152      sys.audit("ensurepip.bootstrap", root)
     153  
     154      _disable_pip_configuration_settings()
     155  
     156      # By default, installing pip and setuptools installs all of the
     157      # following scripts (X.Y == running Python version):
     158      #
     159      #   pip, pipX, pipX.Y, easy_install, easy_install-X.Y
     160      #
     161      # pip 1.5+ allows ensurepip to request that some of those be left out
     162      if altinstall:
     163          # omit pip, pipX and easy_install
     164          os.environ["ENSUREPIP_OPTIONS"] = "altinstall"
     165      elif not default_pip:
     166          # omit pip and easy_install
     167          os.environ["ENSUREPIP_OPTIONS"] = "install"
     168  
     169      with tempfile.TemporaryDirectory() as tmpdir:
     170          # Put our bundled wheels into a temporary directory and construct the
     171          # additional paths that need added to sys.path
     172          additional_paths = []
     173          for name, package in _get_packages().items():
     174              if package.wheel_name:
     175                  # Use bundled wheel package
     176                  wheel_name = package.wheel_name
     177                  wheel_path = resources.files("ensurepip") / "_bundled" / wheel_name
     178                  whl = wheel_path.read_bytes()
     179              else:
     180                  # Use the wheel package directory
     181                  with open(package.wheel_path, "rb") as fp:
     182                      whl = fp.read()
     183                  wheel_name = os.path.basename(package.wheel_path)
     184  
     185              filename = os.path.join(tmpdir, wheel_name)
     186              with open(filename, "wb") as fp:
     187                  fp.write(whl)
     188  
     189              additional_paths.append(filename)
     190  
     191          # Construct the arguments to be passed to the pip command
     192          args = ["install", "--no-cache-dir", "--no-index", "--find-links", tmpdir]
     193          if root:
     194              args += ["--root", root]
     195          if upgrade:
     196              args += ["--upgrade"]
     197          if user:
     198              args += ["--user"]
     199          if verbosity:
     200              args += ["-" + "v" * verbosity]
     201  
     202          return _run_pip([*args, *_PACKAGE_NAMES], additional_paths)
     203  
     204  def _uninstall_helper(*, verbosity=0):
     205      """Helper to support a clean default uninstall process on Windows
     206  
     207      Note that calling this function may alter os.environ.
     208      """
     209      # Nothing to do if pip was never installed, or has been removed
     210      try:
     211          import pip
     212      except ImportError:
     213          return
     214  
     215      # If the installed pip version doesn't match the available one,
     216      # leave it alone
     217      available_version = version()
     218      if pip.__version__ != available_version:
     219          print(f"ensurepip will only uninstall a matching version "
     220                f"({pip.__version__!r} installed, "
     221                f"{available_version!r} available)",
     222                file=sys.stderr)
     223          return
     224  
     225      _disable_pip_configuration_settings()
     226  
     227      # Construct the arguments to be passed to the pip command
     228      args = ["uninstall", "-y", "--disable-pip-version-check"]
     229      if verbosity:
     230          args += ["-" + "v" * verbosity]
     231  
     232      return _run_pip([*args, *reversed(_PACKAGE_NAMES)])
     233  
     234  
     235  def _main(argv=None):
     236      import argparse
     237      parser = argparse.ArgumentParser(prog="python -m ensurepip")
     238      parser.add_argument(
     239          "--version",
     240          action="version",
     241          version="pip {}".format(version()),
     242          help="Show the version of pip that is bundled with this Python.",
     243      )
     244      parser.add_argument(
     245          "-v", "--verbose",
     246          action="count",
     247          default=0,
     248          dest="verbosity",
     249          help=("Give more output. Option is additive, and can be used up to 3 "
     250                "times."),
     251      )
     252      parser.add_argument(
     253          "-U", "--upgrade",
     254          action="store_true",
     255          default=False,
     256          help="Upgrade pip and dependencies, even if already installed.",
     257      )
     258      parser.add_argument(
     259          "--user",
     260          action="store_true",
     261          default=False,
     262          help="Install using the user scheme.",
     263      )
     264      parser.add_argument(
     265          "--root",
     266          default=None,
     267          help="Install everything relative to this alternate root directory.",
     268      )
     269      parser.add_argument(
     270          "--altinstall",
     271          action="store_true",
     272          default=False,
     273          help=("Make an alternate install, installing only the X.Y versioned "
     274                "scripts (Default: pipX, pipX.Y, easy_install-X.Y)."),
     275      )
     276      parser.add_argument(
     277          "--default-pip",
     278          action="store_true",
     279          default=False,
     280          help=("Make a default pip install, installing the unqualified pip "
     281                "and easy_install in addition to the versioned scripts."),
     282      )
     283  
     284      args = parser.parse_args(argv)
     285  
     286      return _bootstrap(
     287          root=args.root,
     288          upgrade=args.upgrade,
     289          user=args.user,
     290          verbosity=args.verbosity,
     291          altinstall=args.altinstall,
     292          default_pip=args.default_pip,
     293      )