4
4
import os
5
5
import sys
6
6
import tempfile
7
+ from functools import wraps
7
8
from subprocess import run # nosec
8
9
from typing import Deque , Iterable , Mapping , ValuesView
9
10
40
41
]
41
42
42
43
44
+ def patch_match_markers () -> None :
45
+ """
46
+ Monkey patches ``pip._internal.req.InstallRequirement.match_markers`` to
47
+ allow us to pass environment other than "extra".
48
+ """
49
+
50
+ @wraps (InstallRequirement .match_markers )
51
+ def match_markers (
52
+ self : InstallRequirement ,
53
+ extras_requested : Iterable [str ] | None = None ,
54
+ environment : dict [str , str ] = {},
55
+ ) -> bool :
56
+ assert "extra" not in environment
57
+
58
+ if not extras_requested :
59
+ # Provide an extra to safely evaluate the markers
60
+ # without matching any extra
61
+ extras_requested = ("" ,)
62
+ if self .markers is not None :
63
+ return any (
64
+ self .markers .evaluate ({"extra" : extra , ** environment })
65
+ for extra in extras_requested
66
+ )
67
+ else :
68
+ return True
69
+
70
+ InstallRequirement .match_markers = match_markers
71
+
72
+
73
+ patch_match_markers ()
74
+
75
+
43
76
def dependency_tree (
44
77
installed_keys : Mapping [str , Distribution ], root_key : str
45
78
) -> set [str ]:
@@ -93,15 +126,17 @@ def get_dists_to_ignore(installed: Iterable[Distribution]) -> list[str]:
93
126
94
127
95
128
def merge (
96
- requirements : Iterable [InstallRequirement ], ignore_conflicts : bool
129
+ requirements : Iterable [InstallRequirement ],
130
+ ignore_conflicts : bool ,
131
+ environment : dict [str , str ] = {},
97
132
) -> ValuesView [InstallRequirement ]:
98
133
by_key : dict [str , InstallRequirement ] = {}
99
134
100
135
for ireq in requirements :
101
136
# Limitation: URL requirements are merged by precise string match, so
102
137
# "file:///example.zip#egg=example", "file:///example.zip", and
103
138
# "example==1.0" will not merge with each other
104
- if ireq .match_markers ():
139
+ if ireq .match_markers (environment = environment ):
105
140
key = key_from_ireq (ireq )
106
141
107
142
if not ignore_conflicts :
@@ -158,6 +193,7 @@ def diff_key_from_req(req: Distribution) -> str:
158
193
def diff (
159
194
compiled_requirements : Iterable [InstallRequirement ],
160
195
installed_dists : Iterable [Distribution ],
196
+ environment : dict [str , str ] = {},
161
197
) -> tuple [set [InstallRequirement ], set [str ]]:
162
198
"""
163
199
Calculate which packages should be installed or uninstalled, given a set
@@ -172,13 +208,15 @@ def diff(
172
208
pkgs_to_ignore = get_dists_to_ignore (installed_dists )
173
209
for dist in installed_dists :
174
210
key = diff_key_from_req (dist )
175
- if key not in requirements_lut or not requirements_lut [key ].match_markers ():
211
+ if key not in requirements_lut or not requirements_lut [key ].match_markers (
212
+ environment = environment
213
+ ):
176
214
to_uninstall .add (key )
177
215
elif requirements_lut [key ].specifier .contains (dist .version ):
178
216
satisfied .add (key )
179
217
180
218
for key , requirement in requirements_lut .items ():
181
- if key not in satisfied and requirement .match_markers ():
219
+ if key not in satisfied and requirement .match_markers (environment = environment ):
182
220
to_install .add (requirement )
183
221
184
222
# Make sure to not uninstall any packages that should be ignored
0 commit comments