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