17db96d56Sopenharmony_ciimport collections 27db96d56Sopenharmony_ciimport os 37db96d56Sopenharmony_ciimport os.path 47db96d56Sopenharmony_ciimport subprocess 57db96d56Sopenharmony_ciimport sys 67db96d56Sopenharmony_ciimport sysconfig 77db96d56Sopenharmony_ciimport tempfile 87db96d56Sopenharmony_cifrom importlib import resources 97db96d56Sopenharmony_ci 107db96d56Sopenharmony_ci 117db96d56Sopenharmony_ci__all__ = ["version", "bootstrap"] 127db96d56Sopenharmony_ci_PACKAGE_NAMES = ('setuptools', 'pip') 137db96d56Sopenharmony_ci_SETUPTOOLS_VERSION = "65.5.0" 147db96d56Sopenharmony_ci_PIP_VERSION = "23.1.2" 157db96d56Sopenharmony_ci_PROJECTS = [ 167db96d56Sopenharmony_ci ("setuptools", _SETUPTOOLS_VERSION, "py3"), 177db96d56Sopenharmony_ci ("pip", _PIP_VERSION, "py3"), 187db96d56Sopenharmony_ci] 197db96d56Sopenharmony_ci 207db96d56Sopenharmony_ci# Packages bundled in ensurepip._bundled have wheel_name set. 217db96d56Sopenharmony_ci# Packages from WHEEL_PKG_DIR have wheel_path set. 227db96d56Sopenharmony_ci_Package = collections.namedtuple('Package', 237db96d56Sopenharmony_ci ('version', 'wheel_name', 'wheel_path')) 247db96d56Sopenharmony_ci 257db96d56Sopenharmony_ci# Directory of system wheel packages. Some Linux distribution packaging 267db96d56Sopenharmony_ci# policies recommend against bundling dependencies. For example, Fedora 277db96d56Sopenharmony_ci# installs wheel packages in the /usr/share/python-wheels/ directory and don't 287db96d56Sopenharmony_ci# install the ensurepip._bundled package. 297db96d56Sopenharmony_ci_WHEEL_PKG_DIR = sysconfig.get_config_var('WHEEL_PKG_DIR') 307db96d56Sopenharmony_ci 317db96d56Sopenharmony_ci 327db96d56Sopenharmony_cidef _find_packages(path): 337db96d56Sopenharmony_ci packages = {} 347db96d56Sopenharmony_ci try: 357db96d56Sopenharmony_ci filenames = os.listdir(path) 367db96d56Sopenharmony_ci except OSError: 377db96d56Sopenharmony_ci # Ignore: path doesn't exist or permission error 387db96d56Sopenharmony_ci filenames = () 397db96d56Sopenharmony_ci # Make the code deterministic if a directory contains multiple wheel files 407db96d56Sopenharmony_ci # of the same package, but don't attempt to implement correct version 417db96d56Sopenharmony_ci # comparison since this case should not happen. 427db96d56Sopenharmony_ci filenames = sorted(filenames) 437db96d56Sopenharmony_ci for filename in filenames: 447db96d56Sopenharmony_ci # filename is like 'pip-21.2.4-py3-none-any.whl' 457db96d56Sopenharmony_ci if not filename.endswith(".whl"): 467db96d56Sopenharmony_ci continue 477db96d56Sopenharmony_ci for name in _PACKAGE_NAMES: 487db96d56Sopenharmony_ci prefix = name + '-' 497db96d56Sopenharmony_ci if filename.startswith(prefix): 507db96d56Sopenharmony_ci break 517db96d56Sopenharmony_ci else: 527db96d56Sopenharmony_ci continue 537db96d56Sopenharmony_ci 547db96d56Sopenharmony_ci # Extract '21.2.4' from 'pip-21.2.4-py3-none-any.whl' 557db96d56Sopenharmony_ci version = filename.removeprefix(prefix).partition('-')[0] 567db96d56Sopenharmony_ci wheel_path = os.path.join(path, filename) 577db96d56Sopenharmony_ci packages[name] = _Package(version, None, wheel_path) 587db96d56Sopenharmony_ci return packages 597db96d56Sopenharmony_ci 607db96d56Sopenharmony_ci 617db96d56Sopenharmony_cidef _get_packages(): 627db96d56Sopenharmony_ci global _PACKAGES, _WHEEL_PKG_DIR 637db96d56Sopenharmony_ci if _PACKAGES is not None: 647db96d56Sopenharmony_ci return _PACKAGES 657db96d56Sopenharmony_ci 667db96d56Sopenharmony_ci packages = {} 677db96d56Sopenharmony_ci for name, version, py_tag in _PROJECTS: 687db96d56Sopenharmony_ci wheel_name = f"{name}-{version}-{py_tag}-none-any.whl" 697db96d56Sopenharmony_ci packages[name] = _Package(version, wheel_name, None) 707db96d56Sopenharmony_ci if _WHEEL_PKG_DIR: 717db96d56Sopenharmony_ci dir_packages = _find_packages(_WHEEL_PKG_DIR) 727db96d56Sopenharmony_ci # only used the wheel package directory if all packages are found there 737db96d56Sopenharmony_ci if all(name in dir_packages for name in _PACKAGE_NAMES): 747db96d56Sopenharmony_ci packages = dir_packages 757db96d56Sopenharmony_ci _PACKAGES = packages 767db96d56Sopenharmony_ci return packages 777db96d56Sopenharmony_ci_PACKAGES = None 787db96d56Sopenharmony_ci 797db96d56Sopenharmony_ci 807db96d56Sopenharmony_cidef _run_pip(args, additional_paths=None): 817db96d56Sopenharmony_ci # Run the bootstrapping in a subprocess to avoid leaking any state that happens 827db96d56Sopenharmony_ci # after pip has executed. Particularly, this avoids the case when pip holds onto 837db96d56Sopenharmony_ci # the files in *additional_paths*, preventing us to remove them at the end of the 847db96d56Sopenharmony_ci # invocation. 857db96d56Sopenharmony_ci code = f""" 867db96d56Sopenharmony_ciimport runpy 877db96d56Sopenharmony_ciimport sys 887db96d56Sopenharmony_cisys.path = {additional_paths or []} + sys.path 897db96d56Sopenharmony_cisys.argv[1:] = {args} 907db96d56Sopenharmony_cirunpy.run_module("pip", run_name="__main__", alter_sys=True) 917db96d56Sopenharmony_ci""" 927db96d56Sopenharmony_ci 937db96d56Sopenharmony_ci cmd = [ 947db96d56Sopenharmony_ci sys.executable, 957db96d56Sopenharmony_ci '-W', 967db96d56Sopenharmony_ci 'ignore::DeprecationWarning', 977db96d56Sopenharmony_ci '-c', 987db96d56Sopenharmony_ci code, 997db96d56Sopenharmony_ci ] 1007db96d56Sopenharmony_ci if sys.flags.isolated: 1017db96d56Sopenharmony_ci # run code in isolated mode if currently running isolated 1027db96d56Sopenharmony_ci cmd.insert(1, '-I') 1037db96d56Sopenharmony_ci return subprocess.run(cmd, check=True).returncode 1047db96d56Sopenharmony_ci 1057db96d56Sopenharmony_ci 1067db96d56Sopenharmony_cidef version(): 1077db96d56Sopenharmony_ci """ 1087db96d56Sopenharmony_ci Returns a string specifying the bundled version of pip. 1097db96d56Sopenharmony_ci """ 1107db96d56Sopenharmony_ci return _get_packages()['pip'].version 1117db96d56Sopenharmony_ci 1127db96d56Sopenharmony_ci 1137db96d56Sopenharmony_cidef _disable_pip_configuration_settings(): 1147db96d56Sopenharmony_ci # We deliberately ignore all pip environment variables 1157db96d56Sopenharmony_ci # when invoking pip 1167db96d56Sopenharmony_ci # See http://bugs.python.org/issue19734 for details 1177db96d56Sopenharmony_ci keys_to_remove = [k for k in os.environ if k.startswith("PIP_")] 1187db96d56Sopenharmony_ci for k in keys_to_remove: 1197db96d56Sopenharmony_ci del os.environ[k] 1207db96d56Sopenharmony_ci # We also ignore the settings in the default pip configuration file 1217db96d56Sopenharmony_ci # See http://bugs.python.org/issue20053 for details 1227db96d56Sopenharmony_ci os.environ['PIP_CONFIG_FILE'] = os.devnull 1237db96d56Sopenharmony_ci 1247db96d56Sopenharmony_ci 1257db96d56Sopenharmony_cidef bootstrap(*, root=None, upgrade=False, user=False, 1267db96d56Sopenharmony_ci altinstall=False, default_pip=False, 1277db96d56Sopenharmony_ci verbosity=0): 1287db96d56Sopenharmony_ci """ 1297db96d56Sopenharmony_ci Bootstrap pip into the current Python installation (or the given root 1307db96d56Sopenharmony_ci directory). 1317db96d56Sopenharmony_ci 1327db96d56Sopenharmony_ci Note that calling this function will alter both sys.path and os.environ. 1337db96d56Sopenharmony_ci """ 1347db96d56Sopenharmony_ci # Discard the return value 1357db96d56Sopenharmony_ci _bootstrap(root=root, upgrade=upgrade, user=user, 1367db96d56Sopenharmony_ci altinstall=altinstall, default_pip=default_pip, 1377db96d56Sopenharmony_ci verbosity=verbosity) 1387db96d56Sopenharmony_ci 1397db96d56Sopenharmony_ci 1407db96d56Sopenharmony_cidef _bootstrap(*, root=None, upgrade=False, user=False, 1417db96d56Sopenharmony_ci altinstall=False, default_pip=False, 1427db96d56Sopenharmony_ci verbosity=0): 1437db96d56Sopenharmony_ci """ 1447db96d56Sopenharmony_ci Bootstrap pip into the current Python installation (or the given root 1457db96d56Sopenharmony_ci directory). Returns pip command status code. 1467db96d56Sopenharmony_ci 1477db96d56Sopenharmony_ci Note that calling this function will alter both sys.path and os.environ. 1487db96d56Sopenharmony_ci """ 1497db96d56Sopenharmony_ci if altinstall and default_pip: 1507db96d56Sopenharmony_ci raise ValueError("Cannot use altinstall and default_pip together") 1517db96d56Sopenharmony_ci 1527db96d56Sopenharmony_ci sys.audit("ensurepip.bootstrap", root) 1537db96d56Sopenharmony_ci 1547db96d56Sopenharmony_ci _disable_pip_configuration_settings() 1557db96d56Sopenharmony_ci 1567db96d56Sopenharmony_ci # By default, installing pip and setuptools installs all of the 1577db96d56Sopenharmony_ci # following scripts (X.Y == running Python version): 1587db96d56Sopenharmony_ci # 1597db96d56Sopenharmony_ci # pip, pipX, pipX.Y, easy_install, easy_install-X.Y 1607db96d56Sopenharmony_ci # 1617db96d56Sopenharmony_ci # pip 1.5+ allows ensurepip to request that some of those be left out 1627db96d56Sopenharmony_ci if altinstall: 1637db96d56Sopenharmony_ci # omit pip, pipX and easy_install 1647db96d56Sopenharmony_ci os.environ["ENSUREPIP_OPTIONS"] = "altinstall" 1657db96d56Sopenharmony_ci elif not default_pip: 1667db96d56Sopenharmony_ci # omit pip and easy_install 1677db96d56Sopenharmony_ci os.environ["ENSUREPIP_OPTIONS"] = "install" 1687db96d56Sopenharmony_ci 1697db96d56Sopenharmony_ci with tempfile.TemporaryDirectory() as tmpdir: 1707db96d56Sopenharmony_ci # Put our bundled wheels into a temporary directory and construct the 1717db96d56Sopenharmony_ci # additional paths that need added to sys.path 1727db96d56Sopenharmony_ci additional_paths = [] 1737db96d56Sopenharmony_ci for name, package in _get_packages().items(): 1747db96d56Sopenharmony_ci if package.wheel_name: 1757db96d56Sopenharmony_ci # Use bundled wheel package 1767db96d56Sopenharmony_ci wheel_name = package.wheel_name 1777db96d56Sopenharmony_ci wheel_path = resources.files("ensurepip") / "_bundled" / wheel_name 1787db96d56Sopenharmony_ci whl = wheel_path.read_bytes() 1797db96d56Sopenharmony_ci else: 1807db96d56Sopenharmony_ci # Use the wheel package directory 1817db96d56Sopenharmony_ci with open(package.wheel_path, "rb") as fp: 1827db96d56Sopenharmony_ci whl = fp.read() 1837db96d56Sopenharmony_ci wheel_name = os.path.basename(package.wheel_path) 1847db96d56Sopenharmony_ci 1857db96d56Sopenharmony_ci filename = os.path.join(tmpdir, wheel_name) 1867db96d56Sopenharmony_ci with open(filename, "wb") as fp: 1877db96d56Sopenharmony_ci fp.write(whl) 1887db96d56Sopenharmony_ci 1897db96d56Sopenharmony_ci additional_paths.append(filename) 1907db96d56Sopenharmony_ci 1917db96d56Sopenharmony_ci # Construct the arguments to be passed to the pip command 1927db96d56Sopenharmony_ci args = ["install", "--no-cache-dir", "--no-index", "--find-links", tmpdir] 1937db96d56Sopenharmony_ci if root: 1947db96d56Sopenharmony_ci args += ["--root", root] 1957db96d56Sopenharmony_ci if upgrade: 1967db96d56Sopenharmony_ci args += ["--upgrade"] 1977db96d56Sopenharmony_ci if user: 1987db96d56Sopenharmony_ci args += ["--user"] 1997db96d56Sopenharmony_ci if verbosity: 2007db96d56Sopenharmony_ci args += ["-" + "v" * verbosity] 2017db96d56Sopenharmony_ci 2027db96d56Sopenharmony_ci return _run_pip([*args, *_PACKAGE_NAMES], additional_paths) 2037db96d56Sopenharmony_ci 2047db96d56Sopenharmony_cidef _uninstall_helper(*, verbosity=0): 2057db96d56Sopenharmony_ci """Helper to support a clean default uninstall process on Windows 2067db96d56Sopenharmony_ci 2077db96d56Sopenharmony_ci Note that calling this function may alter os.environ. 2087db96d56Sopenharmony_ci """ 2097db96d56Sopenharmony_ci # Nothing to do if pip was never installed, or has been removed 2107db96d56Sopenharmony_ci try: 2117db96d56Sopenharmony_ci import pip 2127db96d56Sopenharmony_ci except ImportError: 2137db96d56Sopenharmony_ci return 2147db96d56Sopenharmony_ci 2157db96d56Sopenharmony_ci # If the installed pip version doesn't match the available one, 2167db96d56Sopenharmony_ci # leave it alone 2177db96d56Sopenharmony_ci available_version = version() 2187db96d56Sopenharmony_ci if pip.__version__ != available_version: 2197db96d56Sopenharmony_ci print(f"ensurepip will only uninstall a matching version " 2207db96d56Sopenharmony_ci f"({pip.__version__!r} installed, " 2217db96d56Sopenharmony_ci f"{available_version!r} available)", 2227db96d56Sopenharmony_ci file=sys.stderr) 2237db96d56Sopenharmony_ci return 2247db96d56Sopenharmony_ci 2257db96d56Sopenharmony_ci _disable_pip_configuration_settings() 2267db96d56Sopenharmony_ci 2277db96d56Sopenharmony_ci # Construct the arguments to be passed to the pip command 2287db96d56Sopenharmony_ci args = ["uninstall", "-y", "--disable-pip-version-check"] 2297db96d56Sopenharmony_ci if verbosity: 2307db96d56Sopenharmony_ci args += ["-" + "v" * verbosity] 2317db96d56Sopenharmony_ci 2327db96d56Sopenharmony_ci return _run_pip([*args, *reversed(_PACKAGE_NAMES)]) 2337db96d56Sopenharmony_ci 2347db96d56Sopenharmony_ci 2357db96d56Sopenharmony_cidef _main(argv=None): 2367db96d56Sopenharmony_ci import argparse 2377db96d56Sopenharmony_ci parser = argparse.ArgumentParser(prog="python -m ensurepip") 2387db96d56Sopenharmony_ci parser.add_argument( 2397db96d56Sopenharmony_ci "--version", 2407db96d56Sopenharmony_ci action="version", 2417db96d56Sopenharmony_ci version="pip {}".format(version()), 2427db96d56Sopenharmony_ci help="Show the version of pip that is bundled with this Python.", 2437db96d56Sopenharmony_ci ) 2447db96d56Sopenharmony_ci parser.add_argument( 2457db96d56Sopenharmony_ci "-v", "--verbose", 2467db96d56Sopenharmony_ci action="count", 2477db96d56Sopenharmony_ci default=0, 2487db96d56Sopenharmony_ci dest="verbosity", 2497db96d56Sopenharmony_ci help=("Give more output. Option is additive, and can be used up to 3 " 2507db96d56Sopenharmony_ci "times."), 2517db96d56Sopenharmony_ci ) 2527db96d56Sopenharmony_ci parser.add_argument( 2537db96d56Sopenharmony_ci "-U", "--upgrade", 2547db96d56Sopenharmony_ci action="store_true", 2557db96d56Sopenharmony_ci default=False, 2567db96d56Sopenharmony_ci help="Upgrade pip and dependencies, even if already installed.", 2577db96d56Sopenharmony_ci ) 2587db96d56Sopenharmony_ci parser.add_argument( 2597db96d56Sopenharmony_ci "--user", 2607db96d56Sopenharmony_ci action="store_true", 2617db96d56Sopenharmony_ci default=False, 2627db96d56Sopenharmony_ci help="Install using the user scheme.", 2637db96d56Sopenharmony_ci ) 2647db96d56Sopenharmony_ci parser.add_argument( 2657db96d56Sopenharmony_ci "--root", 2667db96d56Sopenharmony_ci default=None, 2677db96d56Sopenharmony_ci help="Install everything relative to this alternate root directory.", 2687db96d56Sopenharmony_ci ) 2697db96d56Sopenharmony_ci parser.add_argument( 2707db96d56Sopenharmony_ci "--altinstall", 2717db96d56Sopenharmony_ci action="store_true", 2727db96d56Sopenharmony_ci default=False, 2737db96d56Sopenharmony_ci help=("Make an alternate install, installing only the X.Y versioned " 2747db96d56Sopenharmony_ci "scripts (Default: pipX, pipX.Y, easy_install-X.Y)."), 2757db96d56Sopenharmony_ci ) 2767db96d56Sopenharmony_ci parser.add_argument( 2777db96d56Sopenharmony_ci "--default-pip", 2787db96d56Sopenharmony_ci action="store_true", 2797db96d56Sopenharmony_ci default=False, 2807db96d56Sopenharmony_ci help=("Make a default pip install, installing the unqualified pip " 2817db96d56Sopenharmony_ci "and easy_install in addition to the versioned scripts."), 2827db96d56Sopenharmony_ci ) 2837db96d56Sopenharmony_ci 2847db96d56Sopenharmony_ci args = parser.parse_args(argv) 2857db96d56Sopenharmony_ci 2867db96d56Sopenharmony_ci return _bootstrap( 2877db96d56Sopenharmony_ci root=args.root, 2887db96d56Sopenharmony_ci upgrade=args.upgrade, 2897db96d56Sopenharmony_ci user=args.user, 2907db96d56Sopenharmony_ci verbosity=args.verbosity, 2917db96d56Sopenharmony_ci altinstall=args.altinstall, 2927db96d56Sopenharmony_ci default_pip=args.default_pip, 2937db96d56Sopenharmony_ci ) 294