1import os
2import os.path
3import pkgutil
4import sys
5import tempfile
6
7
8__all__ = ["version", "bootstrap"]
9
10
11_SETUPTOOLS_VERSION = "28.8.0"
12
13_PIP_VERSION = "9.0.1"
14
15_PROJECTS = [
16    ("setuptools", _SETUPTOOLS_VERSION),
17    ("pip", _PIP_VERSION),
18]
19
20
21def _run_pip(args, additional_paths=None):
22    # Add our bundled software to the sys.path so we can import it
23    if additional_paths is not None:
24        sys.path = additional_paths + sys.path
25
26    # Install the bundled software
27    import pip
28    pip.main(args)
29
30
31def version():
32    """
33    Returns a string specifying the bundled version of pip.
34    """
35    return _PIP_VERSION
36
37def _disable_pip_configuration_settings():
38    # We deliberately ignore all pip environment variables
39    # when invoking pip
40    # See http://bugs.python.org/issue19734 for details
41    keys_to_remove = [k for k in os.environ if k.startswith("PIP_")]
42    for k in keys_to_remove:
43        del os.environ[k]
44    # We also ignore the settings in the default pip configuration file
45    # See http://bugs.python.org/issue20053 for details
46    os.environ['PIP_CONFIG_FILE'] = os.devnull
47
48
49def bootstrap(*, root=None, upgrade=False, user=False,
50              altinstall=False, default_pip=False,
51              verbosity=0):
52    """
53    Bootstrap pip into the current Python installation (or the given root
54    directory).
55
56    Note that calling this function will alter both sys.path and os.environ.
57    """
58    if altinstall and default_pip:
59        raise ValueError("Cannot use altinstall and default_pip together")
60
61    _disable_pip_configuration_settings()
62
63    # By default, installing pip and setuptools installs all of the
64    # following scripts (X.Y == running Python version):
65    #
66    #   pip, pipX, pipX.Y, easy_install, easy_install-X.Y
67    #
68    # pip 1.5+ allows ensurepip to request that some of those be left out
69    if altinstall:
70        # omit pip, pipX and easy_install
71        os.environ["ENSUREPIP_OPTIONS"] = "altinstall"
72    elif not default_pip:
73        # omit pip and easy_install
74        os.environ["ENSUREPIP_OPTIONS"] = "install"
75
76    with tempfile.TemporaryDirectory() as tmpdir:
77        # Put our bundled wheels into a temporary directory and construct the
78        # additional paths that need added to sys.path
79        additional_paths = []
80        for project, version in _PROJECTS:
81            wheel_name = "{}-{}-py2.py3-none-any.whl".format(project, version)
82            whl = pkgutil.get_data(
83                "ensurepip",
84                "_bundled/{}".format(wheel_name),
85            )
86            with open(os.path.join(tmpdir, wheel_name), "wb") as fp:
87                fp.write(whl)
88
89            additional_paths.append(os.path.join(tmpdir, wheel_name))
90
91        # Construct the arguments to be passed to the pip command
92        args = ["install", "--no-index", "--find-links", tmpdir]
93        if root:
94            args += ["--root", root]
95        if upgrade:
96            args += ["--upgrade"]
97        if user:
98            args += ["--user"]
99        if verbosity:
100            args += ["-" + "v" * verbosity]
101
102        _run_pip(args + [p[0] for p in _PROJECTS], additional_paths)
103
104def _uninstall_helper(*, verbosity=0):
105    """Helper to support a clean default uninstall process on Windows
106
107    Note that calling this function may alter os.environ.
108    """
109    # Nothing to do if pip was never installed, or has been removed
110    try:
111        import pip
112    except ImportError:
113        return
114
115    # If the pip version doesn't match the bundled one, leave it alone
116    if pip.__version__ != _PIP_VERSION:
117        msg = ("ensurepip will only uninstall a matching version "
118               "({!r} installed, {!r} bundled)")
119        print(msg.format(pip.__version__, _PIP_VERSION), file=sys.stderr)
120        return
121
122    _disable_pip_configuration_settings()
123
124    # Construct the arguments to be passed to the pip command
125    args = ["uninstall", "-y", "--disable-pip-version-check"]
126    if verbosity:
127        args += ["-" + "v" * verbosity]
128
129    _run_pip(args + [p[0] for p in reversed(_PROJECTS)])
130
131
132def _main(argv=None):
133    import argparse
134    parser = argparse.ArgumentParser(prog="python -m ensurepip")
135    parser.add_argument(
136        "--version",
137        action="version",
138        version="pip {}".format(version()),
139        help="Show the version of pip that is bundled with this Python.",
140    )
141    parser.add_argument(
142        "-v", "--verbose",
143        action="count",
144        default=0,
145        dest="verbosity",
146        help=("Give more output. Option is additive, and can be used up to 3 "
147              "times."),
148    )
149    parser.add_argument(
150        "-U", "--upgrade",
151        action="store_true",
152        default=False,
153        help="Upgrade pip and dependencies, even if already installed.",
154    )
155    parser.add_argument(
156        "--user",
157        action="store_true",
158        default=False,
159        help="Install using the user scheme.",
160    )
161    parser.add_argument(
162        "--root",
163        default=None,
164        help="Install everything relative to this alternate root directory.",
165    )
166    parser.add_argument(
167        "--altinstall",
168        action="store_true",
169        default=False,
170        help=("Make an alternate install, installing only the X.Y versioned"
171              "scripts (Default: pipX, pipX.Y, easy_install-X.Y)"),
172    )
173    parser.add_argument(
174        "--default-pip",
175        action="store_true",
176        default=False,
177        help=("Make a default pip install, installing the unqualified pip "
178              "and easy_install in addition to the versioned scripts"),
179    )
180
181    args = parser.parse_args(argv)
182
183    bootstrap(
184        root=args.root,
185        upgrade=args.upgrade,
186        user=args.user,
187        verbosity=args.verbosity,
188        altinstall=args.altinstall,
189        default_pip=args.default_pip,
190    )
191