Mercurial > repos > guerler > hhblits
comparison lib/python3.8/site-packages/pip/_internal/req/req_set.py @ 0:9e54283cc701 draft
"planemo upload commit d12c32a45bcd441307e632fca6d9af7d60289d44"
author | guerler |
---|---|
date | Mon, 27 Jul 2020 03:47:31 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:9e54283cc701 |
---|---|
1 # The following comment should be removed at some point in the future. | |
2 # mypy: strict-optional=False | |
3 | |
4 from __future__ import absolute_import | |
5 | |
6 import logging | |
7 from collections import OrderedDict | |
8 | |
9 from pip._vendor.packaging.utils import canonicalize_name | |
10 | |
11 from pip._internal import pep425tags | |
12 from pip._internal.exceptions import InstallationError | |
13 from pip._internal.models.wheel import Wheel | |
14 from pip._internal.utils.logging import indent_log | |
15 from pip._internal.utils.typing import MYPY_CHECK_RUNNING | |
16 | |
17 if MYPY_CHECK_RUNNING: | |
18 from typing import Dict, Iterable, List, Optional, Tuple | |
19 from pip._internal.req.req_install import InstallRequirement | |
20 | |
21 | |
22 logger = logging.getLogger(__name__) | |
23 | |
24 | |
25 class RequirementSet(object): | |
26 | |
27 def __init__(self, check_supported_wheels=True): | |
28 # type: (bool) -> None | |
29 """Create a RequirementSet. | |
30 """ | |
31 | |
32 self.requirements = OrderedDict() # type: Dict[str, InstallRequirement] # noqa: E501 | |
33 self.check_supported_wheels = check_supported_wheels | |
34 | |
35 self.unnamed_requirements = [] # type: List[InstallRequirement] | |
36 self.successfully_downloaded = [] # type: List[InstallRequirement] | |
37 self.reqs_to_cleanup = [] # type: List[InstallRequirement] | |
38 | |
39 def __str__(self): | |
40 # type: () -> str | |
41 requirements = sorted( | |
42 (req for req in self.requirements.values() if not req.comes_from), | |
43 key=lambda req: canonicalize_name(req.name), | |
44 ) | |
45 return ' '.join(str(req.req) for req in requirements) | |
46 | |
47 def __repr__(self): | |
48 # type: () -> str | |
49 requirements = sorted( | |
50 self.requirements.values(), | |
51 key=lambda req: canonicalize_name(req.name), | |
52 ) | |
53 | |
54 format_string = '<{classname} object; {count} requirement(s): {reqs}>' | |
55 return format_string.format( | |
56 classname=self.__class__.__name__, | |
57 count=len(requirements), | |
58 reqs=', '.join(str(req.req) for req in requirements), | |
59 ) | |
60 | |
61 def add_unnamed_requirement(self, install_req): | |
62 # type: (InstallRequirement) -> None | |
63 assert not install_req.name | |
64 self.unnamed_requirements.append(install_req) | |
65 | |
66 def add_named_requirement(self, install_req): | |
67 # type: (InstallRequirement) -> None | |
68 assert install_req.name | |
69 | |
70 project_name = canonicalize_name(install_req.name) | |
71 self.requirements[project_name] = install_req | |
72 | |
73 def add_requirement( | |
74 self, | |
75 install_req, # type: InstallRequirement | |
76 parent_req_name=None, # type: Optional[str] | |
77 extras_requested=None # type: Optional[Iterable[str]] | |
78 ): | |
79 # type: (...) -> Tuple[List[InstallRequirement], Optional[InstallRequirement]] # noqa: E501 | |
80 """Add install_req as a requirement to install. | |
81 | |
82 :param parent_req_name: The name of the requirement that needed this | |
83 added. The name is used because when multiple unnamed requirements | |
84 resolve to the same name, we could otherwise end up with dependency | |
85 links that point outside the Requirements set. parent_req must | |
86 already be added. Note that None implies that this is a user | |
87 supplied requirement, vs an inferred one. | |
88 :param extras_requested: an iterable of extras used to evaluate the | |
89 environment markers. | |
90 :return: Additional requirements to scan. That is either [] if | |
91 the requirement is not applicable, or [install_req] if the | |
92 requirement is applicable and has just been added. | |
93 """ | |
94 # If the markers do not match, ignore this requirement. | |
95 if not install_req.match_markers(extras_requested): | |
96 logger.info( | |
97 "Ignoring %s: markers '%s' don't match your environment", | |
98 install_req.name, install_req.markers, | |
99 ) | |
100 return [], None | |
101 | |
102 # If the wheel is not supported, raise an error. | |
103 # Should check this after filtering out based on environment markers to | |
104 # allow specifying different wheels based on the environment/OS, in a | |
105 # single requirements file. | |
106 if install_req.link and install_req.link.is_wheel: | |
107 wheel = Wheel(install_req.link.filename) | |
108 tags = pep425tags.get_supported() | |
109 if (self.check_supported_wheels and not wheel.supported(tags)): | |
110 raise InstallationError( | |
111 "%s is not a supported wheel on this platform." % | |
112 wheel.filename | |
113 ) | |
114 | |
115 # This next bit is really a sanity check. | |
116 assert install_req.is_direct == (parent_req_name is None), ( | |
117 "a direct req shouldn't have a parent and also, " | |
118 "a non direct req should have a parent" | |
119 ) | |
120 | |
121 # Unnamed requirements are scanned again and the requirement won't be | |
122 # added as a dependency until after scanning. | |
123 if not install_req.name: | |
124 self.add_unnamed_requirement(install_req) | |
125 return [install_req], None | |
126 | |
127 try: | |
128 existing_req = self.get_requirement(install_req.name) | |
129 except KeyError: | |
130 existing_req = None | |
131 | |
132 has_conflicting_requirement = ( | |
133 parent_req_name is None and | |
134 existing_req and | |
135 not existing_req.constraint and | |
136 existing_req.extras == install_req.extras and | |
137 existing_req.req.specifier != install_req.req.specifier | |
138 ) | |
139 if has_conflicting_requirement: | |
140 raise InstallationError( | |
141 "Double requirement given: %s (already in %s, name=%r)" | |
142 % (install_req, existing_req, install_req.name) | |
143 ) | |
144 | |
145 # When no existing requirement exists, add the requirement as a | |
146 # dependency and it will be scanned again after. | |
147 if not existing_req: | |
148 self.add_named_requirement(install_req) | |
149 # We'd want to rescan this requirement later | |
150 return [install_req], install_req | |
151 | |
152 # Assume there's no need to scan, and that we've already | |
153 # encountered this for scanning. | |
154 if install_req.constraint or not existing_req.constraint: | |
155 return [], existing_req | |
156 | |
157 does_not_satisfy_constraint = ( | |
158 install_req.link and | |
159 not ( | |
160 existing_req.link and | |
161 install_req.link.path == existing_req.link.path | |
162 ) | |
163 ) | |
164 if does_not_satisfy_constraint: | |
165 self.reqs_to_cleanup.append(install_req) | |
166 raise InstallationError( | |
167 "Could not satisfy constraints for '%s': " | |
168 "installation from path or url cannot be " | |
169 "constrained to a version" % install_req.name, | |
170 ) | |
171 # If we're now installing a constraint, mark the existing | |
172 # object for real installation. | |
173 existing_req.constraint = False | |
174 existing_req.extras = tuple(sorted( | |
175 set(existing_req.extras) | set(install_req.extras) | |
176 )) | |
177 logger.debug( | |
178 "Setting %s extras to: %s", | |
179 existing_req, existing_req.extras, | |
180 ) | |
181 # Return the existing requirement for addition to the parent and | |
182 # scanning again. | |
183 return [existing_req], existing_req | |
184 | |
185 def has_requirement(self, name): | |
186 # type: (str) -> bool | |
187 project_name = canonicalize_name(name) | |
188 | |
189 return ( | |
190 project_name in self.requirements and | |
191 not self.requirements[project_name].constraint | |
192 ) | |
193 | |
194 def get_requirement(self, name): | |
195 # type: (str) -> InstallRequirement | |
196 project_name = canonicalize_name(name) | |
197 | |
198 if project_name in self.requirements: | |
199 return self.requirements[project_name] | |
200 | |
201 raise KeyError("No project with the name %r" % name) | |
202 | |
203 def cleanup_files(self): | |
204 # type: () -> None | |
205 """Clean up files, remove builds.""" | |
206 logger.debug('Cleaning up...') | |
207 with indent_log(): | |
208 for req in self.reqs_to_cleanup: | |
209 req.remove_temporary_source() |