# -*- coding=utf-8 -*-
"""
Backports and helper functionality to support using new functionality.
"""
from __future__ import absolute_import, print_function
import atexit
import contextlib
import functools
import inspect
import os
import re
import sys
import types
import six
from packaging import specifiers
from .environment import MYPY_RUNNING
from .utils import (
call_function_with_correct_args,
filter_allowed_args,
get_allowed_args,
get_method_args,
nullcontext,
suppress_setattr,
)
if sys.version_info[:2] < (3, 5):
from backports.tempfile import TemporaryDirectory
else:
from tempfile import TemporaryDirectory
if six.PY3:
from contextlib import ExitStack
else:
from contextlib2 import ExitStack
if MYPY_RUNNING:
from optparse import Values
from requests import Session
from typing import (
Any,
Callable,
Dict,
Generator,
Generic,
Iterable,
Iterator,
List,
Optional,
Tuple,
Type,
TypeVar,
Union,
)
from .utils import TShimmedPath, TShim, TShimmedFunc
TFinder = TypeVar("TFinder")
TResolver = TypeVar("TResolver")
TReqTracker = TypeVar("TReqTracker")
TReqSet = TypeVar("TReqSet")
TLink = TypeVar("TLink")
TSession = TypeVar("TSession", bound=Session)
TCommand = TypeVar("TCommand", covariant=True)
TCommandInstance = TypeVar("TCommandInstance")
TCmdDict = Dict[str, Union[Tuple[str, str, str], TCommandInstance]]
TInstallRequirement = TypeVar("TInstallRequirement")
TFormatControl = TypeVar("TFormatControl")
TShimmedCmdDict = Union[TShim, TCmdDict]
TWheelCache = TypeVar("TWheelCache")
TPreparer = TypeVar("TPreparer")
[docs]class SearchScope(object):
def __init__(self, find_links=None, index_urls=None):
self.index_urls = index_urls if index_urls else []
self.find_links = find_links
[docs] @classmethod
def create(cls, find_links=None, index_urls=None):
if not index_urls:
index_urls = ["https://pypi.org/simple"]
return cls(find_links=find_links, index_urls=index_urls)
[docs]class SelectionPreferences(object):
def __init__(
self,
allow_yanked=True,
allow_all_prereleases=False,
format_control=None,
prefer_binary=False,
ignore_requires_python=False,
):
self.allow_yanked = allow_yanked
self.allow_all_prereleases = allow_all_prereleases
self.format_control = format_control
self.prefer_binary = prefer_binary
self.ignore_requires_python = ignore_requires_python
[docs]class TargetPython(object):
fallback_get_tags = None # type: Optional[TShimmedFunc]
def __init__(
self,
platform=None, # type: Optional[str]
py_version_info=None, # type: Optional[Tuple[int, ...]]
abi=None, # type: Optional[str]
implementation=None, # type: Optional[str]
):
# type: (...) -> None
self._given_py_version_info = py_version_info
if py_version_info is None:
py_version_info = sys.version_info[:3]
elif len(py_version_info) < 3:
py_version_info += (3 - len(py_version_info)) * (0,)
else:
py_version_info = py_version_info[:3]
py_version = ".".join(map(str, py_version_info[:2]))
self.abi = abi
self.implementation = implementation
self.platform = platform
self.py_version = py_version
self.py_version_info = py_version_info
self._valid_tags = None
[docs]class CandidatePreferences(object):
def __init__(self, prefer_binary=False, allow_all_prereleases=False):
self.prefer_binary = prefer_binary
self.allow_all_prereleases = allow_all_prereleases
[docs]class LinkCollector(object):
def __init__(self, session=None, search_scope=None):
self.session = session
self.search_scope = search_scope
[docs]class CandidateEvaluator(object):
[docs] @classmethod
def create(
cls,
project_name, # type: str
target_python=None, # type: Optional[TargetPython]
prefer_binary=False, # type: bool
allow_all_prereleases=False, # type: bool
specifier=None, # type: Optional[specifiers.BaseSpecifier]
hashes=None, # type: Optional[Any]
):
if target_python is None:
target_python = TargetPython()
if specifier is None:
specifier = specifiers.SpecifierSet()
supported_tags = target_python.get_tags()
return cls(
project_name=project_name,
supported_tags=supported_tags,
specifier=specifier,
prefer_binary=prefer_binary,
allow_all_prereleases=allow_all_prereleases,
hashes=hashes,
)
def __init__(
self,
project_name, # type: str
supported_tags, # type: List[Any]
specifier, # type: specifiers.BaseSpecifier
prefer_binary=False, # type: bool
allow_all_prereleases=False, # type: bool
hashes=None, # type: Optional[Any]
):
self._allow_all_prereleases = allow_all_prereleases
self._hashes = hashes
self._prefer_binary = prefer_binary
self._project_name = project_name
self._specifier = specifier
self._supported_tags = supported_tags
[docs]class LinkEvaluator(object):
def __init__(
self,
allow_yanked,
project_name,
canonical_name,
formats,
target_python,
ignore_requires_python=False,
ignore_compatibility=True,
):
self._allow_yanked = allow_yanked
self._canonical_name = canonical_name
self._ignore_requires_python = ignore_requires_python
self._formats = formats
self._target_python = target_python
self._ignore_compatibility = ignore_compatibility
self.project_name = project_name
[docs]class InvalidWheelFilename(Exception):
"""Wheel Filename is Invalid"""
[docs]class Wheel(object):
wheel_file_re = re.compile(
r"""^(?P<namever>(?P<name>.+?)-(?P<ver>.*?))
((-(?P<build>\d[^-]*?))?-(?P<pyver>.+?)-(?P<abi>.+?)-(?P<plat>.+?)
\.whl|\.dist-info)$""",
re.VERBOSE,
)
def __init__(self, filename):
# type: (str) -> None
wheel_info = self.wheel_file_re.match(filename)
if not wheel_info:
raise InvalidWheelFilename("%s is not a valid wheel filename." % filename)
self.filename = filename
self.name = wheel_info.group("name").replace("_", "-")
# we'll assume "_" means "-" due to wheel naming scheme
# (https://github.com/pypa/pip/issues/1150)
self.version = wheel_info.group("ver").replace("_", "-")
self.build_tag = wheel_info.group("build")
self.pyversions = wheel_info.group("pyver").split(".")
self.abis = wheel_info.group("abi").split(".")
self.plats = wheel_info.group("plat").split(".")
# All the tag combinations from this file
self.file_tags = {
(x, y, z) for x in self.pyversions for y in self.abis for z in self.plats
}
[docs] def support_index_min(self, tags):
# type: (List[Any]) -> int
"""
Return the lowest index that one of the wheel's file_tag combinations
achieves in the given list of supported tags.
For example, if there are 8 supported tags and one of the file tags
is first in the list, then return 0.
:param tags: the PEP 425 tags to check the wheel against, in order
with most preferred first.
:raises ValueError: If none of the wheel's file tags match one of
the supported tags.
"""
return min(tags.index(tag) for tag in self.file_tags if tag in tags)
[docs] def supported(self, tags):
# type: (List[Any]) -> bool
"""
Return whether the wheel is compatible with one of the given tags.
:param tags: the PEP 425 tags to check the wheel against.
"""
return not self.file_tags.isdisjoint(tags)
[docs]def resolve_possible_shim(target):
# type: (TShimmedFunc) -> Optional[Union[Type, Callable]]
if target is None:
return target
if getattr(target, "shim", None) and isinstance(
target.shim, (types.MethodType, types.FunctionType)
):
return target.shim()
return target
[docs]@contextlib.contextmanager
def temp_environ():
"""Allow the ability to set os.environ temporarily"""
environ = dict(os.environ)
try:
yield
finally:
os.environ.clear()
os.environ.update(environ)
[docs]@contextlib.contextmanager
def get_requirement_tracker(req_tracker_creator=None):
# type: (Optional[Callable]) -> Generator[Optional[TReqTracker], None, None]
root = os.environ.get("PIP_REQ_TRACKER")
if not req_tracker_creator:
yield None
else:
req_tracker_args = []
_, required_args = get_method_args(req_tracker_creator.__init__) # type: ignore
with ExitStack() as ctx:
if root is None:
root = ctx.enter_context(TemporaryDirectory(prefix="req-tracker"))
if root:
root = str(root)
ctx.enter_context(temp_environ())
os.environ["PIP_REQ_TRACKER"] = root
if required_args is not None and "root" in required_args:
req_tracker_args.append(root)
with req_tracker_creator(*req_tracker_args) as tracker:
yield tracker
[docs]@contextlib.contextmanager
def ensure_resolution_dirs(**kwargs):
# type: (Any) -> Iterator[Dict[str, Any]]
"""
Ensures that the proper directories are scaffolded and present in the provided kwargs
for performing dependency resolution via pip.
:return: A new kwargs dictionary with scaffolded directories for **build_dir**, **src_dir**,
**download_dir**, and **wheel_download_dir** added to the key value pairs.
:rtype: Dict[str, Any]
"""
keys = ("build_dir", "src_dir", "download_dir", "wheel_download_dir")
if not any(kwargs.get(key) is None for key in keys):
yield kwargs
else:
with TemporaryDirectory(prefix="pip-shims-") as base_dir:
for key in keys:
if kwargs.get(key) is not None:
continue
target = os.path.join(base_dir, key)
os.makedirs(target)
kwargs[key] = target
yield kwargs
[docs]@contextlib.contextmanager
def wheel_cache(
cache_dir=None, # type: str
format_control=None, # type: Any
wheel_cache_provider=None, # type: TShimmedFunc
format_control_provider=None, # type: Optional[TShimmedFunc]
tempdir_manager_provider=None, # type: TShimmedFunc
):
tempdir_manager_provider = resolve_possible_shim(tempdir_manager_provider)
wheel_cache_provider = resolve_possible_shim(wheel_cache_provider)
format_control_provider = resolve_possible_shim(format_control_provider)
if not format_control and not format_control_provider:
raise TypeError("Format control or provider needed for wheel cache!")
if not format_control:
format_control = format_control_provider(None, None)
with ExitStack() as ctx:
ctx.enter_context(tempdir_manager_provider())
wheel_cache = wheel_cache_provider(cache_dir, format_control)
yield wheel_cache
[docs]def partial_command(shimmed_path, cmd_mapping=None):
# type: (Type, Optional[TShimmedCmdDict]) -> Union[Type[TCommandInstance], functools.partial]
"""
Maps a default set of arguments across all members of a
:class:`~pip_shims.models.ShimmedPath` instance, specifically for
:class:`~pip._internal.command.Command` instances which need
`summary` and `name` arguments.
:param :class:`~pip_shims.models.ShimmedPath` shimmed_path: A
:class:`~pip_shims.models.ShimmedCollection` instance
:param Any cmd_mapping: A reference to use for mapping against, e.g. an
import that depends on pip also
:return: A dictionary mapping new arguments to their default values
:rtype: Dict[str, str]
"""
basecls = shimmed_path.shim()
resolved_cmd_mapping = None # type: Optional[Dict[str, Any]]
cmd_mapping = resolve_possible_shim(cmd_mapping)
if cmd_mapping is not None and isinstance(cmd_mapping, dict):
resolved_cmd_mapping = cmd_mapping.copy()
base_args = [] # type: List[str]
for root_cls in basecls.mro():
if root_cls.__name__ == "Command":
_, root_init_args = get_method_args(root_cls.__init__)
if root_init_args is not None:
base_args = root_init_args.args
needs_name_and_summary = any(arg in base_args for arg in ("name", "summary"))
if not needs_name_and_summary:
basecls.name = shimmed_path.name
return basecls
elif (
not resolved_cmd_mapping
and needs_name_and_summary
and getattr(functools, "partialmethod", None)
):
new_init = functools.partial(
basecls.__init__, name=shimmed_path.name, summary="Summary"
)
basecls.__init__ = new_init
result = basecls
assert resolved_cmd_mapping is not None
for command_name, command_info in resolved_cmd_mapping.items():
if getattr(command_info, "class_name", None) == shimmed_path.name:
summary = getattr(command_info, "summary", "Command summary")
result = functools.partial(basecls, command_name, summary)
break
return result
[docs]def get_session(
install_cmd_provider=None, # type: Optional[TShimmedFunc]
install_cmd=None, # type: TCommandInstance
options=None, # type: Optional[Values]
):
# type: (...) -> TSession
session = None # type: Optional[TSession]
if install_cmd is None:
assert install_cmd_provider is not None
install_cmd_provider = resolve_possible_shim(install_cmd_provider)
assert isinstance(install_cmd_provider, (type, functools.partial))
install_cmd = install_cmd_provider()
if options is None:
options, _ = install_cmd.parser.parse_args([]) # type: ignore
session = install_cmd._build_session(options) # type: ignore
assert session is not None
atexit.register(session.close)
return session
[docs]def populate_options(
install_command=None, # type: TCommandInstance
options=None, # type: Optional[Values]
**kwargs # type: Any
):
# (...) -> Tuple[Dict[str, Any], Values]
results = {}
if install_command is None and options is None:
raise TypeError("Must pass either options or InstallCommand to populate options")
if options is None and install_command is not None:
options, _ = install_command.parser.parse_args([]) # type: ignore
options_dict = options.__dict__
for provided_key, provided_value in kwargs.items():
if provided_key == "isolated":
options_key = "isolated_mode"
elif provided_key == "source_dir":
options_key = "src_dir"
else:
options_key = provided_key
if provided_key in options_dict and provided_value is not None:
setattr(options, options_key, provided_value)
results[provided_key] = provided_value
elif getattr(options, options_key, None) is not None:
results[provided_key] = getattr(options, options_key)
else:
results[provided_key] = provided_value
return results, options
[docs]def get_requirement_set(
install_command=None, # type: Optional[TCommandInstance]
req_set_provider=None, # type: Optional[TShimmedFunc]
build_dir=None, # type: Optional[str]
src_dir=None, # type: Optional[str]
download_dir=None, # type: Optional[str]
wheel_download_dir=None, # type: Optional[str]
session=None, # type: Optional[TSession]
wheel_cache=None, # type: Optional[TWheelCache]
upgrade=False, # type: bool
upgrade_strategy=None, # type: Optional[str]
ignore_installed=False, # type: bool
ignore_dependencies=False, # type: bool
force_reinstall=False, # type: bool
use_user_site=False, # type: bool
isolated=False, # type: bool
ignore_requires_python=False, # type: bool
require_hashes=None, # type: bool
cache_dir=None, # type: Optional[str]
options=None, # type: Optional[Values]
install_cmd_provider=None, # type: Optional[TShimmedFunc]
wheel_cache_provider=None, # type: Optional[TShimmedFunc]
):
# (...) -> TRequirementSet
"""
Creates a requirement set from the supplied parameters.
Not all parameters are passed through for all pip versions, but any
invalid parameters will be ignored if they are not needed to generate a
requirement set on the current pip version.
:param :class:`~pip_shims.models.ShimmedPathCollection` wheel_cache_provider: A
context manager provider which resolves to a `WheelCache` instance
:param install_command: A :class:`~pip._internal.commands.install.InstallCommand`
instance which is used to generate the finder.
:param :class:`~pip_shims.models.ShimmedPathCollection` req_set_provider: A provider
to build requirement set instances.
:param str build_dir: The directory to build requirements in. Removed in pip 10,
defeaults to None
:param str source_dir: The directory to use for source requirements. Removed in
pip 10, defaults to None
:param str download_dir: The directory to download requirement artifacts to. Removed
in pip 10, defaults to None
:param str wheel_download_dir: The directory to download wheels to. Removed in pip
10, defaults ot None
:param :class:`~requests.Session` session: The pip session to use. Removed in pip 10,
defaults to None
:param WheelCache wheel_cache: The pip WheelCache instance to use for caching wheels.
Removed in pip 10, defaults to None
:param bool upgrade: Whether to try to upgrade existing requirements. Removed in pip
10, defaults to False.
:param str upgrade_strategy: The upgrade strategy to use, e.g. "only-if-needed".
Removed in pip 10, defaults to None.
:param bool ignore_installed: Whether to ignore installed packages when resolving.
Removed in pip 10, defaults to False.
:param bool ignore_dependencies: Whether to ignore dependencies of requirements
when resolving. Removed in pip 10, defaults to False.
:param bool force_reinstall: Whether to force reinstall of packages when resolving.
Removed in pip 10, defaults to False.
:param bool use_user_site: Whether to use user site packages when resolving. Removed
in pip 10, defaults to False.
:param bool isolated: Whether to resolve in isolation. Removed in pip 10, defaults
to False.
:param bool ignore_requires_python: Removed in pip 10, defaults to False.
:param bool require_hashes: Whether to require hashes when resolving. Defaults to
False.
:param Values options: An :class:`~optparse.Values` instance from an install cmd
:param install_cmd_provider: A shim for providing new install command instances.
:type install_cmd_provider: :class:`~pip_shims.models.ShimmedPathCollection`
:return: A new requirement set instance
:rtype: :class:`~pip._internal.req.req_set.RequirementSet`
"""
wheel_cache_provider = resolve_possible_shim(wheel_cache_provider)
req_set_provider = resolve_possible_shim(req_set_provider)
if install_command is None:
install_cmd_provider = resolve_possible_shim(install_cmd_provider)
assert isinstance(install_cmd_provider, (type, functools.partial))
install_command = install_cmd_provider()
required_args = inspect.getargs(
req_set_provider.__init__.__code__
).args # type: ignore
results, options = populate_options(
install_command,
options,
build_dir=build_dir,
src_dir=src_dir,
download_dir=download_dir,
upgrade=upgrade,
upgrade_strategy=upgrade_strategy,
ignore_installed=ignore_installed,
ignore_dependencies=ignore_dependencies,
force_reinstall=force_reinstall,
use_user_site=use_user_site,
isolated=isolated,
ignore_requires_python=ignore_requires_python,
require_hashes=require_hashes,
cache_dir=cache_dir,
)
if session is None and "session" in required_args:
session = get_session(install_cmd=install_command, options=options)
with ExitStack() as stack:
if wheel_cache is None:
wheel_cache = stack.enter_context(wheel_cache_provider(cache_dir=cache_dir))
results["wheel_cache"] = wheel_cache
results["session"] = session
results["wheel_download_dir"] = wheel_download_dir
return call_function_with_correct_args(req_set_provider, **results)
[docs]def get_package_finder(
install_cmd=None, # type: Optional[TCommand]
options=None, # type: Optional[Values]
session=None, # type: Optional[TSession]
platform=None, # type: Optional[str]
python_versions=None, # type: Optional[Tuple[str, ...]]
abi=None, # type: Optional[str]
implementation=None, # type: Optional[str]
target_python=None, # type: Optional[Any]
ignore_requires_python=None, # type: Optional[bool]
target_python_builder=None, # type: Optional[TShimmedFunc]
install_cmd_provider=None, # type: Optional[TShimmedFunc]
):
# type: (...) -> TFinder
"""Shim for compatibility to generate package finders.
Build and return a :class:`~pip._internal.index.package_finder.PackageFinder`
instance using the :class:`~pip._internal.commands.install.InstallCommand` helper
method to construct the finder, shimmed with backports as needed for compatibility.
:param install_cmd_provider: A shim for providing new install command instances.
:type install_cmd_provider: :class:`~pip_shims.models.ShimmedPathCollection`
:param install_cmd: A :class:`~pip._internal.commands.install.InstallCommand`
instance which is used to generate the finder.
:param optparse.Values options: An optional :class:`optparse.Values` instance
generated by calling `install_cmd.parser.parse_args()` typically.
:param session: An optional session instance, can be created by the `install_cmd`.
:param Optional[str] platform: An optional platform string, e.g. linux_x86_64
:param Optional[Tuple[str, ...]] python_versions: A tuple of 2-digit strings
representing python versions, e.g. ("27", "35", "36", "37"...)
:param Optional[str] abi: The target abi to support, e.g. "cp38"
:param Optional[str] implementation: An optional implementation string for limiting
searches to a specific implementation, e.g. "cp" or "py"
:param target_python: A :class:`~pip._internal.models.target_python.TargetPython`
instance (will be translated to alternate arguments if necessary on incompatible
pip versions).
:param Optional[bool] ignore_requires_python: Whether to ignore `requires_python`
on resulting candidates, only valid after pip version 19.3.1
:param target_python_builder: A 'TargetPython' builder (e.g. the class itself,
uninstantiated)
:return: A :class:`pip._internal.index.package_finder.PackageFinder` instance
:rtype: :class:`pip._internal.index.package_finder.PackageFinder`
:Example:
>>> from pip_shims.shims import InstallCommand, get_package_finder
>>> install_cmd = InstallCommand()
>>> finder = get_package_finder(
... install_cmd, python_versions=("27", "35", "36", "37", "38"), implementation="
cp"
... )
>>> candidates = finder.find_all_candidates("requests")
>>> requests_222 = next(iter(c for c in candidates if c.version.public == "2.22.0"))
>>> requests_222
<InstallationCandidate('requests', <Version('2.22.0')>, <Link https://files.pythonhos
ted.org/packages/51/bd/23c926cd341ea6b7dd0b2a00aba99ae0f828be89d72b2190f27c11d4b7fb/r
equests-2.22.0-py2.py3-none-any.whl#sha256=9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9
a590f48c010551dc6c4b31 (from https://pypi.org/simple/requests/) (requires-python:>=2.
7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*)>)>
"""
if install_cmd is None:
install_cmd_provider = resolve_possible_shim(install_cmd_provider)
assert isinstance(install_cmd_provider, (type, functools.partial))
install_cmd = install_cmd_provider()
if options is None:
options, _ = install_cmd.parser.parse_args([]) # type: ignore
if session is None:
session = get_session(install_cmd=install_cmd, options=options) # type: ignore
builder_args = inspect.getargs(
install_cmd._build_package_finder.__code__
) # type: ignore
build_kwargs = {"options": options, "session": session}
expects_targetpython = "target_python" in builder_args.args
received_python = any(arg for arg in [platform, python_versions, abi, implementation])
if expects_targetpython and received_python and not target_python:
if target_python_builder is None:
target_python_builder = TargetPython
py_version_info = None
if python_versions:
py_version_info_python = max(python_versions)
py_version_info = tuple([int(part) for part in py_version_info_python])
target_python = target_python_builder(
platform=platform,
abi=abi,
implementation=implementation,
py_version_info=py_version_info,
)
build_kwargs["target_python"] = target_python
elif any(
arg in builder_args.args
for arg in ["platform", "python_versions", "abi", "implementation"]
):
if target_python and not received_python:
tags = target_python.get_tags()
version_impl = {t[0] for t in tags}
# impls = set([v[:2] for v in version_impl])
# impls.remove("py")
# impl = next(iter(impls), "py") if not target_python
versions = {v[2:] for v in version_impl}
build_kwargs.update(
{
"platform": target_python.platform,
"python_versions": versions,
"abi": target_python.abi,
"implementation": target_python.implementation,
}
)
if (
ignore_requires_python is not None
and "ignore_requires_python" in builder_args.args
):
build_kwargs["ignore_requires_python"] = ignore_requires_python
return install_cmd._build_package_finder(**build_kwargs) # type: ignore
[docs]def shim_unpack(
unpack_fn, # type: TShimmedFunc
download_dir, # type str
tempdir_manager_provider, # type: TShimmedFunc
ireq=None, # type: Optional[Any]
link=None, # type: Optional[Any]
location=None, # type Optional[str],
hashes=None, # type: Optional[Any]
progress_bar="off", # type: str
only_download=None, # type: Optional[bool]
downloader_provider=None, # type: Optional[TShimmedFunc]
session=None, # type: Optional[Any]
):
# (...) -> None
"""
Accepts all parameters that have been valid to pass
to :func:`pip._internal.download.unpack_url` and selects or
drops parameters as needed before invoking the provided
callable.
:param unpack_fn: A callable or shim referring to the pip implementation
:type unpack_fn: Callable
:param str download_dir: The directory to download the file to
:param TShimmedFunc tempdir_manager_provider: A callable or shim referring to
`global_tempdir_manager` function from pip or a shimmed no-op context manager
:param Optional[:class:`~pip._internal.req.req_install.InstallRequirement`] ireq:
an Install Requirement instance, defaults to None
:param Optional[:class:`~pip._internal.models.link.Link`] link: A Link instance,
defaults to None.
:param Optional[str] location: A location or source directory if the target is
a VCS url, defaults to None.
:param Optional[Any] hashes: A Hashes instance, defaults to None
:param str progress_bar: Indicates progress par usage during download, defatuls to
off.
:param Optional[bool] only_download: Whether to skip install, defaults to None.
:param Optional[ShimmedPathCollection] downloader_provider: A downloader class
to instantiate, if applicable.
:param Optional[`~requests.Session`] session: A PipSession instance, defaults to
None.
:return: The result of unpacking the url.
:rtype: None
"""
unpack_fn = resolve_possible_shim(unpack_fn)
downloader_provider = resolve_possible_shim(downloader_provider)
tempdir_manager_provider = resolve_possible_shim(tempdir_manager_provider)
required_args = inspect.getargs(unpack_fn.__code__).args # type: ignore
unpack_kwargs = {"download_dir": download_dir}
with tempdir_manager_provider():
if ireq:
if not link and ireq.link:
link = ireq.link
if only_download is None:
only_download = ireq.is_wheel
if hashes is None:
hashes = ireq.hashes(True)
if location is None and getattr(ireq, "source_dir", None):
location = ireq.source_dir
unpack_kwargs.update({"link": link, "location": location})
if hashes is not None and "hashes" in required_args:
unpack_kwargs["hashes"] = hashes
if "progress_bar" in required_args:
unpack_kwargs["progress_bar"] = progress_bar
if only_download is not None and "only_download" in required_args:
unpack_kwargs["only_download"] = only_download
if session is not None and "session" in required_args:
unpack_kwargs["session"] = session
if "downloader" in required_args and downloader_provider is not None:
assert session is not None
assert progress_bar is not None
unpack_kwargs["downloader"] = downloader_provider(session, progress_bar)
return unpack_fn(**unpack_kwargs) # type: ignore
[docs]def _ensure_finder(
finder=None, # type: Optional[TFinder]
finder_provider=None, # type: Optional[Callable]
install_cmd=None, # type: Optional[TCommandInstance]
options=None, # type: Optional[Values]
session=None, # type: Optional[TSession]
):
if not any([finder, finder_provider, install_cmd]):
raise TypeError(
"RequirementPreparer requires a packagefinder but no InstallCommand"
" was provided to build one and none was passed in."
)
if finder is not None:
return finder
else:
if session is None:
session = get_session(install_cmd=install_cmd, options=options)
if finder_provider is not None and options is not None:
finder_provider(options=options, session=session)
else:
finder = get_package_finder(install_cmd, options=options, session=session)
return finder
[docs]@contextlib.contextmanager
def make_preparer(
preparer_fn, # type: TShimmedFunc
req_tracker_fn=None, # type: Optional[TShimmedFunc]
build_dir=None, # type: Optional[str]
src_dir=None, # type: Optional[str]
download_dir=None, # type: Optional[str]
wheel_download_dir=None, # type: Optional[str]
progress_bar="off", # type: str
build_isolation=False, # type: bool
session=None, # type: Optional[TSession]
finder=None, # type: Optional[TFinder]
options=None, # type: Optional[Values]
require_hashes=None, # type: Optional[bool]
use_user_site=None, # type: Optional[bool]
req_tracker=None, # type: Optional[Union[TReqTracker, TShimmedFunc]]
install_cmd_provider=None, # type: Optional[TShimmedFunc]
downloader_provider=None, # type: Optional[TShimmedFunc]
install_cmd=None, # type: Optional[TCommandInstance]
finder_provider=None, # type: Optional[TShimmedFunc]
):
# (...) -> ContextManager
"""
Creates a requirement preparer for preparing pip requirements.
Provides a compatibilty shim that accepts all previously valid arguments and
discards any that are no longer used.
:raises TypeError: No requirement tracker provided and one cannot be generated
:raises TypeError: No valid sessions provided and one cannot be generated
:raises TypeError: No valid finders provided and one cannot be generated
:param TShimmedFunc preparer_fn: Callable or shim for generating preparers.
:param Optional[TShimmedFunc] req_tracker_fn: Callable or shim for generating
requirement trackers, defualts to None
:param Optional[str] build_dir: Directory for building packages and wheels,
defaults to None
:param Optional[str] src_dir: Directory to find or extract source files, defaults
to None
:param Optional[str] download_dir: Target directory to download files, defaults to
None
:param Optional[str] wheel_download_dir: Target directoryto download wheels, defaults
to None
:param str progress_bar: Whether to display a progress bar, defaults to off
:param bool build_isolation: Whether to build requirements in isolation, defaults
to False
:param Optional[TSession] session: Existing session to use for getting requirements,
defaults to None
:param Optional[TFinder] finder: The package finder to use during resolution,
defaults to None
:param Optional[Values] options: Pip options to use if needed, defaults to None
:param Optional[bool] require_hashes: Whether to require hashes for preparation
:param Optional[bool] use_user_site: Whether to use the user site directory for
preparing requirements
:param Optional[Union[TReqTracker, TShimmedFunc]] req_tracker: The requirement
tracker to use for building packages, defaults to None
:param Optional[TShimmedFunc] downloader_provider: A downloader provider
:param Optional[TCommandInstance] install_cmd: The install command used to create
the finder, session, and options if needed, defaults to None
:param Optional[TShimmedFunc] finder_provider: A package finder provider
:yield: A new requirement preparer instance
:rtype: ContextManager[:class:`~pip._internal.operations.prepare.RequirementPreparer`]
:Example:
>>> from pip_shims.shims import (
... InstallCommand, get_package_finder, make_preparer, get_requirement_tracker
... )
>>> install_cmd = InstallCommand()
>>> pip_options, _ = install_cmd.parser.parse_args([])
>>> session = install_cmd._build_session(pip_options)
>>> finder = get_package_finder(
... install_cmd, session=session, options=pip_options
... )
>>> with make_preparer(
... options=pip_options, finder=finder, session=session, install_cmd=ic
... ) as preparer:
... print(preparer)
<pip._internal.operations.prepare.RequirementPreparer object at 0x7f8a2734be80>
"""
preparer_fn = resolve_possible_shim(preparer_fn)
downloader_provider = resolve_possible_shim(downloader_provider)
finder_provider = resolve_possible_shim(finder_provider)
required_args, required_kwargs = get_allowed_args(preparer_fn) # type: ignore
if not req_tracker and not req_tracker_fn and "req_tracker" in required_args:
raise TypeError("No requirement tracker and no req tracker generator found!")
if "downloader" in required_args and not downloader_provider:
raise TypeError("no downloader provided, but one is required to continue!")
req_tracker_fn = resolve_possible_shim(req_tracker_fn)
pip_options_created = options is None
session_is_required = "session" in required_args
finder_is_required = "finder" in required_args
downloader_is_required = "downloader" in required_args
options_map = {
"src_dir": src_dir,
"download_dir": download_dir,
"wheel_download_dir": wheel_download_dir,
"build_dir": build_dir,
"progress_bar": progress_bar,
"build_isolation": build_isolation,
"require_hashes": require_hashes,
"use_user_site": use_user_site,
}
if install_cmd is None:
assert install_cmd_provider is not None
install_cmd_provider = resolve_possible_shim(install_cmd_provider)
assert isinstance(install_cmd_provider, (type, functools.partial))
install_cmd = install_cmd_provider()
preparer_args, options = populate_options(install_cmd, options, **options_map)
if options is not None and pip_options_created:
for k, v in options_map.items():
suppress_setattr(options, k, v, filter_none=True)
if session_is_required:
if session is None:
session = get_session(install_cmd=install_cmd, options=options)
preparer_args["session"] = session
if finder_is_required:
finder = _ensure_finder(
finder=finder,
finder_provider=finder_provider,
install_cmd=install_cmd,
options=options,
session=session,
)
preparer_args["finder"] = finder
if downloader_is_required:
preparer_args["downloader"] = downloader_provider(session, progress_bar)
req_tracker_fn = nullcontext if not req_tracker_fn else req_tracker_fn
with req_tracker_fn() as tracker_ctx:
if "req_tracker" in required_args:
req_tracker = tracker_ctx if req_tracker is None else req_tracker
preparer_args["req_tracker"] = req_tracker
preparer_args["lazy_wheel"] = True
result = call_function_with_correct_args(preparer_fn, **preparer_args)
yield result
[docs]@contextlib.contextmanager
def _ensure_wheel_cache(
wheel_cache=None, # type: Optional[Type[TWheelCache]]
wheel_cache_provider=None, # type: Optional[Callable]
format_control=None, # type: Optional[TFormatControl]
format_control_provider=None, # type: Optional[Type[TShimmedFunc]]
options=None, # type: Optional[Values]
cache_dir=None, # type: Optional[str]
):
if wheel_cache is not None:
yield wheel_cache
elif wheel_cache_provider is not None:
with ExitStack() as stack:
cache_dir = getattr(options, "cache_dir", cache_dir)
format_control = getattr(
options,
"format_control",
format_control_provider(None, None), # TFormatControl
)
wheel_cache = stack.enter_context(
wheel_cache_provider(cache_dir, format_control)
)
yield wheel_cache
[docs]def get_ireq_output_path(wheel_cache, ireq):
if getattr(wheel_cache, "get_path_for_link", None):
return wheel_cache.get_path_for_link(ireq.link)
elif getattr(wheel_cache, "cached_wheel", None):
return wheel_cache.cached_wheel(ireq.link, ireq.name).url_without_fragment
[docs]def get_resolver(
resolver_fn, # type: TShimmedFunc
install_req_provider=None, # type: Optional[TShimmedFunc]
format_control_provider=None, # type: Optional[TShimmedFunc]
wheel_cache_provider=None, # type: Optional[TShimmedFunc]
finder=None, # type: Optional[TFinder]
upgrade_strategy="to-satisfy-only", # type: str
force_reinstall=None, # type: Optional[bool]
ignore_dependencies=None, # type: Optional[bool]
ignore_requires_python=None, # type: Optional[bool]
ignore_installed=True, # type: bool
use_user_site=False, # type: bool
isolated=None, # type: Optional[bool]
wheel_cache=None, # type: Optional[TWheelCache]
preparer=None, # type: Optional[TPreparer]
session=None, # type: Optional[TSession]
options=None, # type: Optional[Values]
make_install_req=None, # type: Optional[Callable]
install_cmd_provider=None, # type: Optional[TShimmedFunc]
install_cmd=None, # type: Optional[TCommandInstance]
use_pep517=True, # type: bool
):
# (...) -> TResolver
"""
A resolver creation compatibility shim for generating a resolver.
Consumes any argument that was previously used to instantiate a
resolver, discards anything that is no longer valid.
.. note:: This is only valid for **pip >= 10.0.0**
:raises ValueError: A session is required but not provided and one cannot be created
:raises ValueError: A finder is required but not provided and one cannot be created
:raises ValueError: An install requirement provider is required and has not been
provided
:param TShimmedFunc resolver_fn: The resolver function used to create new resolver
instances.
:param TShimmedFunc install_req_provider: The provider function to use to generate
install requirements if needed.
:param TShimmedFunc format_control_provider: The provider function to use to generate
a format_control instance if needed.
:param TShimmedFunc wheel_cache_provider: The provider function to use to generate
a wheel cache if needed.
:param Optional[TFinder] finder: The package finder to use during resolution,
defaults to None.
:param str upgrade_strategy: Upgrade strategy to use, defaults to ``only-if-needed``.
:param Optional[bool] force_reinstall: Whether to simulate or assume package
reinstallation during resolution, defaults to None
:param Optional[bool] ignore_dependencies: Whether to ignore package dependencies,
defaults to None
:param Optional[bool] ignore_requires_python: Whether to ignore indicated
required_python versions on packages, defaults to None
:param bool ignore_installed: Whether to ignore installed packages during resolution,
defaults to True
:param bool use_user_site: Whether to use the user site location during resolution,
defaults to False
:param Optional[bool] isolated: Whether to isolate the resolution process, defaults
to None
:param Optional[TWheelCache] wheel_cache: The wheel cache to use, defaults to None
:param Optional[TPreparer] preparer: The requirement preparer to use, defaults to
None
:param Optional[TSession] session: Existing session to use for getting requirements,
defaults to None
:param Optional[Values] options: Pip options to use if needed, defaults to None
:param Optional[functools.partial] make_install_req: The partial function to pass in
to the resolver for actually generating install requirements, if necessary
:param Optional[TCommandInstance] install_cmd: The install command used to create
the finder, session, and options if needed, defaults to None.
:param bool use_pep517: Whether to use the pep517 build process.
:return: A new resolver instance.
:rtype: :class:`~pip._internal.legacy_resolve.Resolver`
:Example:
>>> import os
>>> from tempdir import TemporaryDirectory
>>> from pip_shims.shims import (
... InstallCommand, get_package_finder, make_preparer, get_requirement_tracker,
... get_resolver, InstallRequirement, RequirementSet
... )
>>> install_cmd = InstallCommand()
>>> pip_options, _ = install_cmd.parser.parse_args([])
>>> session = install_cmd._build_session(pip_options)
>>> finder = get_package_finder(
... install_cmd, session=session, options=pip_options
... )
>>> wheel_cache = WheelCache(USER_CACHE_DIR, FormatControl(None, None))
>>> with TemporaryDirectory() as temp_base:
... reqset = RequirementSet()
... ireq = InstallRequirement.from_line("requests")
... ireq.is_direct = True
... build_dir = os.path.join(temp_base, "build")
... src_dir = os.path.join(temp_base, "src")
... ireq.build_location(build_dir)
... with make_preparer(
... options=pip_options, finder=finder, session=session,
... build_dir=build_dir, install_cmd=install_cmd,
... ) as preparer:
... resolver = get_resolver(
... finder=finder, ignore_dependencies=False, ignore_requires_python=True,
... preparer=preparer, session=session, options=pip_options,
... install_cmd=install_cmd, wheel_cache=wheel_cache,
... )
... resolver.require_hashes = False
... reqset.add_requirement(ireq)
... results = resolver.resolve(reqset)
... #reqset.cleanup_files()
... for result_req in reqset.requirements:
... print(result_req)
requests
chardet
certifi
urllib3
idna
"""
resolver_fn = resolve_possible_shim(resolver_fn)
install_req_provider = resolve_possible_shim(install_req_provider)
format_control_provider = resolve_possible_shim(format_control_provider)
wheel_cache_provider = resolve_possible_shim(wheel_cache_provider)
install_cmd_provider = resolve_possible_shim(install_cmd_provider)
required_args = inspect.getargs(resolver_fn.__init__.__code__).args # type: ignore
install_cmd_dependency_map = {"session": session, "finder": finder}
resolver_kwargs = {} # type: Dict[str, Any]
if install_cmd is None:
assert isinstance(install_cmd_provider, (type, functools.partial))
install_cmd = install_cmd_provider()
if options is None and install_cmd is not None:
options, _ = install_cmd.parser.parse_args([]) # type: ignore
for arg, val in install_cmd_dependency_map.items():
if arg not in required_args:
continue
elif val is None and install_cmd is None:
raise TypeError(
"Preparer requires a {} but did not receive one "
"and cannot generate one".format(arg)
)
elif arg == "session" and val is None:
val = get_session(install_cmd=install_cmd, options=options)
elif arg == "finder" and val is None:
val = get_package_finder(install_cmd, options=options, session=session)
resolver_kwargs[arg] = val
if "make_install_req" in required_args:
if make_install_req is None and install_req_provider is not None:
make_install_req_kwargs = {
"isolated": isolated,
"wheel_cache": wheel_cache,
"use_pep517": use_pep517,
}
factory_args, factory_kwargs = filter_allowed_args(
install_req_provider, **make_install_req_kwargs
)
make_install_req = functools.partial(
install_req_provider, *factory_args, **factory_kwargs
)
assert make_install_req is not None
resolver_kwargs["make_install_req"] = make_install_req
if "isolated" in required_args:
resolver_kwargs["isolated"] = isolated
resolver_kwargs.update(
{
"upgrade_strategy": upgrade_strategy,
"force_reinstall": force_reinstall,
"ignore_dependencies": ignore_dependencies,
"ignore_requires_python": ignore_requires_python,
"ignore_installed": ignore_installed,
"use_user_site": use_user_site,
"preparer": preparer,
}
)
if "wheel_cache" in required_args:
with _ensure_wheel_cache(
wheel_cache=wheel_cache,
wheel_cache_provider=wheel_cache_provider,
format_control_provider=format_control_provider,
options=options,
) as wheel_cache:
resolver_kwargs["wheel_cache"] = wheel_cache
return resolver_fn(**resolver_kwargs) # type: ignore
return resolver_fn(**resolver_kwargs) # type: ignore
[docs]def resolve( # noqa:C901
ireq, # type: TInstallRequirement
reqset_provider=None, # type: Optional[TShimmedFunc]
req_tracker_provider=None, # type: Optional[TShimmedFunc]
install_cmd_provider=None, # type: Optional[TShimmedFunc]
install_command=None, # type: Optional[TCommand]
finder_provider=None, # type: Optional[TShimmedFunc]
resolver_provider=None, # type: Optional[TShimmedFunc]
wheel_cache_provider=None, # type: Optional[TShimmedFunc]
format_control_provider=None, # type: Optional[TShimmedFunc]
make_preparer_provider=None, # type: Optional[TShimmedFunc]
tempdir_manager_provider=None, # type: Optional[TShimmedFunc]
options=None, # type: Optional[Values]
session=None, # type: Optional[TSession]
resolver=None, # type: Optional[TResolver]
finder=None, # type: Optional[TFinder]
upgrade_strategy="to-satisfy-only", # type: str
force_reinstall=None, # type: Optional[bool]
ignore_dependencies=None, # type: Optional[bool]
ignore_requires_python=None, # type: Optional[bool]
ignore_installed=True, # type: bool
use_user_site=False, # type: bool
isolated=None, # type: Optional[bool]
build_dir=None, # type: Optional[str]
source_dir=None, # type: Optional[str]
download_dir=None, # type: Optional[str]
cache_dir=None, # type: Optional[str]
wheel_download_dir=None, # type: Optional[str]
wheel_cache=None, # type: Optional[TWheelCache]
require_hashes=None, # type: bool
check_supported_wheels=True, # type: bool
):
# (...) -> Set[TInstallRequirement]
"""
Resolves the provided **InstallRequirement**, returning a dictionary.
Maps a dictionary of names to corresponding ``InstallRequirement`` values.
:param :class:`~pip._internal.req.req_install.InstallRequirement` ireq: An
InstallRequirement to initiate the resolution process
:param :class:`~pip_shims.models.ShimmedPathCollection` reqset_provider: A provider
to build requirement set instances.
:param :class:`~pip_shims.models.ShimmedPathCollection` req_tracker_provider: A
provider to build requirement tracker instances
:param install_cmd_provider: A shim for providing new install command instances.
:type install_cmd_provider: :class:`~pip_shims.models.ShimmedPathCollection`
:param Optional[TCommandInstance] install_command: The install command used to
create the finder, session, and options if needed, defaults to None.
:param :class:`~pip_shims.models.ShimmedPathCollection` finder_provider: A provider
to package finder instances.
:param :class:`~pip_shims.models.ShimmedPathCollection` resolver_provider: A provider
to build resolver instances
:param TShimmedFunc wheel_cache_provider: The provider function to use to generate a
wheel cache if needed.
:param TShimmedFunc format_control_provider: The provider function to use to generate
a format_control instance if needed.
:param TShimmedFunc make_preparer_provider: Callable or shim for generating preparers.
:param Optional[TShimmedFunc] tempdir_manager_provider: Shim for generating tempdir
manager for pip temporary directories
:param Optional[Values] options: Pip options to use if needed, defaults to None
:param Optional[TSession] session: Existing session to use for getting requirements,
defaults to None
:param :class:`~pip._internal.legacy_resolve.Resolver` resolver: A pre-existing
resolver instance to use for resolution
:param Optional[TFinder] finder: The package finder to use during resolution,
defaults to None.
:param str upgrade_strategy: Upgrade strategy to use, defaults to ``only-if-needed``.
:param Optional[bool] force_reinstall: Whether to simulate or assume package
reinstallation during resolution, defaults to None
:param Optional[bool] ignore_dependencies: Whether to ignore package dependencies,
defaults to None
:param Optional[bool] ignore_requires_python: Whether to ignore indicated
required_python versions on packages, defaults to None
:param bool ignore_installed: Whether to ignore installed packages during
resolution, defaults to True
:param bool use_user_site: Whether to use the user site location during resolution,
defaults to False
:param Optional[bool] isolated: Whether to isolate the resolution process, defaults
to None
:param Optional[str] build_dir: Directory for building packages and wheels, defaults
to None
:param str source_dir: The directory to use for source requirements. Removed in pip
10, defaults to None
:param Optional[str] download_dir: Target directory to download files, defaults to
None
:param str cache_dir: The cache directory to use for caching artifacts during
resolution
:param Optional[str] wheel_download_dir: Target directoryto download wheels, defaults
to None
:param Optional[TWheelCache] wheel_cache: The wheel cache to use, defaults to None
:param bool require_hashes: Whether to require hashes when resolving. Defaults to
False.
:param bool check_supported_wheels: Whether to check support of wheels before including
them in resolution.
:return: A dictionary mapping requirements to corresponding
:class:`~pip._internal.req.req_install.InstallRequirement`s
:rtype: :class:`~pip._internal.req.req_install.InstallRequirement`
:Example:
>>> from pip_shims.shims import resolve, InstallRequirement
>>> ireq = InstallRequirement.from_line("requests>=2.20")
>>> results = resolve(ireq)
>>> for k, v in results.items():
... print("{0}: {1!r}".format(k, v))
requests: <InstallRequirement object: requests>=2.20 from https://files.pythonhosted.
org/packages/51/bd/23c926cd341ea6b7dd0b2a00aba99ae0f828be89d72b2190f27c11d4b7fb/reque
sts-2.22.0-py2.py3-none-any.whl#sha256=9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590
f48c010551dc6c4b31 editable=False>
idna: <InstallRequirement object: idna<2.9,>=2.5 from https://files.pythonhosted.org/
packages/14/2c/cd551d81dbe15200be1cf41cd03869a46fe7226e7450af7a6545bfc474c9/idna-2.8-
py2.py3-none-any.whl#sha256=ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432
f7e4a3c (from requests>=2.20) editable=False>
urllib3: <InstallRequirement object: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 from htt
ps://files.pythonhosted.org/packages/b4/40/a9837291310ee1ccc242ceb6ebfd9eb21539649f19
3a7c8c86ba15b98539/urllib3-1.25.7-py2.py3-none-any.whl#sha256=a8a318824cc77d1fd4b2bec
2ded92646630d7fe8619497b142c84a9e6f5a7293 (from requests>=2.20) editable=False>
chardet: <InstallRequirement object: chardet<3.1.0,>=3.0.2 from https://files.pythonh
osted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8
/chardet-3.0.4-py2.py3-none-any.whl#sha256=fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed
4531e3e15460124c106691 (from requests>=2.20) editable=False>
certifi: <InstallRequirement object: certifi>=2017.4.17 from https://files.pythonhost
ed.org/packages/18/b0/8146a4f8dd402f60744fa380bc73ca47303cccf8b9190fd16a827281eac2/ce
rtifi-2019.9.11-py2.py3-none-any.whl#sha256=fd7c7c74727ddcf00e9acd26bba8da604ffec95bf
1c2144e67aff7a8b50e6cef (from requests>=2.20) editable=False>
"""
reqset_provider = resolve_possible_shim(reqset_provider)
finder_provider = resolve_possible_shim(finder_provider)
resolver_provider = resolve_possible_shim(resolver_provider)
wheel_cache_provider = resolve_possible_shim(wheel_cache_provider)
format_control_provider = resolve_possible_shim(format_control_provider)
make_preparer_provider = resolve_possible_shim(make_preparer_provider)
req_tracker_provider = resolve_possible_shim(req_tracker_provider)
install_cmd_provider = resolve_possible_shim(install_cmd_provider)
tempdir_manager_provider = resolve_possible_shim(tempdir_manager_provider)
if install_command is None:
assert isinstance(install_cmd_provider, (type, functools.partial))
install_command = install_cmd_provider()
kwarg_map = {
"upgrade_strategy": upgrade_strategy,
"force_reinstall": force_reinstall,
"ignore_dependencies": ignore_dependencies,
"ignore_requires_python": ignore_requires_python,
"ignore_installed": ignore_installed,
"use_user_site": use_user_site,
"isolated": isolated,
"build_dir": build_dir,
"src_dir": source_dir,
"download_dir": download_dir,
"require_hashes": require_hashes,
"cache_dir": cache_dir,
}
kwargs, options = populate_options(install_command, options, **kwarg_map)
with ExitStack() as ctx:
ctx.enter_context(tempdir_manager_provider())
kwargs = ctx.enter_context(
ensure_resolution_dirs(wheel_download_dir=wheel_download_dir, **kwargs)
)
wheel_download_dir = kwargs.pop("wheel_download_dir")
if session is None:
session = get_session(install_cmd=install_command, options=options)
if finder is None:
finder = finder_provider(
install_command, options=options, session=session
) # type: ignore
format_control = getattr(options, "format_control", None)
if not format_control:
format_control = format_control_provider(None, None) # type: ignore
wheel_cache = ctx.enter_context(
wheel_cache_provider(kwargs["cache_dir"], format_control)
) # type: ignore
ireq.is_direct = True # type: ignore
build_location_kwargs = {
"build_dir": kwargs["build_dir"],
"autodelete": True,
"parallel_builds": False
}
call_function_with_correct_args(ireq.build_location, **build_location_kwargs)
if reqset_provider is None:
raise TypeError(
"cannot resolve without a requirement set provider... failed!"
)
reqset = reqset_provider(
install_command,
options=options,
session=session,
wheel_download_dir=wheel_download_dir,
**kwargs
) # type: ignore
if getattr(reqset, "prepare_files", None):
reqset.add_requirement(ireq)
results = reqset.prepare_files(finder)
result = reqset.requirements
reqset.cleanup_files()
return result
if make_preparer_provider is None:
raise TypeError("Cannot create requirement preparer, cannot resolve!")
preparer_args = {
"build_dir": kwargs["build_dir"],
"src_dir": kwargs["src_dir"],
"download_dir": kwargs["download_dir"],
"wheel_download_dir": wheel_download_dir,
"build_isolation": kwargs["isolated"],
"install_cmd": install_command,
"options": options,
"finder": finder,
"session": session,
"use_user_site": use_user_site,
"require_hashes": require_hashes,
}
if isinstance(req_tracker_provider, (types.FunctionType, functools.partial)):
preparer_args["req_tracker"] = ctx.enter_context(req_tracker_provider())
resolver_keys = [
"upgrade_strategy",
"force_reinstall",
"ignore_dependencies",
"ignore_installed",
"use_user_site",
"isolated",
"use_user_site",
]
resolver_args = {key: kwargs[key] for key in resolver_keys if key in kwargs}
if resolver_provider is None:
raise TypeError("Cannot resolve without a resolver provider... failed!")
preparer = ctx.enter_context(make_preparer_provider(**preparer_args))
resolver = resolver_provider(
finder=finder,
preparer=preparer,
session=session,
options=options,
install_cmd=install_command,
wheel_cache=wheel_cache,
**resolver_args
) # type: ignore
resolver.require_hashes = kwargs.get("require_hashes", False) # type: ignore
_, required_resolver_args = get_method_args(resolver.resolve)
resolver_args = []
if "requirement_set" in required_resolver_args.args:
reqset.add_requirement(ireq)
resolver_args.append(reqset)
elif "root_reqs" in required_resolver_args.args:
resolver_args.append([ireq])
if "check_supported_wheels" in required_resolver_args.args:
resolver_args.append(check_supported_wheels)
result_reqset = resolver.resolve(*resolver_args) # type: ignore
if result_reqset is None:
result_reqset = reqset
results = result_reqset.requirements
cleanup_fn = getattr(reqset, "cleanup_files", None)
if cleanup_fn is not None:
cleanup_fn()
return results
[docs]def build_wheel( # noqa:C901
req=None, # type: Optional[TInstallRequirement]
reqset=None, # type: Optional[Union[TReqSet, Iterable[TInstallRequirement]]]
output_dir=None, # type: Optional[str]
preparer=None, # type: Optional[TPreparer]
wheel_cache=None, # type: Optional[TWheelCache]
build_options=None, # type: Optional[List[str]]
global_options=None, # type: Optional[List[str]]
check_binary_allowed=None, # type: Optional[Callable[TInstallRequirement, bool]]
no_clean=False, # type: bool
session=None, # type: Optional[TSession]
finder=None, # type: Optional[TFinder]
install_command=None, # type: Optional[TCommand]
req_tracker=None, # type: Optional[TReqTracker]
build_dir=None, # type: Optional[str]
src_dir=None, # type: Optional[str]
download_dir=None, # type: Optional[str]
wheel_download_dir=None, # type: Optional[str]
cache_dir=None, # type: Optional[str]
use_user_site=False, # type: bool
use_pep517=None, # type: Optional[bool]
format_control_provider=None, # type: Optional[TShimmedFunc]
wheel_cache_provider=None, # type: Optional[TShimmedFunc]
preparer_provider=None, # type: Optional[TShimmedFunc]
wheel_builder_provider=None, # type: Optional[TShimmedFunc]
build_one_provider=None, # type: Optional[TShimmedFunc]
build_one_inside_env_provider=None, # type: Optional[TShimmedFunc]
build_many_provider=None, # type: Optional[TShimmedFunc]
install_command_provider=None, # type: Optional[TShimmedFunc]
finder_provider=None, # type: Optional[TShimmedFunc]
reqset_provider=None, # type: Optional[TShimmedFunc]
):
# type: (...) -> Generator[Union[str, Tuple[List[TInstallRequirement], ...]], None, None]
"""
Build a wheel or a set of wheels
:raises TypeError: Raised when no requirements are provided
:param Optional[TInstallRequirement] req: An `InstallRequirement` to build
:param Optional[TReqSet] reqset: A `RequirementSet` instance (`pip<10`) or an
iterable of `InstallRequirement` instances (`pip>=10`) to build
:param Optional[str] output_dir: Target output directory, only useful when building
one wheel using pip>=20.0
:param Optional[TPreparer] preparer: A preparer instance, defaults to None
:param Optional[TWheelCache] wheel_cache: A wheel cache instance, defaults to None
:param Optional[List[str]] build_options: A list of build options to pass in
:param Optional[List[str]] global_options: A list of global options to pass in
:param Optional[Callable[TInstallRequirement, bool]] check_binary_allowed: A callable
to check whether we are allowed to build and cache wheels for an ireq
:param bool no_clean: Whether to avoid cleaning up wheels
:param Optional[TSession] session: A `PipSession` instance to pass to create a
`finder` if necessary
:param Optional[TFinder] finder: A `PackageFinder` instance to use for generating a
`WheelBuilder` instance on `pip<20`
:param Optional[TCommandInstance] install_command: The install command used to
create the finder, session, and options if needed, defaults to None.
:param Optional[TReqTracker] req_tracker: An optional requirement tracker instance,
if one already exists
:param Optional[str] build_dir: Passthrough parameter for building preparer
:param Optional[str] src_dir: Passthrough parameter for building preparer
:param Optional[str] download_dir: Passthrough parameter for building preparer
:param Optional[str] wheel_download_dir: Passthrough parameter for building preparer
:param Optional[str] cache_dir: Passthrough cache directory for wheel cache options
:param bool use_user_site: Whether to use the user site directory when preparing
install requirements on `pip<20`
:param Optional[bool] use_pep517: When set to *True* or *False*, prefers building
with or without pep517 as specified, otherwise uses requirement preference.
Only works for single requirements.
:param Optional[TShimmedFunc] format_control_provider: A provider for the
`FormatControl` class
:param Optional[TShimmedFunc] wheel_cache_provider: A provider for the `WheelCache`
class
:param Optional[TShimmedFunc] preparer_provider: A provider for the
`RequirementPreparer` class
:param Optional[TShimmedFunc] wheel_builder_provider: A provider for the
`WheelBuilder` class, if it exists
:param Optional[TShimmedFunc] build_one_provider: A provider for the `_build_one`
function, if it exists
:param Optional[TShimmedFunc] build_one_inside_env_provider: A provider for the
`_build_one_inside_env` function, if it exists
:param Optional[TShimmedFunc] build_many_provider: A provider for the `build`
function, if it exists
:param Optional[TShimmedFunc] install_command_provider: A shim for providing new
install command instances
:param TShimmedFunc finder_provider: A provider to package finder instances
:param TShimmedFunc reqset_provider: A provider for requirement set generation
:return: A tuple of successful and failed install requirements or else a path to
a wheel
:rtype: Optional[Union[str, Tuple[List[TInstallRequirement], List[TInstallRequirement]]]]
"""
wheel_cache_provider = resolve_possible_shim(wheel_cache_provider)
preparer_provider = resolve_possible_shim(preparer_provider)
wheel_builder_provider = resolve_possible_shim(wheel_builder_provider)
build_one_provider = resolve_possible_shim(build_one_provider)
build_one_inside_env_provider = resolve_possible_shim(build_one_inside_env_provider)
build_many_provider = resolve_possible_shim(build_many_provider)
install_cmd_provider = resolve_possible_shim(install_command_provider)
format_control_provider = resolve_possible_shim(format_control_provider)
finder_provider = resolve_possible_shim(finder_provider) or get_package_finder
reqset_provider = resolve_possible_shim(reqset_provider)
global_options = [] if global_options is None else global_options
build_options = [] if build_options is None else build_options
options = None
kwarg_map = {
"cache_dir": cache_dir,
"src_dir": src_dir,
"download_dir": download_dir,
"wheel_download_dir": wheel_download_dir,
"build_dir": build_dir,
"use_user_site": use_user_site,
}
if not req and not reqset:
raise TypeError("Must provide either a requirement or requirement set to build")
with ExitStack() as ctx:
kwargs = kwarg_map.copy()
if wheel_cache is None and (reqset is not None or output_dir is None):
if install_command is None:
assert isinstance(install_cmd_provider, (type, functools.partial))
install_command = install_cmd_provider()
kwargs, options = populate_options(install_command, options, **kwarg_map)
format_control = getattr(options, "format_control", None)
if not format_control:
format_control = format_control_provider(None, None) # type: ignore
wheel_cache = ctx.enter_context(
wheel_cache_provider(options.cache_dir, format_control)
)
if req and not reqset and not output_dir:
output_dir = get_ireq_output_path(wheel_cache, req)
if not reqset and build_one_provider:
yield build_one_provider(req, output_dir, build_options, global_options)
elif build_many_provider:
yield build_many_provider(
reqset, wheel_cache, build_options, global_options, check_binary_allowed
)
else:
builder_args, builder_kwargs = get_allowed_args(wheel_builder_provider)
if "requirement_set" in builder_args and not reqset:
reqset = reqset_provider()
if session is None and finder is None:
session = get_session(install_cmd=install_command, options=options)
finder = finder_provider(
install_command, options=options, session=session
)
if preparer is None:
preparer_kwargs = {
"build_dir": kwargs["build_dir"],
"src_dir": kwargs["src_dir"],
"download_dir": kwargs["download_dir"],
"wheel_download_dir": kwargs["wheel_download_dir"],
"finder": finder,
"session": session
if session
else get_session(install_cmd=install_command, options=options),
"install_cmd": install_command,
"options": options,
"use_user_site": use_user_site,
"req_tracker": req_tracker,
}
preparer = ctx.enter_context(preparer_provider(**preparer_kwargs))
check_bin = check_binary_allowed if check_binary_allowed else lambda x: True
builder_kwargs = {
"requirement_set": reqset,
"finder": finder,
"preparer": preparer,
"wheel_cache": wheel_cache,
"no_clean": no_clean,
"build_options": build_options,
"global_options": global_options,
"check_binary_allowed": check_bin,
}
builder = call_function_with_correct_args(
wheel_builder_provider, **builder_kwargs
)
if req and not reqset:
if not output_dir:
output_dir = get_ireq_output_path(wheel_cache, req)
if use_pep517 is not None:
req.use_pep517 = use_pep517
yield builder._build_one(req, output_dir)
else:
yield builder.build(reqset)