comparison env/lib/python3.9/site-packages/pip/_internal/cli/req_command.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 """Contains the Command base classes that depend on PipSession.
2
3 The classes in this module are in a separate module so the commands not
4 needing download / PackageFinder capability don't unnecessarily import the
5 PackageFinder machinery and all its vendored dependencies, etc.
6 """
7
8 import logging
9 import os
10 from functools import partial
11
12 from pip._internal.cli import cmdoptions
13 from pip._internal.cli.base_command import Command
14 from pip._internal.cli.command_context import CommandContextMixIn
15 from pip._internal.exceptions import CommandError, PreviousBuildDirError
16 from pip._internal.index.collector import LinkCollector
17 from pip._internal.index.package_finder import PackageFinder
18 from pip._internal.models.selection_prefs import SelectionPreferences
19 from pip._internal.network.session import PipSession
20 from pip._internal.operations.prepare import RequirementPreparer
21 from pip._internal.req.constructors import (
22 install_req_from_editable,
23 install_req_from_line,
24 install_req_from_parsed_requirement,
25 install_req_from_req_string,
26 )
27 from pip._internal.req.req_file import parse_requirements
28 from pip._internal.self_outdated_check import pip_self_version_check
29 from pip._internal.utils.temp_dir import tempdir_kinds
30 from pip._internal.utils.typing import MYPY_CHECK_RUNNING
31
32 if MYPY_CHECK_RUNNING:
33 from optparse import Values
34 from typing import Any, List, Optional, Tuple
35
36 from pip._internal.cache import WheelCache
37 from pip._internal.models.target_python import TargetPython
38 from pip._internal.req.req_install import InstallRequirement
39 from pip._internal.req.req_tracker import RequirementTracker
40 from pip._internal.resolution.base import BaseResolver
41 from pip._internal.utils.temp_dir import TempDirectory, TempDirectoryTypeRegistry
42
43
44 logger = logging.getLogger(__name__)
45
46
47 class SessionCommandMixin(CommandContextMixIn):
48
49 """
50 A class mixin for command classes needing _build_session().
51 """
52 def __init__(self):
53 # type: () -> None
54 super().__init__()
55 self._session = None # Optional[PipSession]
56
57 @classmethod
58 def _get_index_urls(cls, options):
59 # type: (Values) -> Optional[List[str]]
60 """Return a list of index urls from user-provided options."""
61 index_urls = []
62 if not getattr(options, "no_index", False):
63 url = getattr(options, "index_url", None)
64 if url:
65 index_urls.append(url)
66 urls = getattr(options, "extra_index_urls", None)
67 if urls:
68 index_urls.extend(urls)
69 # Return None rather than an empty list
70 return index_urls or None
71
72 def get_default_session(self, options):
73 # type: (Values) -> PipSession
74 """Get a default-managed session."""
75 if self._session is None:
76 self._session = self.enter_context(self._build_session(options))
77 # there's no type annotation on requests.Session, so it's
78 # automatically ContextManager[Any] and self._session becomes Any,
79 # then https://github.com/python/mypy/issues/7696 kicks in
80 assert self._session is not None
81 return self._session
82
83 def _build_session(self, options, retries=None, timeout=None):
84 # type: (Values, Optional[int], Optional[int]) -> PipSession
85 assert not options.cache_dir or os.path.isabs(options.cache_dir)
86 session = PipSession(
87 cache=(
88 os.path.join(options.cache_dir, "http")
89 if options.cache_dir else None
90 ),
91 retries=retries if retries is not None else options.retries,
92 trusted_hosts=options.trusted_hosts,
93 index_urls=self._get_index_urls(options),
94 )
95
96 # Handle custom ca-bundles from the user
97 if options.cert:
98 session.verify = options.cert
99
100 # Handle SSL client certificate
101 if options.client_cert:
102 session.cert = options.client_cert
103
104 # Handle timeouts
105 if options.timeout or timeout:
106 session.timeout = (
107 timeout if timeout is not None else options.timeout
108 )
109
110 # Handle configured proxies
111 if options.proxy:
112 session.proxies = {
113 "http": options.proxy,
114 "https": options.proxy,
115 }
116
117 # Determine if we can prompt the user for authentication or not
118 session.auth.prompting = not options.no_input
119
120 return session
121
122
123 class IndexGroupCommand(Command, SessionCommandMixin):
124
125 """
126 Abstract base class for commands with the index_group options.
127
128 This also corresponds to the commands that permit the pip version check.
129 """
130
131 def handle_pip_version_check(self, options):
132 # type: (Values) -> None
133 """
134 Do the pip version check if not disabled.
135
136 This overrides the default behavior of not doing the check.
137 """
138 # Make sure the index_group options are present.
139 assert hasattr(options, 'no_index')
140
141 if options.disable_pip_version_check or options.no_index:
142 return
143
144 # Otherwise, check if we're using the latest version of pip available.
145 session = self._build_session(
146 options,
147 retries=0,
148 timeout=min(5, options.timeout)
149 )
150 with session:
151 pip_self_version_check(session, options)
152
153
154 KEEPABLE_TEMPDIR_TYPES = [
155 tempdir_kinds.BUILD_ENV,
156 tempdir_kinds.EPHEM_WHEEL_CACHE,
157 tempdir_kinds.REQ_BUILD,
158 ]
159
160
161 def with_cleanup(func):
162 # type: (Any) -> Any
163 """Decorator for common logic related to managing temporary
164 directories.
165 """
166 def configure_tempdir_registry(registry):
167 # type: (TempDirectoryTypeRegistry) -> None
168 for t in KEEPABLE_TEMPDIR_TYPES:
169 registry.set_delete(t, False)
170
171 def wrapper(self, options, args):
172 # type: (RequirementCommand, Values, List[Any]) -> Optional[int]
173 assert self.tempdir_registry is not None
174 if options.no_clean:
175 configure_tempdir_registry(self.tempdir_registry)
176
177 try:
178 return func(self, options, args)
179 except PreviousBuildDirError:
180 # This kind of conflict can occur when the user passes an explicit
181 # build directory with a pre-existing folder. In that case we do
182 # not want to accidentally remove it.
183 configure_tempdir_registry(self.tempdir_registry)
184 raise
185
186 return wrapper
187
188
189 class RequirementCommand(IndexGroupCommand):
190
191 def __init__(self, *args, **kw):
192 # type: (Any, Any) -> None
193 super().__init__(*args, **kw)
194
195 self.cmd_opts.add_option(cmdoptions.no_clean())
196
197 @staticmethod
198 def determine_resolver_variant(options):
199 # type: (Values) -> str
200 """Determines which resolver should be used, based on the given options."""
201 if "legacy-resolver" in options.deprecated_features_enabled:
202 return "legacy"
203
204 return "2020-resolver"
205
206 @classmethod
207 def make_requirement_preparer(
208 cls,
209 temp_build_dir, # type: TempDirectory
210 options, # type: Values
211 req_tracker, # type: RequirementTracker
212 session, # type: PipSession
213 finder, # type: PackageFinder
214 use_user_site, # type: bool
215 download_dir=None, # type: str
216 ):
217 # type: (...) -> RequirementPreparer
218 """
219 Create a RequirementPreparer instance for the given parameters.
220 """
221 temp_build_dir_path = temp_build_dir.path
222 assert temp_build_dir_path is not None
223
224 resolver_variant = cls.determine_resolver_variant(options)
225 if resolver_variant == "2020-resolver":
226 lazy_wheel = 'fast-deps' in options.features_enabled
227 if lazy_wheel:
228 logger.warning(
229 'pip is using lazily downloaded wheels using HTTP '
230 'range requests to obtain dependency information. '
231 'This experimental feature is enabled through '
232 '--use-feature=fast-deps and it is not ready for '
233 'production.'
234 )
235 else:
236 lazy_wheel = False
237 if 'fast-deps' in options.features_enabled:
238 logger.warning(
239 'fast-deps has no effect when used with the legacy resolver.'
240 )
241
242 return RequirementPreparer(
243 build_dir=temp_build_dir_path,
244 src_dir=options.src_dir,
245 download_dir=download_dir,
246 build_isolation=options.build_isolation,
247 req_tracker=req_tracker,
248 session=session,
249 progress_bar=options.progress_bar,
250 finder=finder,
251 require_hashes=options.require_hashes,
252 use_user_site=use_user_site,
253 lazy_wheel=lazy_wheel,
254 )
255
256 @classmethod
257 def make_resolver(
258 cls,
259 preparer, # type: RequirementPreparer
260 finder, # type: PackageFinder
261 options, # type: Values
262 wheel_cache=None, # type: Optional[WheelCache]
263 use_user_site=False, # type: bool
264 ignore_installed=True, # type: bool
265 ignore_requires_python=False, # type: bool
266 force_reinstall=False, # type: bool
267 upgrade_strategy="to-satisfy-only", # type: str
268 use_pep517=None, # type: Optional[bool]
269 py_version_info=None, # type: Optional[Tuple[int, ...]]
270 ):
271 # type: (...) -> BaseResolver
272 """
273 Create a Resolver instance for the given parameters.
274 """
275 make_install_req = partial(
276 install_req_from_req_string,
277 isolated=options.isolated_mode,
278 use_pep517=use_pep517,
279 )
280 resolver_variant = cls.determine_resolver_variant(options)
281 # The long import name and duplicated invocation is needed to convince
282 # Mypy into correctly typechecking. Otherwise it would complain the
283 # "Resolver" class being redefined.
284 if resolver_variant == "2020-resolver":
285 import pip._internal.resolution.resolvelib.resolver
286
287 return pip._internal.resolution.resolvelib.resolver.Resolver(
288 preparer=preparer,
289 finder=finder,
290 wheel_cache=wheel_cache,
291 make_install_req=make_install_req,
292 use_user_site=use_user_site,
293 ignore_dependencies=options.ignore_dependencies,
294 ignore_installed=ignore_installed,
295 ignore_requires_python=ignore_requires_python,
296 force_reinstall=force_reinstall,
297 upgrade_strategy=upgrade_strategy,
298 py_version_info=py_version_info,
299 )
300 import pip._internal.resolution.legacy.resolver
301 return pip._internal.resolution.legacy.resolver.Resolver(
302 preparer=preparer,
303 finder=finder,
304 wheel_cache=wheel_cache,
305 make_install_req=make_install_req,
306 use_user_site=use_user_site,
307 ignore_dependencies=options.ignore_dependencies,
308 ignore_installed=ignore_installed,
309 ignore_requires_python=ignore_requires_python,
310 force_reinstall=force_reinstall,
311 upgrade_strategy=upgrade_strategy,
312 py_version_info=py_version_info,
313 )
314
315 def get_requirements(
316 self,
317 args, # type: List[str]
318 options, # type: Values
319 finder, # type: PackageFinder
320 session, # type: PipSession
321 ):
322 # type: (...) -> List[InstallRequirement]
323 """
324 Parse command-line arguments into the corresponding requirements.
325 """
326 requirements = [] # type: List[InstallRequirement]
327 for filename in options.constraints:
328 for parsed_req in parse_requirements(
329 filename,
330 constraint=True, finder=finder, options=options,
331 session=session):
332 req_to_add = install_req_from_parsed_requirement(
333 parsed_req,
334 isolated=options.isolated_mode,
335 user_supplied=False,
336 )
337 requirements.append(req_to_add)
338
339 for req in args:
340 req_to_add = install_req_from_line(
341 req, None, isolated=options.isolated_mode,
342 use_pep517=options.use_pep517,
343 user_supplied=True,
344 )
345 requirements.append(req_to_add)
346
347 for req in options.editables:
348 req_to_add = install_req_from_editable(
349 req,
350 user_supplied=True,
351 isolated=options.isolated_mode,
352 use_pep517=options.use_pep517,
353 )
354 requirements.append(req_to_add)
355
356 # NOTE: options.require_hashes may be set if --require-hashes is True
357 for filename in options.requirements:
358 for parsed_req in parse_requirements(
359 filename,
360 finder=finder, options=options, session=session):
361 req_to_add = install_req_from_parsed_requirement(
362 parsed_req,
363 isolated=options.isolated_mode,
364 use_pep517=options.use_pep517,
365 user_supplied=True,
366 )
367 requirements.append(req_to_add)
368
369 # If any requirement has hash options, enable hash checking.
370 if any(req.has_hash_options for req in requirements):
371 options.require_hashes = True
372
373 if not (args or options.editables or options.requirements):
374 opts = {'name': self.name}
375 if options.find_links:
376 raise CommandError(
377 'You must give at least one requirement to {name} '
378 '(maybe you meant "pip {name} {links}"?)'.format(
379 **dict(opts, links=' '.join(options.find_links))))
380 else:
381 raise CommandError(
382 'You must give at least one requirement to {name} '
383 '(see "pip help {name}")'.format(**opts))
384
385 return requirements
386
387 @staticmethod
388 def trace_basic_info(finder):
389 # type: (PackageFinder) -> None
390 """
391 Trace basic information about the provided objects.
392 """
393 # Display where finder is looking for packages
394 search_scope = finder.search_scope
395 locations = search_scope.get_formatted_locations()
396 if locations:
397 logger.info(locations)
398
399 def _build_package_finder(
400 self,
401 options, # type: Values
402 session, # type: PipSession
403 target_python=None, # type: Optional[TargetPython]
404 ignore_requires_python=None, # type: Optional[bool]
405 ):
406 # type: (...) -> PackageFinder
407 """
408 Create a package finder appropriate to this requirement command.
409
410 :param ignore_requires_python: Whether to ignore incompatible
411 "Requires-Python" values in links. Defaults to False.
412 """
413 link_collector = LinkCollector.create(session, options=options)
414 selection_prefs = SelectionPreferences(
415 allow_yanked=True,
416 format_control=options.format_control,
417 allow_all_prereleases=options.pre,
418 prefer_binary=options.prefer_binary,
419 ignore_requires_python=ignore_requires_python,
420 )
421
422 return PackageFinder.create(
423 link_collector=link_collector,
424 selection_prefs=selection_prefs,
425 target_python=target_python,
426 )