• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import os
2import os.path
3import pkgutil
4import sys
5import tempfile
6
7
8__all__ = ["version", "bootstrap"]
9
10
11_SETUPTOOLS_VERSION = "41.2.0"
12
13_PIP_VERSION = "19.2.3"
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._internal
28    return pip._internal.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    # Discard the return value
59    _bootstrap(root=root, upgrade=upgrade, user=user,
60               altinstall=altinstall, default_pip=default_pip,
61               verbosity=verbosity)
62
63
64def _bootstrap(*, root=None, upgrade=False, user=False,
65              altinstall=False, default_pip=False,
66              verbosity=0):
67    """
68    Bootstrap pip into the current Python installation (or the given root
69    directory). Returns pip command status code.
70
71    Note that calling this function will alter both sys.path and os.environ.
72    """
73    if altinstall and default_pip:
74        raise ValueError("Cannot use altinstall and default_pip together")
75
76    sys.audit("ensurepip.bootstrap", root)
77
78    _disable_pip_configuration_settings()
79
80    # By default, installing pip and setuptools installs all of the
81    # following scripts (X.Y == running Python version):
82    #
83    #   pip, pipX, pipX.Y, easy_install, easy_install-X.Y
84    #
85    # pip 1.5+ allows ensurepip to request that some of those be left out
86    if altinstall:
87        # omit pip, pipX and easy_install
88        os.environ["ENSUREPIP_OPTIONS"] = "altinstall"
89    elif not default_pip:
90        # omit pip and easy_install
91        os.environ["ENSUREPIP_OPTIONS"] = "install"
92
93    with tempfile.TemporaryDirectory() as tmpdir:
94        # Put our bundled wheels into a temporary directory and construct the
95        # additional paths that need added to sys.path
96        additional_paths = []
97        for project, version in _PROJECTS:
98            wheel_name = "{}-{}-py2.py3-none-any.whl".format(project, version)
99            whl = pkgutil.get_data(
100                "ensurepip",
101                "_bundled/{}".format(wheel_name),
102            )
103            with open(os.path.join(tmpdir, wheel_name), "wb") as fp:
104                fp.write(whl)
105
106            additional_paths.append(os.path.join(tmpdir, wheel_name))
107
108        # Construct the arguments to be passed to the pip command
109        args = ["install", "--no-index", "--find-links", tmpdir]
110        if root:
111            args += ["--root", root]
112        if upgrade:
113            args += ["--upgrade"]
114        if user:
115            args += ["--user"]
116        if verbosity:
117            args += ["-" + "v" * verbosity]
118
119        return _run_pip(args + [p[0] for p in _PROJECTS], additional_paths)
120
121def _uninstall_helper(*, verbosity=0):
122    """Helper to support a clean default uninstall process on Windows
123
124    Note that calling this function may alter os.environ.
125    """
126    # Nothing to do if pip was never installed, or has been removed
127    try:
128        import pip
129    except ImportError:
130        return
131
132    # If the pip version doesn't match the bundled one, leave it alone
133    if pip.__version__ != _PIP_VERSION:
134        msg = ("ensurepip will only uninstall a matching version "
135               "({!r} installed, {!r} bundled)")
136        print(msg.format(pip.__version__, _PIP_VERSION), file=sys.stderr)
137        return
138
139    _disable_pip_configuration_settings()
140
141    # Construct the arguments to be passed to the pip command
142    args = ["uninstall", "-y", "--disable-pip-version-check"]
143    if verbosity:
144        args += ["-" + "v" * verbosity]
145
146    return _run_pip(args + [p[0] for p in reversed(_PROJECTS)])
147
148
149def _main(argv=None):
150    import argparse
151    parser = argparse.ArgumentParser(prog="python -m ensurepip")
152    parser.add_argument(
153        "--version",
154        action="version",
155        version="pip {}".format(version()),
156        help="Show the version of pip that is bundled with this Python.",
157    )
158    parser.add_argument(
159        "-v", "--verbose",
160        action="count",
161        default=0,
162        dest="verbosity",
163        help=("Give more output. Option is additive, and can be used up to 3 "
164              "times."),
165    )
166    parser.add_argument(
167        "-U", "--upgrade",
168        action="store_true",
169        default=False,
170        help="Upgrade pip and dependencies, even if already installed.",
171    )
172    parser.add_argument(
173        "--user",
174        action="store_true",
175        default=False,
176        help="Install using the user scheme.",
177    )
178    parser.add_argument(
179        "--root",
180        default=None,
181        help="Install everything relative to this alternate root directory.",
182    )
183    parser.add_argument(
184        "--altinstall",
185        action="store_true",
186        default=False,
187        help=("Make an alternate install, installing only the X.Y versioned "
188              "scripts (Default: pipX, pipX.Y, easy_install-X.Y)."),
189    )
190    parser.add_argument(
191        "--default-pip",
192        action="store_true",
193        default=False,
194        help=("Make a default pip install, installing the unqualified pip "
195              "and easy_install in addition to the versioned scripts."),
196    )
197
198    args = parser.parse_args(argv)
199
200    return _bootstrap(
201        root=args.root,
202        upgrade=args.upgrade,
203        user=args.user,
204        verbosity=args.verbosity,
205        altinstall=args.altinstall,
206        default_pip=args.default_pip,
207    )
208