|
3 | 3 |
|
4 | 4 | from __future__ import absolute_import
|
5 | 5 |
|
6 |
| -import operator |
7 |
| -from collections import defaultdict, deque |
8 |
| - |
9 |
| -from pex.dist_metadata import Requirement |
10 |
| -from pex.exceptions import production_assert |
11 |
| -from pex.orderedset import OrderedSet |
12 |
| -from pex.pep_503 import ProjectName |
13 |
| -from pex.resolve.locked_resolve import LockedRequirement, LockedResolve |
| 6 | +from pex.resolve.locked_resolve import LockedResolve |
14 | 7 | from pex.sorted_tuple import SortedTuple
|
15 |
| -from pex.third_party.packaging.markers import Marker, Variable |
16 |
| -from pex.typing import TYPE_CHECKING, cast |
| 8 | +from pex.typing import TYPE_CHECKING |
17 | 9 |
|
18 | 10 | if TYPE_CHECKING:
|
19 |
| - from typing import Callable, DefaultDict, Dict, Iterable, List, Optional, Tuple, Union |
20 |
| - |
21 | 11 | import attr # vendor:skip
|
22 |
| - |
23 |
| - EvalExtra = Callable[[ProjectName], bool] |
24 | 12 | else:
|
25 | 13 | from pex.third_party import attr
|
26 | 14 |
|
27 | 15 |
|
28 |
| -_OPERATORS = { |
29 |
| - "in": lambda lhs, rhs: lhs in rhs, |
30 |
| - "not in": lambda lhs, rhs: lhs not in rhs, |
31 |
| - "<": operator.lt, |
32 |
| - "<=": operator.le, |
33 |
| - "==": operator.eq, |
34 |
| - "!=": operator.ne, |
35 |
| - ">=": operator.ge, |
36 |
| - ">": operator.gt, |
37 |
| -} |
38 |
| - |
39 |
| - |
40 |
| -class _Op(object): |
41 |
| - def __init__(self, lhs): |
42 |
| - self.lhs = lhs # type: EvalExtra |
43 |
| - self.rhs = None # type: Optional[EvalExtra] |
44 |
| - |
45 |
| - |
46 |
| -class _And(_Op): |
47 |
| - def __call__(self, extra): |
48 |
| - # type: (ProjectName) -> bool |
49 |
| - production_assert(self.rhs is not None) |
50 |
| - return self.lhs(extra) and cast("EvalExtra", self.rhs)(extra) |
51 |
| - |
52 |
| - |
53 |
| -class _Or(_Op): |
54 |
| - def __call__(self, extra): |
55 |
| - # type: (ProjectName) -> bool |
56 |
| - production_assert(self.rhs is not None) |
57 |
| - return self.lhs(extra) or cast("EvalExtra", self.rhs)(extra) |
58 |
| - |
59 |
| - |
60 |
| -def _parse_extra_item( |
61 |
| - stack, # type: List[EvalExtra] |
62 |
| - item, # type: Union[str, List, Tuple] |
63 |
| - marker, # type: Marker |
64 |
| -): |
65 |
| - # type: (...) -> None |
66 |
| - |
67 |
| - if item == "and": |
68 |
| - stack.append(_And(stack.pop())) |
69 |
| - elif item == "or": |
70 |
| - stack.append(_Or(stack.pop())) |
71 |
| - elif isinstance(item, list): |
72 |
| - for element in item: |
73 |
| - _parse_extra_item(stack, element, marker) |
74 |
| - elif isinstance(item, tuple): |
75 |
| - lhs, op, rhs = item |
76 |
| - if isinstance(lhs, Variable) and "extra" == str(lhs): |
77 |
| - check = lambda extra: _OPERATORS[str(op)](extra, ProjectName(str(rhs))) |
78 |
| - elif isinstance(rhs, Variable) and "extra" == str(rhs): |
79 |
| - check = lambda extra: _OPERATORS[str(op)](extra, ProjectName(str(lhs))) |
80 |
| - else: |
81 |
| - # Any other condition could potentially be true. |
82 |
| - check = lambda _: True |
83 |
| - if stack: |
84 |
| - production_assert(isinstance(stack[-1], _Op)) |
85 |
| - cast(_Op, stack[-1]).rhs = check |
86 |
| - else: |
87 |
| - stack.append(check) |
88 |
| - else: |
89 |
| - raise ValueError("Marker is invalid: {marker}".format(marker=marker)) |
| 16 | +def remove_unused_requires_dist(locked_resolve): |
| 17 | + # type: (LockedResolve) -> LockedResolve |
90 | 18 |
|
91 |
| - |
92 |
| -def _parse_extra_check(marker): |
93 |
| - # type: (Marker) -> EvalExtra |
94 |
| - checks = [] # type: List[EvalExtra] |
95 |
| - for item in marker._markers: |
96 |
| - _parse_extra_item(checks, item, marker) |
97 |
| - production_assert(len(checks) == 1) |
98 |
| - return checks[0] |
99 |
| - |
100 |
| - |
101 |
| -_EXTRA_CHECKS = {} # type: Dict[str, EvalExtra] |
102 |
| - |
103 |
| - |
104 |
| -def _parse_marker_for_extra_check(marker): |
105 |
| - # type: (Marker) -> EvalExtra |
106 |
| - maker_str = str(marker) |
107 |
| - eval_extra = _EXTRA_CHECKS.get(maker_str) |
108 |
| - if not eval_extra: |
109 |
| - eval_extra = _parse_extra_check(marker) |
110 |
| - _EXTRA_CHECKS[maker_str] = eval_extra |
111 |
| - return eval_extra |
112 |
| - |
113 |
| - |
114 |
| -def _evaluate_for_extras( |
115 |
| - marker, # type: Optional[Marker] |
116 |
| - extras, # type: Iterable[str] |
117 |
| -): |
118 |
| - # type: (...) -> bool |
119 |
| - if not marker: |
120 |
| - return True |
121 |
| - eval_extra = _parse_marker_for_extra_check(marker) |
122 |
| - return any(eval_extra(ProjectName(extra)) for extra in (extras or [""])) |
123 |
| - |
124 |
| - |
125 |
| -def remove_unused_requires_dist( |
126 |
| - resolve_requirements, # type: Iterable[Requirement] |
127 |
| - locked_resolve, # type: LockedResolve |
128 |
| -): |
129 |
| - # type: (...) -> LockedResolve |
130 |
| - |
131 |
| - locked_req_by_project_name = { |
132 |
| - locked_req.pin.project_name: locked_req for locked_req in locked_resolve.locked_requirements |
| 19 | + locked_projects = { |
| 20 | + locked_req.pin.project_name for locked_req in locked_resolve.locked_requirements |
133 | 21 | }
|
134 |
| - requires_dist_by_locked_req = defaultdict( |
135 |
| - OrderedSet |
136 |
| - ) # type: DefaultDict[LockedRequirement, OrderedSet[Requirement]] |
137 |
| - seen = set() |
138 |
| - requirements = deque(resolve_requirements) |
139 |
| - while requirements: |
140 |
| - requirement = requirements.popleft() |
141 |
| - if requirement in seen: |
142 |
| - continue |
143 |
| - |
144 |
| - seen.add(requirement) |
145 |
| - locked_req = locked_req_by_project_name[requirement.project_name] |
146 |
| - for dep in locked_req.requires_dists: |
147 |
| - if _evaluate_for_extras(dep.marker, requirement.extras): |
148 |
| - requires_dist_by_locked_req[locked_req].add(dep) |
149 |
| - requirements.append(dep) |
150 |
| - |
151 | 22 | return attr.evolve(
|
152 | 23 | locked_resolve,
|
153 | 24 | locked_requirements=SortedTuple(
|
154 | 25 | attr.evolve(
|
155 | 26 | locked_requirement,
|
156 | 27 | requires_dists=SortedTuple(
|
157 |
| - requires_dist_by_locked_req[locked_requirement], key=str |
| 28 | + ( |
| 29 | + requires_dist |
| 30 | + for requires_dist in locked_requirement.requires_dists |
| 31 | + # Otherwise, the requirement markers were never selected in the resolve |
| 32 | + # process; so the requirement was not locked. |
| 33 | + if requires_dist.project_name in locked_projects |
| 34 | + ), |
| 35 | + key=str, |
158 | 36 | ),
|
159 | 37 | )
|
160 | 38 | for locked_requirement in locked_resolve.locked_requirements
|
|
0 commit comments