1"""
2Backports and helper functionality to support using new functionality.
3"""
4
5import atexit
6import contextlib
7import functools
8import inspect
9import os
10import re
11import sys
12import types
13
14from packaging import specifiers
15
16from .environment import MYPY_RUNNING
17from .utils import (
18    call_function_with_correct_args,
19    filter_allowed_args,
20    get_allowed_args,
21    get_method_args,
22    nullcontext,
23    suppress_setattr,
24)
25
26if sys.version_info[:2] < (3, 5):
27    from backports.tempfile import TemporaryDirectory
28else:
29    from tempfile import TemporaryDirectory
30
31from contextlib import ExitStack
32
33if MYPY_RUNNING:
34    from optparse import Values
35    from typing import (
36        Any,
37        Callable,
38        Dict,
39        Generator,
40        Generic,
41        Iterable,
42        Iterator,
43        List,
44        Optional,
45        Tuple,
46        Type,
47        TypeVar,
48        Union,
49    )
50
51    from requests import Session
52
53    from .utils import TShim, TShimmedFunc, TShimmedPath
54
55    TFinder = TypeVar("TFinder")
56    TResolver = TypeVar("TResolver")
57    TReqTracker = TypeVar("TReqTracker")
58    TReqSet = TypeVar("TReqSet")
59    TLink = TypeVar("TLink")
60    TSession = TypeVar("TSession", bound=Session)
61    TCommand = TypeVar("TCommand", covariant=True)
62    TCommandInstance = TypeVar("TCommandInstance")
63    TCmdDict = Dict[str, Union[Tuple[str, str, str], TCommandInstance]]
64    TInstallRequirement = TypeVar("TInstallRequirement")
65    TFormatControl = TypeVar("TFormatControl")
66    TShimmedCmdDict = Union[TShim, TCmdDict]
67    TWheelCache = TypeVar("TWheelCache")
68    TPreparer = TypeVar("TPreparer")
69
70
71class SearchScope(object):
72    def __init__(self, find_links=None, index_urls=None):
73        self.index_urls = index_urls if index_urls else []
74        self.find_links = find_links
75
76    @classmethod
77    def create(cls, find_links=None, index_urls=None):
78        if not index_urls:
79            index_urls = ["https://pypi.org/simple"]
80        return cls(find_links=find_links, index_urls=index_urls)
81
82
83class SelectionPreferences(object):
84    def __init__(
85        self,
86        allow_yanked=True,
87        allow_all_prereleases=False,
88        format_control=None,
89        prefer_binary=False,
90        ignore_requires_python=False,
91    ):
92        self.allow_yanked = allow_yanked
93        self.allow_all_prereleases = allow_all_prereleases
94        self.format_control = format_control
95        self.prefer_binary = prefer_binary
96        self.ignore_requires_python = ignore_requires_python
97
98
99class TargetPython(object):
100    fallback_get_tags = None  # type: Optional[TShimmedFunc]
101
102    def __init__(
103        self,
104        platform=None,  # type: Optional[str]
105        py_version_info=None,  # type: Optional[Tuple[int, ...]]
106        abi=None,  # type: Optional[str]
107        implementation=None,  # type: Optional[str]
108    ):
109        # type: (...) -> None
110        self._given_py_version_info = py_version_info
111        if py_version_info is None:
112            py_version_info = sys.version_info[:3]
113        elif len(py_version_info) < 3:
114            py_version_info += (3 - len(py_version_info)) * (0,)
115        else:
116            py_version_info = py_version_info[:3]
117        py_version = ".".join(map(str, py_version_info[:2]))
118        self.abi = abi
119        self.implementation = implementation
120        self.platform = platform
121        self.py_version = py_version
122        self.py_version_info = py_version_info
123        self._valid_tags = None
124
125    def get_tags(self):
126        if self._valid_tags is None and self.fallback_get_tags:
127            fallback_func = resolve_possible_shim(self.fallback_get_tags)
128            versions = None
129            if self._given_py_version_info:
130                versions = ["".join(map(str, self._given_py_version_info[:2]))]
131            self._valid_tags = fallback_func(
132                versions=versions,
133                platform=self.platform,
134                abi=self.abi,
135                impl=self.implementation,
136            )
137        return self._valid_tags
138
139
140class CandidatePreferences(object):
141    def __init__(self, prefer_binary=False, allow_all_prereleases=False):
142        self.prefer_binary = prefer_binary
143        self.allow_all_prereleases = allow_all_prereleases
144
145
146class LinkCollector(object):
147    def __init__(self, session=None, search_scope=None):
148        self.session = session
149        self.search_scope = search_scope
150
151
152class CandidateEvaluator(object):
153    @classmethod
154    def create(
155        cls,
156        project_name,  # type: str
157        target_python=None,  # type: Optional[TargetPython]
158        prefer_binary=False,  # type: bool
159        allow_all_prereleases=False,  # type: bool
160        specifier=None,  # type: Optional[specifiers.BaseSpecifier]
161        hashes=None,  # type: Optional[Any]
162    ):
163        if target_python is None:
164            target_python = TargetPython()
165        if specifier is None:
166            specifier = specifiers.SpecifierSet()
167
168        supported_tags = target_python.get_tags()
169
170        return cls(
171            project_name=project_name,
172            supported_tags=supported_tags,
173            specifier=specifier,
174            prefer_binary=prefer_binary,
175            allow_all_prereleases=allow_all_prereleases,
176            hashes=hashes,
177        )
178
179    def __init__(
180        self,
181        project_name,  # type: str
182        supported_tags,  # type: List[Any]
183        specifier,  # type: specifiers.BaseSpecifier
184        prefer_binary=False,  # type: bool
185        allow_all_prereleases=False,  # type: bool
186        hashes=None,  # type: Optional[Any]
187    ):
188        self._allow_all_prereleases = allow_all_prereleases
189        self._hashes = hashes
190        self._prefer_binary = prefer_binary
191        self._project_name = project_name
192        self._specifier = specifier
193        self._supported_tags = supported_tags
194
195
196class LinkEvaluator(object):
197    def __init__(
198        self,
199        allow_yanked,
200        project_name,
201        canonical_name,
202        formats,
203        target_python,
204        ignore_requires_python=False,
205        ignore_compatibility=True,
206    ):
207        self._allow_yanked = allow_yanked
208        self._canonical_name = canonical_name
209        self._ignore_requires_python = ignore_requires_python
210        self._formats = formats
211        self._target_python = target_python
212        self._ignore_compatibility = ignore_compatibility
213
214        self.project_name = project_name
215
216
217class InvalidWheelFilename(Exception):
218    """Wheel Filename is Invalid"""
219
220
221class Wheel(object):
222    wheel_file_re = re.compile(
223        r"""^(?P<namever>(?P<name>.+?)-(?P<ver>.*?))
224        ((-(?P<build>\d[^-]*?))?-(?P<pyver>.+?)-(?P<abi>.+?)-(?P<plat>.+?)
225        \.whl|\.dist-info)$""",
226        re.VERBOSE,
227    )
228
229    def __init__(self, filename):
230        # type: (str) -> None
231        wheel_info = self.wheel_file_re.match(filename)
232        if not wheel_info:
233            raise InvalidWheelFilename("%s is not a valid wheel filename." % filename)
234        self.filename = filename
235        self.name = wheel_info.group("name").replace("_", "-")
236        # we'll assume "_" means "-" due to wheel naming scheme
237        # (https://github.com/pypa/pip/issues/1150)
238        self.version = wheel_info.group("ver").replace("_", "-")
239        self.build_tag = wheel_info.group("build")
240        self.pyversions = wheel_info.group("pyver").split(".")
241        self.abis = wheel_info.group("abi").split(".")
242        self.plats = wheel_info.group("plat").split(".")
243
244        # All the tag combinations from this file
245        self.file_tags = {
246            (x, y, z) for x in self.pyversions for y in self.abis for z in self.plats
247        }
248
249    def get_formatted_file_tags(self):
250        # type: () -> List[str]
251        """
252        Return the wheel's tags as a sorted list of strings.
253        """
254        return sorted("-".join(tag) for tag in self.file_tags)
255
256    def support_index_min(self, tags):
257        # type: (List[Any]) -> int
258        """
259        Return the lowest index that one of the wheel's file_tag combinations
260        achieves in the given list of supported tags.
261
262        For example, if there are 8 supported tags and one of the file tags
263        is first in the list, then return 0.
264
265        :param tags: the PEP 425 tags to check the wheel against, in order
266            with most preferred first.
267        :raises ValueError: If none of the wheel's file tags match one of
268            the supported tags.
269        """
270        return min(tags.index(tag) for tag in self.file_tags if tag in tags)
271
272    def supported(self, tags):
273        # type: (List[Any]) -> bool
274        """
275        Return whether the wheel is compatible with one of the given tags.
276
277        :param tags: the PEP 425 tags to check the wheel against.
278        """
279        return not self.file_tags.isdisjoint(tags)
280
281
282def resolve_possible_shim(target):
283    # type: (TShimmedFunc) -> Optional[Union[Type, Callable]]
284    if target is None:
285        return target
286    if getattr(target, "shim", None) and isinstance(
287        target.shim, (types.MethodType, types.FunctionType)
288    ):
289        return target.shim()
290    return target
291
292
293@contextlib.contextmanager
294def temp_environ():
295    """Allow the ability to set os.environ temporarily"""
296    environ = dict(os.environ)
297    try:
298        yield
299    finally:
300        os.environ.clear()
301        os.environ.update(environ)
302
303
304@contextlib.contextmanager
305def get_requirement_tracker(req_tracker_creator=None):
306    # type: (Optional[Callable]) -> Generator[Optional[TReqTracker], None, None]
307    root = os.environ.get("PIP_REQ_TRACKER")
308    if not req_tracker_creator:
309        yield None
310    else:
311        req_tracker_args = []
312        _, required_args = get_method_args(req_tracker_creator.__init__)  # type: ignore
313        with ExitStack() as ctx:
314            if root is None:
315                root = ctx.enter_context(TemporaryDirectory(prefix="req-tracker"))
316                if root:
317                    root = str(root)
318                    ctx.enter_context(temp_environ())
319                    os.environ["PIP_REQ_TRACKER"] = root
320            if required_args is not None and "root" in required_args:
321                req_tracker_args.append(root)
322            with req_tracker_creator(*req_tracker_args) as tracker:
323                yield tracker
324
325
326@contextlib.contextmanager
327def ensure_resolution_dirs(**kwargs):
328    # type: (Any) -> Iterator[Dict[str, Any]]
329    """
330    Ensures that the proper directories are scaffolded and present in the provided kwargs
331    for performing dependency resolution via pip.
332
333    :return: A new kwargs dictionary with scaffolded directories for **build_dir**, **src_dir**,
334        **download_dir**, and **wheel_download_dir** added to the key value pairs.
335    :rtype: Dict[str, Any]
336    """
337    keys = ("build_dir", "src_dir", "download_dir", "wheel_download_dir")
338    if not any(kwargs.get(key) is None for key in keys):
339        yield kwargs
340    else:
341        with TemporaryDirectory(prefix="pip-shims-") as base_dir:
342            for key in keys:
343                if kwargs.get(key) is not None:
344                    continue
345                target = os.path.join(base_dir, key)
346                os.makedirs(target)
347                kwargs[key] = target
348            yield kwargs
349
350
351@contextlib.contextmanager
352def wheel_cache(
353    cache_dir=None,  # type: str
354    format_control=None,  # type: Any
355    wheel_cache_provider=None,  # type: TShimmedFunc
356    format_control_provider=None,  # type: Optional[TShimmedFunc]
357    tempdir_manager_provider=None,  # type: TShimmedFunc
358):
359    tempdir_manager_provider = resolve_possible_shim(tempdir_manager_provider)
360    wheel_cache_provider = resolve_possible_shim(wheel_cache_provider)
361    format_control_provider = resolve_possible_shim(format_control_provider)
362    if not format_control and not format_control_provider:
363        raise TypeError("Format control or provider needed for wheel cache!")
364    if not format_control:
365        format_control = format_control_provider(None, None)
366    with ExitStack() as ctx:
367        ctx.enter_context(tempdir_manager_provider())
368        wheel_cache = wheel_cache_provider(cache_dir, format_control)
369        yield wheel_cache
370
371
372def partial_command(shimmed_path, cmd_mapping=None):
373    # type: (Type, Optional[TShimmedCmdDict]) -> Union[Type[TCommandInstance], functools.partial]
374    """
375    Maps a default set of arguments across all members of a
376    :class:`~pip_shims.models.ShimmedPath` instance, specifically for
377    :class:`~pip._internal.command.Command` instances which need
378    `summary` and `name` arguments.
379
380    :param :class:`~pip_shims.models.ShimmedPath` shimmed_path:  A
381        :class:`~pip_shims.models.ShimmedCollection` instance
382    :param Any cmd_mapping: A reference to use for mapping against, e.g. an
383        import that depends on pip also
384    :return: A dictionary mapping new arguments to their default values
385    :rtype: Dict[str, str]
386    """
387    basecls = shimmed_path.shim()
388    resolved_cmd_mapping = None  # type: Optional[Dict[str, Any]]
389    cmd_mapping = resolve_possible_shim(cmd_mapping)
390    if cmd_mapping is not None and isinstance(cmd_mapping, dict):
391        resolved_cmd_mapping = cmd_mapping.copy()
392    base_args = []  # type: List[str]
393    for root_cls in basecls.mro():
394        if root_cls.__name__ == "Command":
395            _, root_init_args = get_method_args(root_cls.__init__)
396            if root_init_args is not None:
397                base_args = root_init_args.args
398    needs_name_and_summary = any(arg in base_args for arg in ("name", "summary"))
399    if not needs_name_and_summary:
400        basecls.name = shimmed_path.name
401        return basecls
402    elif (
403        not resolved_cmd_mapping
404        and needs_name_and_summary
405        and getattr(functools, "partialmethod", None)
406    ):
407        new_init = functools.partial(
408            basecls.__init__, name=shimmed_path.name, summary="Summary"
409        )
410        basecls.__init__ = new_init
411    result = basecls
412    assert resolved_cmd_mapping is not None
413    for command_name, command_info in resolved_cmd_mapping.items():
414        if getattr(command_info, "class_name", None) == shimmed_path.name:
415            summary = getattr(command_info, "summary", "Command summary")
416            result = functools.partial(basecls, command_name, summary)
417            break
418    return result
419
420
421def get_session(
422    install_cmd_provider=None,  # type: Optional[TShimmedFunc]
423    install_cmd=None,  # type: TCommandInstance
424    options=None,  # type: Optional[Values]
425):
426    # type: (...) -> TSession
427    session = None  # type: Optional[TSession]
428    if install_cmd is None:
429        assert install_cmd_provider is not None
430        install_cmd_provider = resolve_possible_shim(install_cmd_provider)
431        assert isinstance(install_cmd_provider, (type, functools.partial))
432        install_cmd = install_cmd_provider()
433    if options is None:
434        options, _ = install_cmd.parser.parse_args([])  # type: ignore
435    session = install_cmd._build_session(options)  # type: ignore
436    assert session is not None
437    atexit.register(session.close)
438    return session
439
440
441def populate_options(
442    install_command=None,  # type: TCommandInstance
443    options=None,  # type: Optional[Values]
444    **kwargs  # type: Any
445):
446    # (...) -> Tuple[Dict[str, Any], Values]
447    results = {}
448    if install_command is None and options is None:
449        raise TypeError("Must pass either options or InstallCommand to populate options")
450    if options is None and install_command is not None:
451        options, _ = install_command.parser.parse_args([])  # type: ignore
452    options_dict = options.__dict__
453    for provided_key, provided_value in kwargs.items():
454        if provided_key == "isolated":
455            options_key = "isolated_mode"
456        elif provided_key == "source_dir":
457            options_key = "src_dir"
458        else:
459            options_key = provided_key
460        if provided_key in options_dict and provided_value is not None:
461            setattr(options, options_key, provided_value)
462            results[provided_key] = provided_value
463        elif getattr(options, options_key, None) is not None:
464            results[provided_key] = getattr(options, options_key)
465        else:
466            results[provided_key] = provided_value
467    return results, options
468
469
470def get_requirement_set(
471    install_command=None,  # type: Optional[TCommandInstance]
472    req_set_provider=None,  # type: Optional[TShimmedFunc]
473    build_dir=None,  # type: Optional[str]
474    src_dir=None,  # type: Optional[str]
475    download_dir=None,  # type: Optional[str]
476    wheel_download_dir=None,  # type: Optional[str]
477    session=None,  # type: Optional[TSession]
478    wheel_cache=None,  # type: Optional[TWheelCache]
479    upgrade=False,  # type: bool
480    upgrade_strategy=None,  # type: Optional[str]
481    ignore_installed=False,  # type: bool
482    ignore_dependencies=False,  # type: bool
483    force_reinstall=False,  # type: bool
484    use_user_site=False,  # type: bool
485    isolated=False,  # type: bool
486    ignore_requires_python=False,  # type: bool
487    require_hashes=None,  # type: bool
488    cache_dir=None,  # type: Optional[str]
489    options=None,  # type: Optional[Values]
490    install_cmd_provider=None,  # type: Optional[TShimmedFunc]
491    wheel_cache_provider=None,  # type: Optional[TShimmedFunc]
492):
493    # (...) -> TRequirementSet
494    """
495    Creates a requirement set from the supplied parameters.
496
497    Not all parameters are passed through for all pip versions, but any
498    invalid parameters will be ignored if they are not needed to generate a
499    requirement set on the current pip version.
500
501    :param :class:`~pip_shims.models.ShimmedPathCollection` wheel_cache_provider: A
502        context manager provider which resolves to a `WheelCache` instance
503    :param install_command: A :class:`~pip._internal.commands.install.InstallCommand`
504        instance which is used to generate the finder.
505    :param :class:`~pip_shims.models.ShimmedPathCollection` req_set_provider: A provider
506        to build requirement set instances.
507    :param str build_dir: The directory to build requirements in. Removed in pip 10,
508        defeaults to None
509    :param str source_dir: The directory to use for source requirements. Removed in
510        pip 10, defaults to None
511    :param str download_dir: The directory to download requirement artifacts to. Removed
512        in pip 10, defaults to None
513    :param str wheel_download_dir: The directory to download wheels to. Removed in pip
514        10, defaults ot None
515    :param :class:`~requests.Session` session: The pip session to use. Removed in pip 10,
516        defaults to None
517    :param WheelCache wheel_cache: The pip WheelCache instance to use for caching wheels.
518        Removed in pip 10, defaults to None
519    :param bool upgrade: Whether to try to upgrade existing requirements. Removed in pip
520        10, defaults to False.
521    :param str upgrade_strategy: The upgrade strategy to use, e.g. "only-if-needed".
522        Removed in pip 10, defaults to None.
523    :param bool ignore_installed: Whether to ignore installed packages when resolving.
524        Removed in pip 10, defaults to False.
525    :param bool ignore_dependencies: Whether to ignore dependencies of requirements
526        when resolving. Removed in pip 10, defaults to False.
527    :param bool force_reinstall: Whether to force reinstall of packages when resolving.
528        Removed in pip 10, defaults to False.
529    :param bool use_user_site: Whether to use user site packages when resolving. Removed
530        in pip 10, defaults to False.
531    :param bool isolated: Whether to resolve in isolation. Removed in pip 10, defaults
532        to False.
533    :param bool ignore_requires_python: Removed in pip 10, defaults to False.
534    :param bool require_hashes: Whether to require hashes when resolving. Defaults to
535        False.
536    :param Values options: An :class:`~optparse.Values` instance from an install cmd
537    :param install_cmd_provider: A shim for providing new install command instances.
538    :type install_cmd_provider: :class:`~pip_shims.models.ShimmedPathCollection`
539    :return: A new requirement set instance
540    :rtype: :class:`~pip._internal.req.req_set.RequirementSet`
541    """
542    wheel_cache_provider = resolve_possible_shim(wheel_cache_provider)
543    req_set_provider = resolve_possible_shim(req_set_provider)
544    if install_command is None:
545        install_cmd_provider = resolve_possible_shim(install_cmd_provider)
546        assert isinstance(install_cmd_provider, (type, functools.partial))
547        install_command = install_cmd_provider()
548    required_args = inspect.getargs(
549        req_set_provider.__init__.__code__
550    ).args  # type: ignore
551    results, options = populate_options(
552        install_command,
553        options,
554        build_dir=build_dir,
555        src_dir=src_dir,
556        download_dir=download_dir,
557        upgrade=upgrade,
558        upgrade_strategy=upgrade_strategy,
559        ignore_installed=ignore_installed,
560        ignore_dependencies=ignore_dependencies,
561        force_reinstall=force_reinstall,
562        use_user_site=use_user_site,
563        isolated=isolated,
564        ignore_requires_python=ignore_requires_python,
565        require_hashes=require_hashes,
566        cache_dir=cache_dir,
567    )
568    if session is None and "session" in required_args:
569        session = get_session(install_cmd=install_command, options=options)
570    with ExitStack() as stack:
571        if wheel_cache is None:
572            wheel_cache = stack.enter_context(wheel_cache_provider(cache_dir=cache_dir))
573        results["wheel_cache"] = wheel_cache
574        results["session"] = session
575        results["wheel_download_dir"] = wheel_download_dir
576        return call_function_with_correct_args(req_set_provider, **results)
577
578
579def get_package_finder(
580    install_cmd=None,  # type: Optional[TCommand]
581    options=None,  # type: Optional[Values]
582    session=None,  # type: Optional[TSession]
583    platform=None,  # type: Optional[str]
584    python_versions=None,  # type: Optional[Tuple[str, ...]]
585    abi=None,  # type: Optional[str]
586    implementation=None,  # type: Optional[str]
587    target_python=None,  # type: Optional[Any]
588    ignore_requires_python=None,  # type: Optional[bool]
589    target_python_builder=None,  # type: Optional[TShimmedFunc]
590    install_cmd_provider=None,  # type: Optional[TShimmedFunc]
591):
592    # type: (...) -> TFinder
593    """Shim for compatibility to generate package finders.
594
595    Build and return a :class:`~pip._internal.index.package_finder.PackageFinder`
596    instance using the :class:`~pip._internal.commands.install.InstallCommand` helper
597    method to construct the finder, shimmed with backports as needed for compatibility.
598
599    :param install_cmd_provider: A shim for providing new install command instances.
600    :type install_cmd_provider: :class:`~pip_shims.models.ShimmedPathCollection`
601    :param install_cmd: A :class:`~pip._internal.commands.install.InstallCommand`
602        instance which is used to generate the finder.
603    :param optparse.Values options: An optional :class:`optparse.Values` instance
604        generated by calling `install_cmd.parser.parse_args()` typically.
605    :param session: An optional session instance, can be created by the `install_cmd`.
606    :param Optional[str] platform: An optional platform string, e.g. linux_x86_64
607    :param Optional[Tuple[str, ...]] python_versions: A tuple of 2-digit strings
608        representing python versions, e.g. ("27", "35", "36", "37"...)
609    :param Optional[str] abi: The target abi to support, e.g. "cp38"
610    :param Optional[str] implementation: An optional implementation string for limiting
611        searches to a specific implementation, e.g. "cp" or "py"
612    :param target_python: A :class:`~pip._internal.models.target_python.TargetPython`
613        instance (will be translated to alternate arguments if necessary on incompatible
614        pip versions).
615    :param Optional[bool] ignore_requires_python: Whether to ignore `requires_python`
616        on resulting candidates, only valid after pip version 19.3.1
617    :param target_python_builder: A 'TargetPython' builder (e.g. the class itself,
618        uninstantiated)
619    :return: A :class:`pip._internal.index.package_finder.PackageFinder` instance
620    :rtype: :class:`pip._internal.index.package_finder.PackageFinder`
621
622    :Example:
623
624    >>> from pip_shims.shims import InstallCommand, get_package_finder
625    >>> install_cmd = InstallCommand()
626    >>> finder = get_package_finder(
627    ...     install_cmd, python_versions=("27", "35", "36", "37", "38"), implementation="
628    cp"
629    ... )
630    >>> candidates = finder.find_all_candidates("requests")
631    >>> requests_222 = next(iter(c for c in candidates if c.version.public == "2.22.0"))
632    >>> requests_222
633    <InstallationCandidate('requests', <Version('2.22.0')>, <Link https://files.pythonhos
634    ted.org/packages/51/bd/23c926cd341ea6b7dd0b2a00aba99ae0f828be89d72b2190f27c11d4b7fb/r
635    equests-2.22.0-py2.py3-none-any.whl#sha256=9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9
636    a590f48c010551dc6c4b31 (from https://pypi.org/simple/requests/) (requires-python:>=2.
637    7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*)>)>
638    """
639    if install_cmd is None:
640        install_cmd_provider = resolve_possible_shim(install_cmd_provider)
641        assert isinstance(install_cmd_provider, (type, functools.partial))
642        install_cmd = install_cmd_provider()
643    if options is None:
644        options, _ = install_cmd.parser.parse_args([])  # type: ignore
645    if session is None:
646        session = get_session(install_cmd=install_cmd, options=options)  # type: ignore
647    builder_args = inspect.getargs(
648        install_cmd._build_package_finder.__code__
649    )  # type: ignore
650    build_kwargs = {"options": options, "session": session}
651    expects_targetpython = "target_python" in builder_args.args
652    received_python = any(arg for arg in [platform, python_versions, abi, implementation])
653    if expects_targetpython and received_python and not target_python:
654        if target_python_builder is None:
655            target_python_builder = TargetPython
656        py_version_info = None
657        if python_versions:
658            py_version_info_python = max(python_versions)
659            py_version_info = tuple([int(part) for part in py_version_info_python])
660        target_python_args = {
661            "platform": platform,
662            "platforms": [platform] if platform else None,
663            "abi": abi,
664            "abis": [abi] if abi else None,
665            "implementation": implementation,
666            "py_version_info": py_version_info,
667        }
668        target_python = call_function_with_correct_args(
669            target_python_builder, **target_python_args
670        )
671        build_kwargs["target_python"] = target_python
672    elif any(
673        arg in builder_args.args
674        for arg in ["platform", "python_versions", "abi", "implementation"]
675    ):
676        if target_python and not received_python:
677            tags = target_python.get_tags()
678            version_impl = {t[0] for t in tags}
679            # impls = set([v[:2] for v in version_impl])
680            # impls.remove("py")
681            # impl = next(iter(impls), "py") if not target_python
682            versions = {v[2:] for v in version_impl}
683            build_kwargs.update(
684                {
685                    "platform": target_python.platform,
686                    "python_versions": versions,
687                    "abi": target_python.abi,
688                    "implementation": target_python.implementation,
689                }
690            )
691    if (
692        ignore_requires_python is not None
693        and "ignore_requires_python" in builder_args.args
694    ):
695        build_kwargs["ignore_requires_python"] = ignore_requires_python
696    return install_cmd._build_package_finder(**build_kwargs)  # type: ignore
697
698
699def shim_unpack(
700    unpack_fn,  # type: TShimmedFunc
701    download_dir,  # type str
702    tempdir_manager_provider,  # type: TShimmedFunc
703    ireq=None,  # type: Optional[Any]
704    link=None,  # type: Optional[Any]
705    location=None,  # type Optional[str],
706    hashes=None,  # type: Optional[Any]
707    progress_bar="off",  # type: str
708    only_download=None,  # type: Optional[bool]
709    downloader_provider=None,  # type: Optional[TShimmedFunc]
710    session=None,  # type: Optional[Any]
711):
712    # (...) -> None
713    """
714    Accepts all parameters that have been valid to pass
715    to :func:`pip._internal.download.unpack_url` and selects or
716    drops parameters as needed before invoking the provided
717    callable.
718
719    :param unpack_fn: A callable or shim referring to the pip implementation
720    :type unpack_fn: Callable
721    :param str download_dir: The directory to download the file to
722    :param TShimmedFunc tempdir_manager_provider: A callable or shim referring to
723        `global_tempdir_manager` function from pip or a shimmed no-op context manager
724    :param Optional[:class:`~pip._internal.req.req_install.InstallRequirement`] ireq:
725        an Install Requirement instance, defaults to None
726    :param Optional[:class:`~pip._internal.models.link.Link`] link: A Link instance,
727        defaults to None.
728    :param Optional[str] location: A location or source directory if the target is
729        a VCS url, defaults to None.
730    :param Optional[Any] hashes: A Hashes instance, defaults to None
731    :param str progress_bar: Indicates progress par usage during download, defatuls to
732        off.
733    :param Optional[bool] only_download: Whether to skip install, defaults to None.
734    :param Optional[ShimmedPathCollection] downloader_provider: A downloader class
735        to instantiate, if applicable.
736    :param Optional[`~requests.Session`] session: A PipSession instance, defaults to
737        None.
738    :return: The result of unpacking the url.
739    :rtype: None
740    """
741    unpack_fn = resolve_possible_shim(unpack_fn)
742    downloader_provider = resolve_possible_shim(downloader_provider)
743    tempdir_manager_provider = resolve_possible_shim(tempdir_manager_provider)
744    required_args = inspect.getargs(unpack_fn.__code__).args  # type: ignore
745    unpack_kwargs = {"download_dir": download_dir}
746    with tempdir_manager_provider():
747        if ireq:
748            if not link and ireq.link:
749                link = ireq.link
750            if only_download is None:
751                only_download = ireq.is_wheel
752            if hashes is None:
753                hashes = ireq.hashes(True)
754            if location is None and getattr(ireq, "source_dir", None):
755                location = ireq.source_dir
756        unpack_kwargs.update({"link": link, "location": location})
757        if hashes is not None and "hashes" in required_args:
758            unpack_kwargs["hashes"] = hashes
759        if "progress_bar" in required_args:
760            unpack_kwargs["progress_bar"] = progress_bar
761        if only_download is not None and "only_download" in required_args:
762            unpack_kwargs["only_download"] = only_download
763        if session is not None and "session" in required_args:
764            unpack_kwargs["session"] = session
765        if (
766            "download" in required_args or "downloader" in required_args
767        ) and downloader_provider is not None:
768            arg_name = "download" if "download" in required_args else "downloader"
769            assert session is not None
770            assert progress_bar is not None
771            unpack_kwargs[arg_name] = downloader_provider(session, progress_bar)
772        return unpack_fn(**unpack_kwargs)  # type: ignore
773
774
775def _ensure_finder(
776    finder=None,  # type: Optional[TFinder]
777    finder_provider=None,  # type: Optional[Callable]
778    install_cmd=None,  # type: Optional[TCommandInstance]
779    options=None,  # type: Optional[Values]
780    session=None,  # type: Optional[TSession]
781):
782    if not any([finder, finder_provider, install_cmd]):
783        raise TypeError(
784            "RequirementPreparer requires a packagefinder but no InstallCommand"
785            " was provided to build one and none was passed in."
786        )
787    if finder is not None:
788        return finder
789    else:
790        if session is None:
791            session = get_session(install_cmd=install_cmd, options=options)
792        if finder_provider is not None and options is not None:
793            finder_provider(options=options, session=session)
794        else:
795            finder = get_package_finder(install_cmd, options=options, session=session)
796        return finder
797
798
799@contextlib.contextmanager
800def make_preparer(
801    preparer_fn,  # type: TShimmedFunc
802    req_tracker_fn=None,  # type: Optional[TShimmedFunc]
803    build_dir=None,  # type: Optional[str]
804    src_dir=None,  # type: Optional[str]
805    download_dir=None,  # type: Optional[str]
806    wheel_download_dir=None,  # type: Optional[str]
807    progress_bar="off",  # type: str
808    build_isolation=False,  # type: bool
809    session=None,  # type: Optional[TSession]
810    finder=None,  # type: Optional[TFinder]
811    options=None,  # type: Optional[Values]
812    require_hashes=None,  # type: Optional[bool]
813    use_user_site=None,  # type: Optional[bool]
814    req_tracker=None,  # type: Optional[Union[TReqTracker, TShimmedFunc]]
815    install_cmd_provider=None,  # type: Optional[TShimmedFunc]
816    downloader_provider=None,  # type: Optional[TShimmedFunc]
817    install_cmd=None,  # type: Optional[TCommandInstance]
818    finder_provider=None,  # type: Optional[TShimmedFunc]
819):
820    # (...) -> ContextManager
821    """
822    Creates a requirement preparer for preparing pip requirements.
823
824    Provides a compatibilty shim that accepts all previously valid arguments and
825    discards any that are no longer used.
826
827    :raises TypeError: No requirement tracker provided and one cannot be generated
828    :raises TypeError: No valid sessions provided and one cannot be generated
829    :raises TypeError: No valid finders provided and one cannot be generated
830    :param TShimmedFunc preparer_fn: Callable or shim for generating preparers.
831    :param Optional[TShimmedFunc] req_tracker_fn: Callable or shim for generating
832        requirement trackers, defualts to None
833    :param Optional[str] build_dir: Directory for building packages and wheels,
834        defaults to None
835    :param Optional[str] src_dir: Directory to find or extract source files, defaults
836        to None
837    :param Optional[str] download_dir: Target directory to download files, defaults to
838        None
839    :param Optional[str] wheel_download_dir: Target directoryto download wheels, defaults
840        to None
841    :param str progress_bar: Whether to display a progress bar, defaults to off
842    :param bool build_isolation: Whether to build requirements in isolation, defaults
843        to False
844    :param Optional[TSession] session: Existing session to use for getting requirements,
845        defaults to None
846    :param Optional[TFinder] finder: The package finder to use during resolution,
847        defaults to None
848    :param Optional[Values] options: Pip options to use if needed, defaults to None
849    :param Optional[bool] require_hashes: Whether to require hashes for preparation
850    :param Optional[bool] use_user_site: Whether to use the user site directory for
851        preparing requirements
852    :param Optional[Union[TReqTracker, TShimmedFunc]] req_tracker: The requirement
853        tracker to use for building packages, defaults to None
854    :param Optional[TShimmedFunc] downloader_provider: A downloader provider
855    :param Optional[TCommandInstance] install_cmd: The install command used to create
856        the finder, session, and options if needed, defaults to None
857    :param Optional[TShimmedFunc] finder_provider: A package finder provider
858    :yield: A new requirement preparer instance
859    :rtype: ContextManager[:class:`~pip._internal.operations.prepare.RequirementPreparer`]
860
861    :Example:
862
863    >>> from pip_shims.shims import (
864    ...     InstallCommand, get_package_finder, make_preparer, get_requirement_tracker
865    ... )
866    >>> install_cmd = InstallCommand()
867    >>> pip_options, _ = install_cmd.parser.parse_args([])
868    >>> session = install_cmd._build_session(pip_options)
869    >>> finder = get_package_finder(
870    ...     install_cmd, session=session, options=pip_options
871    ... )
872    >>> with make_preparer(
873    ...     options=pip_options, finder=finder, session=session, install_cmd=ic
874    ... ) as preparer:
875    ...     print(preparer)
876    <pip._internal.operations.prepare.RequirementPreparer object at 0x7f8a2734be80>
877    """
878    preparer_fn = resolve_possible_shim(preparer_fn)
879    downloader_provider = resolve_possible_shim(downloader_provider)
880    finder_provider = resolve_possible_shim(finder_provider)
881    required_args, required_kwargs = get_allowed_args(preparer_fn)  # type: ignore
882    if not req_tracker and not req_tracker_fn and "req_tracker" in required_args:
883        raise TypeError("No requirement tracker and no req tracker generator found!")
884    if "downloader" in required_args and not downloader_provider:
885        raise TypeError("no downloader provided, but one is required to continue!")
886    req_tracker_fn = resolve_possible_shim(req_tracker_fn)
887    pip_options_created = options is None
888    session_is_required = "session" in required_args
889    finder_is_required = "finder" in required_args
890    downloader_is_required = "downloader" in required_args
891    options_map = {
892        "src_dir": src_dir,
893        "download_dir": download_dir,
894        "wheel_download_dir": wheel_download_dir,
895        "build_dir": build_dir,
896        "progress_bar": progress_bar,
897        "build_isolation": build_isolation,
898        "require_hashes": require_hashes,
899        "use_user_site": use_user_site,
900    }
901    if install_cmd is None:
902        assert install_cmd_provider is not None
903        install_cmd_provider = resolve_possible_shim(install_cmd_provider)
904        assert isinstance(install_cmd_provider, (type, functools.partial))
905        install_cmd = install_cmd_provider()
906    preparer_args, options = populate_options(install_cmd, options, **options_map)
907    if options is not None and pip_options_created:
908        for k, v in options_map.items():
909            suppress_setattr(options, k, v, filter_none=True)
910    if session_is_required:
911        if session is None:
912            session = get_session(install_cmd=install_cmd, options=options)
913        preparer_args["session"] = session
914    if finder_is_required:
915        finder = _ensure_finder(
916            finder=finder,
917            finder_provider=finder_provider,
918            install_cmd=install_cmd,
919            options=options,
920            session=session,
921        )
922        preparer_args["finder"] = finder
923    if downloader_is_required:
924        preparer_args["downloader"] = downloader_provider(session, progress_bar)
925    if "in_tree_build" in required_args:
926        preparer_args["in_tree_build"] = True
927    req_tracker_fn = nullcontext if not req_tracker_fn else req_tracker_fn
928    with req_tracker_fn() as tracker_ctx:
929        if "req_tracker" in required_args:
930            req_tracker = tracker_ctx if req_tracker is None else req_tracker
931            preparer_args["req_tracker"] = req_tracker
932        preparer_args["lazy_wheel"] = True
933        result = call_function_with_correct_args(preparer_fn, **preparer_args)
934        yield result
935
936
937@contextlib.contextmanager
938def _ensure_wheel_cache(
939    wheel_cache=None,  # type: Optional[Type[TWheelCache]]
940    wheel_cache_provider=None,  # type: Optional[Callable]
941    format_control=None,  # type: Optional[TFormatControl]
942    format_control_provider=None,  # type: Optional[Type[TShimmedFunc]]
943    options=None,  # type: Optional[Values]
944    cache_dir=None,  # type: Optional[str]
945):
946    if wheel_cache is not None:
947        yield wheel_cache
948    elif wheel_cache_provider is not None:
949        with ExitStack() as stack:
950            cache_dir = getattr(options, "cache_dir", cache_dir)
951            format_control = getattr(
952                options,
953                "format_control",
954                format_control_provider(None, None),  # TFormatControl
955            )
956            wheel_cache = stack.enter_context(
957                wheel_cache_provider(cache_dir, format_control)
958            )
959            yield wheel_cache
960
961
962def get_ireq_output_path(wheel_cache, ireq):
963    if getattr(wheel_cache, "get_path_for_link", None):
964        return wheel_cache.get_path_for_link(ireq.link)
965    elif getattr(wheel_cache, "cached_wheel", None):
966        return wheel_cache.cached_wheel(ireq.link, ireq.name).url_without_fragment
967
968
969def get_resolver(
970    resolver_fn,  # type: TShimmedFunc
971    install_req_provider=None,  # type: Optional[TShimmedFunc]
972    format_control_provider=None,  # type: Optional[TShimmedFunc]
973    wheel_cache_provider=None,  # type: Optional[TShimmedFunc]
974    finder=None,  # type: Optional[TFinder]
975    upgrade_strategy="to-satisfy-only",  # type: str
976    force_reinstall=None,  # type: Optional[bool]
977    ignore_dependencies=None,  # type: Optional[bool]
978    ignore_requires_python=None,  # type: Optional[bool]
979    ignore_installed=True,  # type: bool
980    use_user_site=False,  # type: bool
981    isolated=None,  # type: Optional[bool]
982    wheel_cache=None,  # type: Optional[TWheelCache]
983    preparer=None,  # type: Optional[TPreparer]
984    session=None,  # type: Optional[TSession]
985    options=None,  # type: Optional[Values]
986    make_install_req=None,  # type: Optional[Callable]
987    install_cmd_provider=None,  # type: Optional[TShimmedFunc]
988    install_cmd=None,  # type: Optional[TCommandInstance]
989    use_pep517=True,  # type: bool
990):
991    # (...) -> TResolver
992    """
993    A resolver creation compatibility shim for generating a resolver.
994
995    Consumes any argument that was previously used to instantiate a
996    resolver, discards anything that is no longer valid.
997
998    .. note:: This is only valid for **pip >= 10.0.0**
999
1000    :raises ValueError: A session is required but not provided and one cannot be created
1001    :raises ValueError: A finder is required but not provided and one cannot be created
1002    :raises ValueError: An install requirement provider is required and has not been
1003        provided
1004    :param TShimmedFunc resolver_fn: The resolver function used to create new resolver
1005        instances.
1006    :param TShimmedFunc install_req_provider: The provider function to use to generate
1007        install requirements if needed.
1008    :param TShimmedFunc format_control_provider: The provider function to use to generate
1009        a format_control instance if needed.
1010    :param TShimmedFunc wheel_cache_provider: The provider function to use to generate
1011        a wheel cache if needed.
1012    :param Optional[TFinder] finder: The package finder to use during resolution,
1013        defaults to None.
1014    :param str upgrade_strategy: Upgrade strategy to use, defaults to ``only-if-needed``.
1015    :param Optional[bool] force_reinstall: Whether to simulate or assume package
1016        reinstallation during resolution, defaults to None
1017    :param Optional[bool] ignore_dependencies: Whether to ignore package dependencies,
1018        defaults to None
1019    :param Optional[bool] ignore_requires_python: Whether to ignore indicated
1020        required_python versions on packages, defaults to None
1021    :param bool ignore_installed: Whether to ignore installed packages during resolution,
1022        defaults to True
1023    :param bool use_user_site: Whether to use the user site location during resolution,
1024        defaults to False
1025    :param Optional[bool] isolated: Whether to isolate the resolution process, defaults
1026        to None
1027    :param Optional[TWheelCache] wheel_cache: The wheel cache to use, defaults to None
1028    :param Optional[TPreparer] preparer: The requirement preparer to use, defaults to
1029        None
1030    :param Optional[TSession] session: Existing session to use for getting requirements,
1031        defaults to None
1032    :param Optional[Values] options: Pip options to use if needed, defaults to None
1033    :param Optional[functools.partial] make_install_req: The partial function to pass in
1034        to the resolver for actually generating install requirements, if necessary
1035    :param Optional[TCommandInstance] install_cmd: The install command used to create
1036        the finder, session, and options if needed, defaults to None.
1037    :param bool use_pep517: Whether to use the pep517 build process.
1038    :return: A new resolver instance.
1039    :rtype: :class:`~pip._internal.legacy_resolve.Resolver`
1040
1041    :Example:
1042
1043    >>> import os
1044    >>> from tempdir import TemporaryDirectory
1045    >>> from pip_shims.shims import (
1046    ...     InstallCommand, get_package_finder, make_preparer, get_requirement_tracker,
1047    ...     get_resolver, InstallRequirement, RequirementSet
1048    ... )
1049    >>> install_cmd = InstallCommand()
1050    >>> pip_options, _ = install_cmd.parser.parse_args([])
1051    >>> session = install_cmd._build_session(pip_options)
1052    >>> finder = get_package_finder(
1053    ...     install_cmd, session=session, options=pip_options
1054    ... )
1055    >>> wheel_cache = WheelCache(USER_CACHE_DIR, FormatControl(None, None))
1056    >>> with TemporaryDirectory() as temp_base:
1057    ...     reqset = RequirementSet()
1058    ...     ireq = InstallRequirement.from_line("requests")
1059    ...     ireq.is_direct = True
1060    ...     build_dir = os.path.join(temp_base, "build")
1061    ...     src_dir = os.path.join(temp_base, "src")
1062    ...     ireq.build_location(build_dir)
1063    ...     with make_preparer(
1064    ...         options=pip_options, finder=finder, session=session,
1065    ...         build_dir=build_dir, install_cmd=install_cmd,
1066    ...     ) as preparer:
1067    ...         resolver = get_resolver(
1068    ...             finder=finder, ignore_dependencies=False, ignore_requires_python=True,
1069    ...             preparer=preparer, session=session, options=pip_options,
1070    ...             install_cmd=install_cmd, wheel_cache=wheel_cache,
1071    ...         )
1072    ...         resolver.require_hashes = False
1073    ...         reqset.add_requirement(ireq)
1074    ...         results = resolver.resolve(reqset)
1075    ...         #reqset.cleanup_files()
1076    ...         for result_req in reqset.requirements:
1077    ...             print(result_req)
1078    requests
1079    chardet
1080    certifi
1081    urllib3
1082    idna
1083    """
1084    resolver_fn = resolve_possible_shim(resolver_fn)
1085    install_req_provider = resolve_possible_shim(install_req_provider)
1086    format_control_provider = resolve_possible_shim(format_control_provider)
1087    wheel_cache_provider = resolve_possible_shim(wheel_cache_provider)
1088    install_cmd_provider = resolve_possible_shim(install_cmd_provider)
1089    required_args = inspect.getargs(resolver_fn.__init__.__code__).args  # type: ignore
1090    install_cmd_dependency_map = {"session": session, "finder": finder}
1091    resolver_kwargs = {}  # type: Dict[str, Any]
1092    if install_cmd is None:
1093        assert isinstance(install_cmd_provider, (type, functools.partial))
1094        install_cmd = install_cmd_provider()
1095    if options is None and install_cmd is not None:
1096        options, _ = install_cmd.parser.parse_args([])  # type: ignore
1097    for arg, val in install_cmd_dependency_map.items():
1098        if arg not in required_args:
1099            continue
1100        elif val is None and install_cmd is None:
1101            raise TypeError(
1102                "Preparer requires a {} but did not receive one "
1103                "and cannot generate one".format(arg)
1104            )
1105        elif arg == "session" and val is None:
1106            val = get_session(install_cmd=install_cmd, options=options)
1107        elif arg == "finder" and val is None:
1108            val = get_package_finder(install_cmd, options=options, session=session)
1109        resolver_kwargs[arg] = val
1110    if "make_install_req" in required_args:
1111        if make_install_req is None and install_req_provider is not None:
1112            make_install_req_kwargs = {
1113                "isolated": isolated,
1114                "wheel_cache": wheel_cache,
1115                "use_pep517": use_pep517,
1116            }
1117            factory_args, factory_kwargs = filter_allowed_args(
1118                install_req_provider, **make_install_req_kwargs
1119            )
1120            make_install_req = functools.partial(
1121                install_req_provider, *factory_args, **factory_kwargs
1122            )
1123        assert make_install_req is not None
1124        resolver_kwargs["make_install_req"] = make_install_req
1125    if "isolated" in required_args:
1126        resolver_kwargs["isolated"] = isolated
1127    resolver_kwargs.update(
1128        {
1129            "upgrade_strategy": upgrade_strategy,
1130            "force_reinstall": force_reinstall,
1131            "ignore_dependencies": ignore_dependencies,
1132            "ignore_requires_python": ignore_requires_python,
1133            "ignore_installed": ignore_installed,
1134            "use_user_site": use_user_site,
1135            "preparer": preparer,
1136        }
1137    )
1138    if "wheel_cache" in required_args:
1139        with _ensure_wheel_cache(
1140            wheel_cache=wheel_cache,
1141            wheel_cache_provider=wheel_cache_provider,
1142            format_control_provider=format_control_provider,
1143            options=options,
1144        ) as wheel_cache:
1145            resolver_kwargs["wheel_cache"] = wheel_cache
1146            return resolver_fn(**resolver_kwargs)  # type: ignore
1147    return resolver_fn(**resolver_kwargs)  # type: ignore
1148
1149
1150def resolve(  # noqa:C901
1151    ireq,  # type: TInstallRequirement
1152    reqset_provider=None,  # type: Optional[TShimmedFunc]
1153    req_tracker_provider=None,  # type: Optional[TShimmedFunc]
1154    install_cmd_provider=None,  # type: Optional[TShimmedFunc]
1155    install_command=None,  # type: Optional[TCommand]
1156    finder_provider=None,  # type: Optional[TShimmedFunc]
1157    resolver_provider=None,  # type: Optional[TShimmedFunc]
1158    wheel_cache_provider=None,  # type: Optional[TShimmedFunc]
1159    format_control_provider=None,  # type: Optional[TShimmedFunc]
1160    make_preparer_provider=None,  # type: Optional[TShimmedFunc]
1161    tempdir_manager_provider=None,  # type: Optional[TShimmedFunc]
1162    options=None,  # type: Optional[Values]
1163    session=None,  # type: Optional[TSession]
1164    resolver=None,  # type: Optional[TResolver]
1165    finder=None,  # type: Optional[TFinder]
1166    upgrade_strategy="to-satisfy-only",  # type: str
1167    force_reinstall=None,  # type: Optional[bool]
1168    ignore_dependencies=None,  # type: Optional[bool]
1169    ignore_requires_python=None,  # type: Optional[bool]
1170    ignore_installed=True,  # type: bool
1171    use_user_site=False,  # type: bool
1172    isolated=None,  # type: Optional[bool]
1173    build_dir=None,  # type: Optional[str]
1174    source_dir=None,  # type: Optional[str]
1175    download_dir=None,  # type: Optional[str]
1176    cache_dir=None,  # type: Optional[str]
1177    wheel_download_dir=None,  # type: Optional[str]
1178    wheel_cache=None,  # type: Optional[TWheelCache]
1179    require_hashes=None,  # type: bool
1180    check_supported_wheels=True,  # type: bool
1181):
1182    # (...) -> Set[TInstallRequirement]
1183    """
1184    Resolves the provided **InstallRequirement**, returning a dictionary.
1185
1186    Maps a dictionary of names to corresponding ``InstallRequirement`` values.
1187
1188    :param :class:`~pip._internal.req.req_install.InstallRequirement` ireq: An
1189        InstallRequirement to initiate the resolution process
1190    :param :class:`~pip_shims.models.ShimmedPathCollection` reqset_provider: A provider
1191        to build requirement set instances.
1192    :param :class:`~pip_shims.models.ShimmedPathCollection` req_tracker_provider: A
1193        provider to build requirement tracker instances
1194    :param install_cmd_provider: A shim for providing new install command instances.
1195    :type install_cmd_provider: :class:`~pip_shims.models.ShimmedPathCollection`
1196    :param Optional[TCommandInstance] install_command:  The install command used to
1197        create the finder, session, and options if needed, defaults to None.
1198    :param :class:`~pip_shims.models.ShimmedPathCollection` finder_provider: A provider
1199        to package finder instances.
1200    :param :class:`~pip_shims.models.ShimmedPathCollection` resolver_provider: A provider
1201        to build resolver instances
1202    :param TShimmedFunc wheel_cache_provider: The provider function to use to generate a
1203        wheel cache if needed.
1204    :param TShimmedFunc format_control_provider: The provider function to use to generate
1205        a format_control instance if needed.
1206    :param TShimmedFunc make_preparer_provider: Callable or shim for generating preparers.
1207    :param Optional[TShimmedFunc] tempdir_manager_provider: Shim for generating tempdir
1208        manager for pip temporary directories
1209    :param Optional[Values] options: Pip options to use if needed, defaults to None
1210    :param Optional[TSession] session: Existing session to use for getting requirements,
1211        defaults to None
1212    :param :class:`~pip._internal.legacy_resolve.Resolver` resolver: A pre-existing
1213        resolver instance to use for resolution
1214    :param Optional[TFinder] finder: The package finder to use during resolution,
1215        defaults to None.
1216    :param str upgrade_strategy: Upgrade strategy to use, defaults to ``only-if-needed``.
1217    :param Optional[bool] force_reinstall: Whether to simulate or assume package
1218        reinstallation during resolution, defaults to None
1219    :param Optional[bool] ignore_dependencies: Whether to ignore package dependencies,
1220        defaults to None
1221    :param Optional[bool] ignore_requires_python: Whether to ignore indicated
1222        required_python versions on packages, defaults to None
1223    :param bool ignore_installed: Whether to ignore installed packages during
1224        resolution, defaults to True
1225    :param bool use_user_site: Whether to use the user site location during resolution,
1226        defaults to False
1227    :param Optional[bool] isolated: Whether to isolate the resolution process, defaults
1228        to None
1229    :param Optional[str] build_dir: Directory for building packages and wheels, defaults
1230        to None
1231    :param str source_dir: The directory to use for source requirements. Removed in pip
1232        10, defaults to None
1233    :param Optional[str] download_dir: Target directory to download files, defaults to
1234        None
1235    :param str cache_dir: The cache directory to use for caching artifacts during
1236        resolution
1237    :param Optional[str] wheel_download_dir: Target directoryto download wheels, defaults
1238        to None
1239    :param Optional[TWheelCache] wheel_cache: The wheel cache to use, defaults to None
1240    :param bool require_hashes: Whether to require hashes when resolving. Defaults to
1241        False.
1242    :param bool check_supported_wheels: Whether to check support of wheels before including
1243        them in resolution.
1244    :return: A dictionary mapping requirements to corresponding
1245        :class:`~pip._internal.req.req_install.InstallRequirement`s
1246    :rtype: :class:`~pip._internal.req.req_install.InstallRequirement`
1247
1248    :Example:
1249
1250    >>> from pip_shims.shims import resolve, InstallRequirement
1251    >>> ireq = InstallRequirement.from_line("requests>=2.20")
1252    >>> results = resolve(ireq)
1253    >>> for k, v in results.items():
1254    ...    print("{0}: {1!r}".format(k, v))
1255    requests: <InstallRequirement object: requests>=2.20 from https://files.pythonhosted.
1256    org/packages/51/bd/23c926cd341ea6b7dd0b2a00aba99ae0f828be89d72b2190f27c11d4b7fb/reque
1257    sts-2.22.0-py2.py3-none-any.whl#sha256=9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590
1258    f48c010551dc6c4b31 editable=False>
1259    idna: <InstallRequirement object: idna<2.9,>=2.5 from https://files.pythonhosted.org/
1260    packages/14/2c/cd551d81dbe15200be1cf41cd03869a46fe7226e7450af7a6545bfc474c9/idna-2.8-
1261    py2.py3-none-any.whl#sha256=ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432
1262    f7e4a3c (from requests>=2.20) editable=False>
1263    urllib3: <InstallRequirement object: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 from htt
1264    ps://files.pythonhosted.org/packages/b4/40/a9837291310ee1ccc242ceb6ebfd9eb21539649f19
1265    3a7c8c86ba15b98539/urllib3-1.25.7-py2.py3-none-any.whl#sha256=a8a318824cc77d1fd4b2bec
1266    2ded92646630d7fe8619497b142c84a9e6f5a7293 (from requests>=2.20) editable=False>
1267    chardet: <InstallRequirement object: chardet<3.1.0,>=3.0.2 from https://files.pythonh
1268    osted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8
1269    /chardet-3.0.4-py2.py3-none-any.whl#sha256=fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed
1270    4531e3e15460124c106691 (from requests>=2.20) editable=False>
1271    certifi: <InstallRequirement object: certifi>=2017.4.17 from https://files.pythonhost
1272    ed.org/packages/18/b0/8146a4f8dd402f60744fa380bc73ca47303cccf8b9190fd16a827281eac2/ce
1273    rtifi-2019.9.11-py2.py3-none-any.whl#sha256=fd7c7c74727ddcf00e9acd26bba8da604ffec95bf
1274    1c2144e67aff7a8b50e6cef (from requests>=2.20) editable=False>
1275    """
1276    reqset_provider = resolve_possible_shim(reqset_provider)
1277    finder_provider = resolve_possible_shim(finder_provider)
1278    resolver_provider = resolve_possible_shim(resolver_provider)
1279    wheel_cache_provider = resolve_possible_shim(wheel_cache_provider)
1280    format_control_provider = resolve_possible_shim(format_control_provider)
1281    make_preparer_provider = resolve_possible_shim(make_preparer_provider)
1282    req_tracker_provider = resolve_possible_shim(req_tracker_provider)
1283    install_cmd_provider = resolve_possible_shim(install_cmd_provider)
1284    tempdir_manager_provider = resolve_possible_shim(tempdir_manager_provider)
1285    if install_command is None:
1286        assert isinstance(install_cmd_provider, (type, functools.partial))
1287        install_command = install_cmd_provider()
1288    kwarg_map = {
1289        "upgrade_strategy": upgrade_strategy,
1290        "force_reinstall": force_reinstall,
1291        "ignore_dependencies": ignore_dependencies,
1292        "ignore_requires_python": ignore_requires_python,
1293        "ignore_installed": ignore_installed,
1294        "use_user_site": use_user_site,
1295        "isolated": isolated,
1296        "build_dir": build_dir,
1297        "src_dir": source_dir,
1298        "download_dir": download_dir,
1299        "require_hashes": require_hashes,
1300        "cache_dir": cache_dir,
1301    }
1302    kwargs, options = populate_options(install_command, options, **kwarg_map)
1303    with ExitStack() as ctx:
1304        ctx.enter_context(tempdir_manager_provider())
1305        kwargs = ctx.enter_context(
1306            ensure_resolution_dirs(wheel_download_dir=wheel_download_dir, **kwargs)
1307        )
1308        wheel_download_dir = kwargs.pop("wheel_download_dir")
1309        if session is None:
1310            session = get_session(install_cmd=install_command, options=options)
1311        if finder is None:
1312            finder = finder_provider(
1313                install_command, options=options, session=session
1314            )  # type: ignore
1315        format_control = getattr(options, "format_control", None)
1316        if not format_control:
1317            format_control = format_control_provider(None, None)  # type: ignore
1318        wheel_cache = ctx.enter_context(
1319            wheel_cache_provider(kwargs["cache_dir"], format_control)
1320        )  # type: ignore
1321        ireq.is_direct = True  # type: ignore
1322        build_location_kwargs = {
1323            "build_dir": kwargs["build_dir"],
1324            "autodelete": True,
1325            "parallel_builds": False,
1326        }
1327        call_function_with_correct_args(ireq.build_location, **build_location_kwargs)
1328        if reqset_provider is None:
1329            raise TypeError(
1330                "cannot resolve without a requirement set provider... failed!"
1331            )
1332        reqset = reqset_provider(
1333            install_command,
1334            options=options,
1335            session=session,
1336            wheel_download_dir=wheel_download_dir,
1337            **kwargs
1338        )  # type: ignore
1339        if getattr(reqset, "prepare_files", None):
1340            reqset.add_requirement(ireq)
1341            results = reqset.prepare_files(finder)
1342            result = reqset.requirements
1343            reqset.cleanup_files()
1344            return result
1345        if make_preparer_provider is None:
1346            raise TypeError("Cannot create requirement preparer, cannot resolve!")
1347
1348        preparer_args = {
1349            "build_dir": kwargs["build_dir"],
1350            "src_dir": kwargs["src_dir"],
1351            "download_dir": kwargs["download_dir"],
1352            "wheel_download_dir": wheel_download_dir,
1353            "build_isolation": kwargs["isolated"],
1354            "install_cmd": install_command,
1355            "options": options,
1356            "finder": finder,
1357            "session": session,
1358            "use_user_site": use_user_site,
1359            "require_hashes": require_hashes,
1360        }
1361        if isinstance(req_tracker_provider, (types.FunctionType, functools.partial)):
1362            preparer_args["req_tracker"] = ctx.enter_context(req_tracker_provider())
1363        resolver_keys = [
1364            "upgrade_strategy",
1365            "force_reinstall",
1366            "ignore_dependencies",
1367            "ignore_installed",
1368            "use_user_site",
1369            "isolated",
1370            "use_user_site",
1371        ]
1372        resolver_args = {key: kwargs[key] for key in resolver_keys if key in kwargs}
1373        if resolver_provider is None:
1374            raise TypeError("Cannot resolve without a resolver provider... failed!")
1375        preparer = ctx.enter_context(make_preparer_provider(**preparer_args))
1376        resolver = resolver_provider(
1377            finder=finder,
1378            preparer=preparer,
1379            session=session,
1380            options=options,
1381            install_cmd=install_command,
1382            wheel_cache=wheel_cache,
1383            **resolver_args
1384        )  # type: ignore
1385        resolver.require_hashes = kwargs.get("require_hashes", False)  # type: ignore
1386        _, required_resolver_args = get_method_args(resolver.resolve)
1387        resolver_args = []
1388        if "requirement_set" in required_resolver_args.args:
1389            reqset.add_requirement(ireq)
1390            resolver_args.append(reqset)
1391        elif "root_reqs" in required_resolver_args.args:
1392            resolver_args.append([ireq])
1393        if "check_supported_wheels" in required_resolver_args.args:
1394            resolver_args.append(check_supported_wheels)
1395        result_reqset = resolver.resolve(*resolver_args)  # type: ignore
1396        if result_reqset is None:
1397            result_reqset = reqset
1398        results = result_reqset.requirements
1399        cleanup_fn = getattr(reqset, "cleanup_files", None)
1400        if cleanup_fn is not None:
1401            cleanup_fn()
1402        return results
1403
1404
1405def build_wheel(  # noqa:C901
1406    req=None,  # type: Optional[TInstallRequirement]
1407    reqset=None,  # type: Optional[Union[TReqSet, Iterable[TInstallRequirement]]]
1408    output_dir=None,  # type: Optional[str]
1409    preparer=None,  # type: Optional[TPreparer]
1410    wheel_cache=None,  # type: Optional[TWheelCache]
1411    build_options=None,  # type: Optional[List[str]]
1412    global_options=None,  # type: Optional[List[str]]
1413    check_binary_allowed=None,  # type: Optional[Callable[TInstallRequirement, bool]]
1414    no_clean=False,  # type: bool
1415    session=None,  # type: Optional[TSession]
1416    finder=None,  # type: Optional[TFinder]
1417    install_command=None,  # type: Optional[TCommand]
1418    req_tracker=None,  # type: Optional[TReqTracker]
1419    build_dir=None,  # type: Optional[str]
1420    src_dir=None,  # type: Optional[str]
1421    download_dir=None,  # type: Optional[str]
1422    wheel_download_dir=None,  # type: Optional[str]
1423    cache_dir=None,  # type: Optional[str]
1424    use_user_site=False,  # type: bool
1425    use_pep517=None,  # type: Optional[bool]
1426    verify=False,  # type: bool
1427    editable=False,  # type: bool
1428    format_control_provider=None,  # type: Optional[TShimmedFunc]
1429    wheel_cache_provider=None,  # type: Optional[TShimmedFunc]
1430    preparer_provider=None,  # type: Optional[TShimmedFunc]
1431    wheel_builder_provider=None,  # type: Optional[TShimmedFunc]
1432    build_one_provider=None,  # type: Optional[TShimmedFunc]
1433    build_one_inside_env_provider=None,  # type: Optional[TShimmedFunc]
1434    build_many_provider=None,  # type: Optional[TShimmedFunc]
1435    install_command_provider=None,  # type: Optional[TShimmedFunc]
1436    finder_provider=None,  # type: Optional[TShimmedFunc]
1437    reqset_provider=None,  # type: Optional[TShimmedFunc]
1438):
1439    # type: (...) -> Generator[Union[str, Tuple[List[TInstallRequirement], ...]], None, None]
1440    """
1441    Build a wheel or a set of wheels
1442
1443    :raises TypeError: Raised when no requirements are provided
1444    :param Optional[TInstallRequirement] req:  An `InstallRequirement` to build
1445    :param Optional[TReqSet] reqset: A `RequirementSet` instance (`pip<10`) or an
1446        iterable of `InstallRequirement` instances (`pip>=10`) to build
1447    :param Optional[str] output_dir: Target output directory, only useful when building
1448        one wheel using pip>=20.0
1449    :param Optional[TPreparer] preparer: A preparer instance, defaults to None
1450    :param Optional[TWheelCache] wheel_cache: A wheel cache instance, defaults to None
1451    :param Optional[List[str]] build_options: A list of build options to pass in
1452    :param Optional[List[str]] global_options: A list of global options to pass in
1453    :param Optional[Callable[TInstallRequirement, bool]] check_binary_allowed: A callable
1454        to check whether we are allowed to build and cache wheels for an ireq
1455    :param bool no_clean: Whether to avoid cleaning up wheels
1456    :param Optional[TSession] session: A `PipSession` instance to pass to create a
1457        `finder` if necessary
1458    :param Optional[TFinder] finder: A `PackageFinder` instance to use for generating a
1459        `WheelBuilder` instance on `pip<20`
1460    :param Optional[TCommandInstance] install_command:  The install command used to
1461        create the finder, session, and options if needed, defaults to None.
1462    :param Optional[TReqTracker] req_tracker: An optional requirement tracker instance,
1463        if one already exists
1464    :param Optional[str] build_dir: Passthrough parameter for building preparer
1465    :param Optional[str] src_dir: Passthrough parameter for building preparer
1466    :param Optional[str] download_dir: Passthrough parameter for building preparer
1467    :param Optional[str] wheel_download_dir: Passthrough parameter for building preparer
1468    :param Optional[str] cache_dir: Passthrough cache directory for wheel cache options
1469    :param bool use_user_site: Whether to use the user site directory when preparing
1470        install requirements on `pip<20`
1471    :param Optional[bool] use_pep517: When set to *True* or *False*, prefers building
1472        with or without pep517 as specified, otherwise uses requirement preference.
1473        Only works for single requirements.
1474    :param Optional[TShimmedFunc] format_control_provider: A provider for the
1475        `FormatControl` class
1476    :param Optional[TShimmedFunc] wheel_cache_provider: A provider for the `WheelCache`
1477        class
1478    :param Optional[TShimmedFunc] preparer_provider: A provider for the
1479        `RequirementPreparer` class
1480    :param Optional[TShimmedFunc] wheel_builder_provider: A provider for the
1481        `WheelBuilder` class, if it exists
1482    :param Optional[TShimmedFunc] build_one_provider: A provider for the `_build_one`
1483        function, if it exists
1484    :param Optional[TShimmedFunc] build_one_inside_env_provider: A provider for the
1485        `_build_one_inside_env` function, if it exists
1486    :param Optional[TShimmedFunc] build_many_provider: A provider for the `build`
1487        function, if it exists
1488    :param Optional[TShimmedFunc] install_command_provider: A shim for providing new
1489        install command instances
1490    :param TShimmedFunc finder_provider: A provider to package finder instances
1491    :param TShimmedFunc reqset_provider: A provider for requirement set generation
1492    :return: A tuple of successful and failed install requirements or else a path to
1493        a wheel
1494    :rtype: Optional[Union[str, Tuple[List[TInstallRequirement], List[TInstallRequirement]]]]
1495    """
1496    wheel_cache_provider = resolve_possible_shim(wheel_cache_provider)
1497    preparer_provider = resolve_possible_shim(preparer_provider)
1498    wheel_builder_provider = resolve_possible_shim(wheel_builder_provider)
1499    build_one_provider = resolve_possible_shim(build_one_provider)
1500    build_one_inside_env_provider = resolve_possible_shim(build_one_inside_env_provider)
1501    build_many_provider = resolve_possible_shim(build_many_provider)
1502    install_cmd_provider = resolve_possible_shim(install_command_provider)
1503    format_control_provider = resolve_possible_shim(format_control_provider)
1504    finder_provider = resolve_possible_shim(finder_provider) or get_package_finder
1505    reqset_provider = resolve_possible_shim(reqset_provider)
1506    global_options = [] if global_options is None else global_options
1507    build_options = [] if build_options is None else build_options
1508    options = None
1509    kwarg_map = {
1510        "cache_dir": cache_dir,
1511        "src_dir": src_dir,
1512        "download_dir": download_dir,
1513        "wheel_download_dir": wheel_download_dir,
1514        "build_dir": build_dir,
1515        "use_user_site": use_user_site,
1516    }
1517    if not req and not reqset:
1518        raise TypeError("Must provide either a requirement or requirement set to build")
1519    with ExitStack() as ctx:
1520        kwargs = kwarg_map.copy()
1521        if wheel_cache is None and (reqset is not None or output_dir is None):
1522            if install_command is None:
1523                assert isinstance(install_cmd_provider, (type, functools.partial))
1524                install_command = install_cmd_provider()
1525            kwargs, options = populate_options(install_command, options, **kwarg_map)
1526            format_control = getattr(options, "format_control", None)
1527            if not format_control:
1528                format_control = format_control_provider(None, None)  # type: ignore
1529            wheel_cache = ctx.enter_context(
1530                wheel_cache_provider(options.cache_dir, format_control)
1531            )
1532        if req and not reqset and not output_dir:
1533            output_dir = get_ireq_output_path(wheel_cache, req)
1534        if not reqset and build_one_provider:
1535            build_one_kwargs = {
1536                "req": req,
1537                "output_dir": output_dir,
1538                "verify": verify,
1539                "editable": editable,
1540                "build_options": build_options,
1541                "global_options": global_options,
1542            }
1543            yield call_function_with_correct_args(build_one_provider, **build_one_kwargs)
1544        elif build_many_provider:
1545            yield build_many_provider(
1546                reqset, wheel_cache, build_options, global_options, check_binary_allowed
1547            )
1548        else:
1549            builder_args, builder_kwargs = get_allowed_args(wheel_builder_provider)
1550            if "requirement_set" in builder_args and not reqset:
1551                reqset = reqset_provider()
1552            if session is None and finder is None:
1553                session = get_session(install_cmd=install_command, options=options)
1554                finder = finder_provider(
1555                    install_command, options=options, session=session
1556                )
1557            if preparer is None:
1558                preparer_kwargs = {
1559                    "build_dir": kwargs["build_dir"],
1560                    "src_dir": kwargs["src_dir"],
1561                    "download_dir": kwargs["download_dir"],
1562                    "wheel_download_dir": kwargs["wheel_download_dir"],
1563                    "finder": finder,
1564                    "session": session
1565                    if session
1566                    else get_session(install_cmd=install_command, options=options),
1567                    "install_cmd": install_command,
1568                    "options": options,
1569                    "use_user_site": use_user_site,
1570                    "req_tracker": req_tracker,
1571                }
1572                preparer = ctx.enter_context(preparer_provider(**preparer_kwargs))
1573            check_bin = check_binary_allowed if check_binary_allowed else lambda x: True
1574            builder_kwargs = {
1575                "requirement_set": reqset,
1576                "finder": finder,
1577                "preparer": preparer,
1578                "wheel_cache": wheel_cache,
1579                "no_clean": no_clean,
1580                "build_options": build_options,
1581                "global_options": global_options,
1582                "check_binary_allowed": check_bin,
1583            }
1584            builder = call_function_with_correct_args(
1585                wheel_builder_provider, **builder_kwargs
1586            )
1587            if req and not reqset:
1588                if not output_dir:
1589                    output_dir = get_ireq_output_path(wheel_cache, req)
1590                if use_pep517 is not None:
1591                    req.use_pep517 = use_pep517
1592                yield builder._build_one(req, output_dir)
1593            else:
1594                yield builder.build(reqset)
1595