Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/pip/_internal/operations/check.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
| author | guerler |
|---|---|
| date | Fri, 31 Jul 2020 00:32:28 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 0:d30785e31577 | 1:56ad4e20f292 |
|---|---|
| 1 """Validation of dependencies of packages | |
| 2 """ | |
| 3 | |
| 4 import logging | |
| 5 from collections import namedtuple | |
| 6 | |
| 7 from pip._vendor.packaging.utils import canonicalize_name | |
| 8 from pip._vendor.pkg_resources import RequirementParseError | |
| 9 | |
| 10 from pip._internal.distributions import ( | |
| 11 make_distribution_for_install_requirement, | |
| 12 ) | |
| 13 from pip._internal.utils.misc import get_installed_distributions | |
| 14 from pip._internal.utils.typing import MYPY_CHECK_RUNNING | |
| 15 | |
| 16 logger = logging.getLogger(__name__) | |
| 17 | |
| 18 if MYPY_CHECK_RUNNING: | |
| 19 from pip._internal.req.req_install import InstallRequirement | |
| 20 from typing import ( | |
| 21 Any, Callable, Dict, Optional, Set, Tuple, List | |
| 22 ) | |
| 23 | |
| 24 # Shorthands | |
| 25 PackageSet = Dict[str, 'PackageDetails'] | |
| 26 Missing = Tuple[str, Any] | |
| 27 Conflicting = Tuple[str, str, Any] | |
| 28 | |
| 29 MissingDict = Dict[str, List[Missing]] | |
| 30 ConflictingDict = Dict[str, List[Conflicting]] | |
| 31 CheckResult = Tuple[MissingDict, ConflictingDict] | |
| 32 | |
| 33 PackageDetails = namedtuple('PackageDetails', ['version', 'requires']) | |
| 34 | |
| 35 | |
| 36 def create_package_set_from_installed(**kwargs): | |
| 37 # type: (**Any) -> Tuple[PackageSet, bool] | |
| 38 """Converts a list of distributions into a PackageSet. | |
| 39 """ | |
| 40 # Default to using all packages installed on the system | |
| 41 if kwargs == {}: | |
| 42 kwargs = {"local_only": False, "skip": ()} | |
| 43 | |
| 44 package_set = {} | |
| 45 problems = False | |
| 46 for dist in get_installed_distributions(**kwargs): | |
| 47 name = canonicalize_name(dist.project_name) | |
| 48 try: | |
| 49 package_set[name] = PackageDetails(dist.version, dist.requires()) | |
| 50 except RequirementParseError as e: | |
| 51 # Don't crash on broken metadata | |
| 52 logging.warning("Error parsing requirements for %s: %s", name, e) | |
| 53 problems = True | |
| 54 return package_set, problems | |
| 55 | |
| 56 | |
| 57 def check_package_set(package_set, should_ignore=None): | |
| 58 # type: (PackageSet, Optional[Callable[[str], bool]]) -> CheckResult | |
| 59 """Check if a package set is consistent | |
| 60 | |
| 61 If should_ignore is passed, it should be a callable that takes a | |
| 62 package name and returns a boolean. | |
| 63 """ | |
| 64 if should_ignore is None: | |
| 65 def should_ignore(name): | |
| 66 return False | |
| 67 | |
| 68 missing = dict() | |
| 69 conflicting = dict() | |
| 70 | |
| 71 for package_name in package_set: | |
| 72 # Info about dependencies of package_name | |
| 73 missing_deps = set() # type: Set[Missing] | |
| 74 conflicting_deps = set() # type: Set[Conflicting] | |
| 75 | |
| 76 if should_ignore(package_name): | |
| 77 continue | |
| 78 | |
| 79 for req in package_set[package_name].requires: | |
| 80 name = canonicalize_name(req.project_name) # type: str | |
| 81 | |
| 82 # Check if it's missing | |
| 83 if name not in package_set: | |
| 84 missed = True | |
| 85 if req.marker is not None: | |
| 86 missed = req.marker.evaluate() | |
| 87 if missed: | |
| 88 missing_deps.add((name, req)) | |
| 89 continue | |
| 90 | |
| 91 # Check if there's a conflict | |
| 92 version = package_set[name].version # type: str | |
| 93 if not req.specifier.contains(version, prereleases=True): | |
| 94 conflicting_deps.add((name, version, req)) | |
| 95 | |
| 96 if missing_deps: | |
| 97 missing[package_name] = sorted(missing_deps, key=str) | |
| 98 if conflicting_deps: | |
| 99 conflicting[package_name] = sorted(conflicting_deps, key=str) | |
| 100 | |
| 101 return missing, conflicting | |
| 102 | |
| 103 | |
| 104 def check_install_conflicts(to_install): | |
| 105 # type: (List[InstallRequirement]) -> Tuple[PackageSet, CheckResult] | |
| 106 """For checking if the dependency graph would be consistent after \ | |
| 107 installing given requirements | |
| 108 """ | |
| 109 # Start from the current state | |
| 110 package_set, _ = create_package_set_from_installed() | |
| 111 # Install packages | |
| 112 would_be_installed = _simulate_installation_of(to_install, package_set) | |
| 113 | |
| 114 # Only warn about directly-dependent packages; create a whitelist of them | |
| 115 whitelist = _create_whitelist(would_be_installed, package_set) | |
| 116 | |
| 117 return ( | |
| 118 package_set, | |
| 119 check_package_set( | |
| 120 package_set, should_ignore=lambda name: name not in whitelist | |
| 121 ) | |
| 122 ) | |
| 123 | |
| 124 | |
| 125 def _simulate_installation_of(to_install, package_set): | |
| 126 # type: (List[InstallRequirement], PackageSet) -> Set[str] | |
| 127 """Computes the version of packages after installing to_install. | |
| 128 """ | |
| 129 | |
| 130 # Keep track of packages that were installed | |
| 131 installed = set() | |
| 132 | |
| 133 # Modify it as installing requirement_set would (assuming no errors) | |
| 134 for inst_req in to_install: | |
| 135 abstract_dist = make_distribution_for_install_requirement(inst_req) | |
| 136 dist = abstract_dist.get_pkg_resources_distribution() | |
| 137 | |
| 138 name = canonicalize_name(dist.key) | |
| 139 package_set[name] = PackageDetails(dist.version, dist.requires()) | |
| 140 | |
| 141 installed.add(name) | |
| 142 | |
| 143 return installed | |
| 144 | |
| 145 | |
| 146 def _create_whitelist(would_be_installed, package_set): | |
| 147 # type: (Set[str], PackageSet) -> Set[str] | |
| 148 packages_affected = set(would_be_installed) | |
| 149 | |
| 150 for package_name in package_set: | |
| 151 if package_name in packages_affected: | |
| 152 continue | |
| 153 | |
| 154 for req in package_set[package_name].requires: | |
| 155 if canonicalize_name(req.name) in packages_affected: | |
| 156 packages_affected.add(package_name) | |
| 157 break | |
| 158 | |
| 159 return packages_affected |
