import os
import subprocess
import sys
import sysconfig
import tempfile
from contextlib import nullcontext
from importlib import resources
from pathlib import Path
from shutil import copy2
__all__ = ["version", "bootstrap"]
_PIP_VERSION = "25.3"
if (_pkg_dir := sysconfig.get_config_var('WHEEL_PKG_DIR')) is not None:
_WHEEL_PKG_DIR = Path(_pkg_dir).resolve()
else:
_WHEEL_PKG_DIR = None
def _find_wheel_pkg_dir_pip():
if _WHEEL_PKG_DIR is None:
return None
dist_matching_wheels = _WHEEL_PKG_DIR.glob('pip-*.whl')
try:
last_matching_dist_wheel = sorted(dist_matching_wheels)[-1]
except IndexError:
return None
return nullcontext(last_matching_dist_wheel)
def _get_pip_whl_path_ctx():
if (alternative_pip_wheel_path := _find_wheel_pkg_dir_pip()) is not None:
return alternative_pip_wheel_path
return resources.as_file(
resources.files('ensurepip')
/ '_bundled'
/ f'pip-{_PIP_VERSION}-py3-none-any.whl'
)
def _get_pip_version():
with _get_pip_whl_path_ctx() as bundled_wheel_path:
wheel_name = bundled_wheel_path.name
return (
wheel_name.
removeprefix('pip-').
partition('-')[0]
)
def _run_pip(args, additional_paths=None):
code = f"""
import runpy
import sys
sys.path = {additional_paths or []} + sys.path
sys.argv[1:] = {args}
runpy.run_module("pip", run_name="__main__", alter_sys=True)
"""
cmd = [
sys.executable,
'-W',
'ignore::DeprecationWarning',
'-c',
code,
]
if sys.flags.isolated:
cmd.insert(1, '-I')
return subprocess.run(cmd, check=True).returncode
def version():
return _get_pip_version()
def _disable_pip_configuration_settings():
keys_to_remove = [k for k in os.environ if k.startswith("PIP_")]
for k in keys_to_remove:
del os.environ[k]
os.environ['PIP_CONFIG_FILE'] = os.devnull
def bootstrap(*, root=None, upgrade=False, user=False,
altinstall=False, default_pip=False,
verbosity=0):
_bootstrap(root=root, upgrade=upgrade, user=user,
altinstall=altinstall, default_pip=default_pip,
verbosity=verbosity)
def _bootstrap(*, root=None, upgrade=False, user=False,
altinstall=False, default_pip=False,
verbosity=0):
if altinstall and default_pip:
raise ValueError("Cannot use altinstall and default_pip together")
sys.audit("ensurepip.bootstrap", root)
_disable_pip_configuration_settings()
if altinstall:
os.environ["ENSUREPIP_OPTIONS"] = "altinstall"
elif not default_pip:
os.environ["ENSUREPIP_OPTIONS"] = "install"
with tempfile.TemporaryDirectory() as tmpdir:
tmpdir_path = Path(tmpdir)
with _get_pip_whl_path_ctx() as bundled_wheel_path:
tmp_wheel_path = tmpdir_path / bundled_wheel_path.name
copy2(bundled_wheel_path, tmp_wheel_path)
args = ["install", "--no-cache-dir", "--no-index", "--find-links", tmpdir]
if root:
args += ["--root", root]
if upgrade:
args += ["--upgrade"]
if user:
args += ["--user"]
if verbosity:
args += ["-" + "v" * verbosity]
return _run_pip([*args, "pip"], [os.fsdecode(tmp_wheel_path)])
def _uninstall_helper(*, verbosity=0):
try:
import pip
except ImportError:
return
available_version = version()
if pip.__version__ != available_version:
print(f"ensurepip will only uninstall a matching version "
f"({pip.__version__!r} installed, "
f"{available_version!r} available)",
file=sys.stderr)
return
_disable_pip_configuration_settings()
args = ["uninstall", "-y", "--disable-pip-version-check"]
if verbosity:
args += ["-" + "v" * verbosity]
return _run_pip([*args, "pip"])
def _main(argv=None):
import argparse
parser = argparse.ArgumentParser(color=True)
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)."),
)
parser.add_argument(
"--default-pip",
action="store_true",
default=False,
help=("Make a default pip install, installing the unqualified pip "
"in addition to the versioned scripts."),
)
args = parser.parse_args(argv)
return _bootstrap(
root=args.root,
upgrade=args.upgrade,
user=args.user,
verbosity=args.verbosity,
altinstall=args.altinstall,
default_pip=args.default_pip,
)