11from optparse
import Values
12from typing
import Any, Collection, Dict, Iterable, List, Optional, Sequence, Union
27 get_default_environment,
28 get_directory_distribution,
29 get_wheel_distribution,
37 generate_metadata
as generate_metadata_legacy,
40 install_editable
as install_editable_legacy,
48 ConfiguredBuildBackendHookCaller,
66 Represents something that may be installed later on, may have information
67 about where to fetch the relevant requirement and also contains logic for
68 installing the said requirement.
73 req: Optional[Requirement],
74 comes_from: Optional[Union[str,
"InstallRequirement"]],
75 editable: bool =
False,
76 link: Optional[Link] =
None,
77 markers: Optional[Marker] =
None,
78 use_pep517: Optional[bool] =
None,
79 isolated: bool =
False,
81 global_options: Optional[List[str]] =
None,
82 hash_options: Optional[Dict[str, List[str]]] =
None,
83 config_settings: Optional[Dict[str, Union[str, List[str]]]] =
None,
84 constraint: bool =
False,
85 extras: Collection[str] = (),
86 user_supplied: bool =
False,
87 permit_editable_wheels: bool =
False,
89 assert req
is None or isinstance(req, Requirement), req
109 if link
is None and req
and req.url:
112 self.
link = self.original_link = link
117 self.cached_wheel_source_link: Optional[Link] =
None
121 self.download_info: Optional[DirectUrl] =
None
134 if markers
is None and req:
190 s +=
" from {}".format(redact_auth_from_url(self.
link.url))
192 s = redact_auth_from_url(self.
link.url)
194 s =
"<InstallRequirement>"
199 location =
"<memory>"
200 s += f
" in {location}"
207 s += f
" (from {comes_from})"
211 return "<{} object: {} editable={!r}>".format(
212 self.__class__.__name__, str(self), self.
editable
216 """An un-tested helper for getting state, for debugging."""
217 attributes =
vars(self)
218 names = sorted(attributes)
220 state = (
"{}={!r}".format(attr, attributes[attr])
for attr
in sorted(names))
221 return "<{name} object: {{{state}}}>".format(
222 name=self.__class__.__name__,
223 state=
", ".join(state),
228 def name(self) -> Optional[str]:
233 @functools.lru_cache()
239 runner = runner_with_spinner_message(
240 "Checking if build backend supports build_editable"
243 return "build_editable" in self.
pep517_backend._supported_features()
247 return self.
req.specifier
251 """Whether this requirement was specified as a direct URL."""
252 return self.original_link
is not None
256 """Return whether I am pinned to an exact version.
258 For example, some-package==1.2 is pinned; some-package>1.2 is not.
261 return len(specifiers) == 1
and next(iter(specifiers)).operator
in {
"==",
"==="}
263 def match_markers(self, extras_requested: Optional[Iterable[str]] =
None) -> bool:
264 if not extras_requested:
267 extras_requested = (
"",)
270 self.
markers.evaluate({
"extra": extra})
for extra
in extras_requested
277 """Return whether any known-good hashes are specified as options.
279 These activate --require-hashes mode; hashes specified as part of a
285 def hashes(self, trust_internet: bool =
True) -> Hashes:
286 """Return a hash-comparer that considers my option- and URL-based
287 hashes to be known-good.
289 Hashes in URLs--ones embedded in the requirements file, not ones
290 downloaded from an index server--are almost peers with ones from
291 flags. They satisfy --require-hashes (whether it was implicitly or
292 explicitly activated) but do not activate it. md5 and sha224 are not
293 allowed in flags, which should nudge people toward good algos. We
294 always OR all hashes together, even ones from URLs.
296 :param trust_internet: Whether to trust URL-based (#md5=...) hashes
297 downloaded from the internet, as by populate_link()
304 link = self.original_link
309 return Hashes(good_hashes)
312 """Format a nice indicator to show where this "comes from" """
322 s +=
"->" + comes_from
326 self, build_dir: str, autodelete: bool, parallel_builds: bool
328 assert build_dir
is not None
348 dir_name: str = canonicalize_name(self.
namename)
350 dir_name = f
"{dir_name}_{uuid.uuid4().hex}"
360 delete_arg =
None if autodelete
else False
362 path=actual_build_dir,
365 globally_managed=
True,
369 """Set requirement after generating metadata."""
370 assert self.
req is None
391 metadata_name = canonicalize_name(self.
metadata[
"Name"])
392 if canonicalize_name(self.
req.name) == metadata_name:
398 "Generating metadata for package %s "
399 "produced metadata for project name %s. Fix your "
400 "#egg=%s fragments.",
408 """Find an installed distribution that satisfies or conflicts
409 with this requirement, and set self.satisfied_by or
410 self.should_reinstall appropriately.
414 existing_dist = get_default_environment().get_distribution(self.
req.name)
415 if not existing_dist:
422 if not version_compatible:
429 f
"Will not install to the user site because it will "
430 f
"lack sys.path precedence to {existing_dist.raw_name} "
431 f
"in {existing_dist.location}"
449 return self.
link.is_wheel
455 return self.cached_wheel_source_link
is not None
466 assert self.
source_dir, f
"No source dir for {self}"
473 assert self.
source_dir, f
"No source dir for {self}"
480 assert self.
source_dir, f
"No source dir for {self}"
483 def load_pyproject_toml(self) -> None:
484 """Load the pyproject.toml file.
486 After calling this routine, all of the attributes related to PEP 517
487 processing for this requirement have been set. In particular, the
488 use_pep517 attribute can be used to determine whether we should
489 follow the PEP 517 or legacy (setup.py) code path.
491 pyproject_toml_data = load_pyproject_toml(
495 if pyproject_toml_data
is None:
498 reason=f
"Config settings are ignored for project {self}.",
500 "to use --use-pep517 or add a "
501 "pyproject.toml file to the project"
509 requires, backend, check, backend_path = pyproject_toml_data
516 backend_path=backend_path,
520 """Check that an editable requirement if valid for use with PEP 517/518.
522 This verifies that an editable that has a pyproject.toml either supports PEP 660
523 or as a setup.py or a setup.cfg
533 f
"Project {self} has a 'pyproject.toml' and its build "
534 f
"backend is missing the 'build_editable' hook. Since it does not "
535 f
"have a 'setup.py' nor a 'setup.cfg', "
536 f
"it cannot be installed in editable mode. "
537 f
"Consider using a build backend that supports PEP 660."
541 """Ensure that project metadata is available.
543 Under PEP 517 and PEP 660, call the backend hook to prepare the metadata.
544 Under legacy processing, call setup.py egg-info.
547 details = self.
namename or f
"from {self.link}"
557 build_env=self.build_env,
563 build_env=self.build_env,
569 build_env=self.build_env,
585 def metadata(self) -> Any:
586 if not hasattr(self,
"_metadata"):
595 return get_wheel_distribution(
598 raise AssertionError(
599 f
"InstallRequirement {self} has no metadata directory and no wheel: "
600 f
"can't make a distribution."
606 if self.
req.specifier
and version
not in self.
req.specifier:
608 "Requested %s, but installing version %s",
614 "Source in %s has version %s, which satisfies requirement %s",
624 autodelete: bool =
False,
625 parallel_builds: bool =
False,
627 """Ensure that a source_dir is set.
629 This will create a temporary build dir if the name of the requirement
632 :param parent_dir: The ideal pip parent_dir for the source_dir.
633 Generally src_dir for editables and build_dir for sdists.
634 :return: self.source_dir
639 autodelete=autodelete,
640 parallel_builds=parallel_builds,
647 "Cannot update repository at %s; repository location is unknown",
653 if self.
link.scheme ==
"file":
659 assert vcs_backend, f
"Unsupported VCS URL {self.link.url}"
660 hidden_url = hide_url(self.
link.url)
665 self, auto_confirm: bool =
False, verbose: bool =
False
666 ) -> Optional[UninstallPathSet]:
668 Uninstall the distribution currently satisfying this requirement.
670 Prompts before removing or modifying files unless
671 ``auto_confirm`` is True.
673 Refuses to delete or modify files outside of ``sys.prefix`` -
674 thus uninstallation within a virtual environment can only
675 modify that virtual environment, even if the virtualenv is
676 linked to global site-packages.
680 dist = get_default_environment().get_distribution(self.
req.name)
684 logger.info(
"Found existing installation: %s", dist)
688 return uninstalled_pathset
694 ), f
"name {name!r} doesn't start with prefix {prefix!r}"
695 name = name[
len(prefix) + 1 :]
703 def archive(self, build_dir: Optional[str]) ->
None:
704 """Saves archive to provided build_dir.
706 Used for saving downloaded VCS requirements as part of `pip download`.
709 if build_dir
is None:
712 create_archive =
True
717 response = ask_path_exists(
718 "The file {} exists. (i)gnore, (w)ipe, "
719 "(b)ackup, (a)bort ".format(display_path(archive_path)),
720 (
"i",
"w",
"b",
"a"),
723 create_archive =
False
724 elif response ==
"w":
727 elif response ==
"b":
728 dest_file = backup_dir(archive_path)
730 "Backing up %s to %s",
731 display_path(archive_path),
732 display_path(dest_file),
735 elif response ==
"a":
738 if not create_archive:
749 for dirpath, dirnames, filenames
in os.walk(dir):
750 for dirname
in dirnames:
759 for filename
in filenames:
768 logger.info(
"Saved %s", display_path(archive_path))
772 global_options: Optional[Sequence[str]] =
None,
773 root: Optional[str] =
None,
774 home: Optional[str] =
None,
775 prefix: Optional[str] =
None,
776 warn_script_location: bool =
True,
777 use_user_site: bool =
False,
778 pycompile: bool =
True,
790 install_editable_legacy(
791 global_options=global_options
if global_options
is not None else [],
794 use_user_site=use_user_site,
798 build_env=self.build_env,
811 req_description=str(self.
req),
813 warn_script_location=warn_script_location,
814 direct_url=self.download_info
if self.
is_direct else None,
820def check_invalid_constraint_type(req: InstallRequirement) -> str:
824 problem =
"Unnamed requirements are not allowed as constraints"
826 problem =
"Editable requirements are not allowed as constraints"
828 problem =
"Constraints cannot have extras"
833 "Constraints are only allowed to take the form of a package "
834 "name and a version specifier. Other forms were originally "
835 "permitted as an accident of the implementation, but were "
836 "undocumented. The new implementation of the resolver no "
837 "longer supports these forms."
839 replacement=
"replacing the constraint with a requirement",
848def _has_option(options: Values, reqs: List[InstallRequirement], option: str) -> bool:
849 if getattr(options, option,
None):
857def check_legacy_setup_py_options(
859 reqs: List[InstallRequirement],
861 has_build_options =
_has_option(options, reqs,
"build_options")
862 has_global_options =
_has_option(options, reqs,
"global_options")
863 if has_build_options
or has_global_options:
865 reason=
"--build-option and --global-option are deprecated.",
867 replacement=
"to use --config-settings",
871 "Implying --no-binary=:all: due to the presence of "
872 "--build-option / --global-option. "
None __init__(self, Optional[Requirement] req, Optional[Union[str, "InstallRequirement"]] comes_from, bool editable=False, Optional[Link] link=None, Optional[Marker] markers=None, Optional[bool] use_pep517=None, bool isolated=False, *Optional[List[str]] global_options=None, Optional[Dict[str, List[str]]] hash_options=None, Optional[Dict[str, Union[str, List[str]]]] config_settings=None, bool constraint=False, Collection[str] extras=(), bool user_supplied=False, bool permit_editable_wheels=False)
str _get_archive_name(self, str path, str parentdir, str rootdir)
bool has_hash_options(self)
unpacked_source_directory
None prepare_metadata(self)
None update_editable(self)
None warn_on_mismatching_name(self)
BaseDistribution get_dist(self)
None check_if_exists(self, bool use_user_site)
str pyproject_toml_path(self)
bool is_wheel_from_cache(self)
None _set_requirement(self)
SpecifierSet specifier(self)
None ensure_has_source_dir(self, str parent_dir, bool autodelete=False, bool parallel_builds=False)
Hashes hashes(self, bool trust_internet=True)
bool match_markers(self, Optional[Iterable[str]] extras_requested=None)
None assert_source_matches_version(self)
Optional[str] from_path(self)
str unpacked_source_directory(self)
Optional[UninstallPathSet] uninstall(self, bool auto_confirm=False, bool verbose=False)
str ensure_build_location(self, str build_dir, bool autodelete, bool parallel_builds)
bool supports_pyproject_editable(self)
None isolated_editable_sanity_check(self)
None archive(self, Optional[str] build_dir)
bool _has_option(Values options, List[InstallRequirement] reqs, str option)