Skip to content

Commit e608777

Browse files
committed
More robust evaluation for self-referential names
Tracks the `target_name` that we are recursively evaluating, and short-circuits evaluation if we attempt to evaluate that name again. Also allows the assignment evaluation to try multiple assignments until either a real value is found, or all assignments are exhausted. Also prevents combining lhs and rhs of binary addition if either side is the `"??"` sentinel value. Fixes #56
1 parent f2ad1be commit e608777

File tree

1 file changed

+29
-11
lines changed

1 file changed

+29
-11
lines changed

dowsing/setuptools/setup_py_parsing.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,9 @@ def visit_Call(self, node: cst.Call) -> Optional[bool]:
178178
BOOL_NAMES = {"True": True, "False": False, "None": None}
179179
PRETEND_ARGV = ["setup.py", "bdist_wheel"]
180180

181-
def evaluate_in_scope(self, item: cst.CSTNode, scope: Any) -> Any:
181+
def evaluate_in_scope(
182+
self, item: cst.CSTNode, scope: Any, target_name: str = ""
183+
) -> Any:
182184
qnames = self.get_metadata(QualifiedNameProvider, item)
183185

184186
if isinstance(item, cst.SimpleString):
@@ -224,13 +226,23 @@ def evaluate_in_scope(self, item: cst.CSTNode, scope: Any) -> Any:
224226
# module scope isn't in the dict
225227
return "??"
226228

227-
return self.evaluate_in_scope(gp.value, scope)
229+
# we have recursed, likey due to `x = x + y` assignment or similar
230+
# self-referential evaluation
231+
if target_name and target_name == name:
232+
return "??"
233+
234+
# keep trying assignments until we get something other than ??
235+
result = self.evaluate_in_scope(gp.value, scope, name)
236+
if result and result != "??":
237+
return result
238+
# give up
239+
return "??"
228240
elif isinstance(item, (cst.Tuple, cst.List)):
229241
lst = []
230242
for el in item.elements:
231243
lst.append(
232244
self.evaluate_in_scope(
233-
el.value, self.get_metadata(ScopeProvider, el)
245+
el.value, self.get_metadata(ScopeProvider, el), target_name
234246
)
235247
)
236248
if isinstance(item, cst.Tuple):
@@ -248,10 +260,10 @@ def evaluate_in_scope(self, item: cst.CSTNode, scope: Any) -> Any:
248260
for arg in item.args:
249261
if isinstance(arg.keyword, cst.Name):
250262
args[names.index(arg.keyword.value)] = self.evaluate_in_scope(
251-
arg.value, scope
263+
arg.value, scope, target_name
252264
)
253265
else:
254-
args[i] = self.evaluate_in_scope(arg.value, scope)
266+
args[i] = self.evaluate_in_scope(arg.value, scope, target_name)
255267
i += 1
256268

257269
# TODO clear ones that are still default
@@ -264,26 +276,30 @@ def evaluate_in_scope(self, item: cst.CSTNode, scope: Any) -> Any:
264276
d = {}
265277
for arg in item.args:
266278
if isinstance(arg.keyword, cst.Name):
267-
d[arg.keyword.value] = self.evaluate_in_scope(arg.value, scope)
279+
d[arg.keyword.value] = self.evaluate_in_scope(
280+
arg.value, scope, target_name
281+
)
268282
# TODO something with **kwargs
269283
return d
270284
elif isinstance(item, cst.Dict):
271285
d = {}
272286
for el2 in item.elements:
273287
if isinstance(el2, cst.DictElement):
274288
d[self.evaluate_in_scope(el2.key, scope)] = self.evaluate_in_scope(
275-
el2.value, scope
289+
el2.value, scope, target_name
276290
)
277291
return d
278292
elif isinstance(item, cst.Subscript):
279-
lhs = self.evaluate_in_scope(item.value, scope)
293+
lhs = self.evaluate_in_scope(item.value, scope, target_name)
280294
if isinstance(lhs, str):
281295
# A "??" entry, propagate
282296
return "??"
283297

284298
# TODO: Figure out why this is Sequence
285299
if isinstance(item.slice[0].slice, cst.Index):
286-
rhs = self.evaluate_in_scope(item.slice[0].slice.value, scope)
300+
rhs = self.evaluate_in_scope(
301+
item.slice[0].slice.value, scope, target_name
302+
)
287303
try:
288304
if isinstance(lhs, dict):
289305
return lhs.get(rhs, "??")
@@ -296,8 +312,10 @@ def evaluate_in_scope(self, item: cst.CSTNode, scope: Any) -> Any:
296312
# LOG.warning(f"Omit2 {type(item.slice[0].slice)!r}")
297313
return "??"
298314
elif isinstance(item, cst.BinaryOperation):
299-
lhs = self.evaluate_in_scope(item.left, scope)
300-
rhs = self.evaluate_in_scope(item.right, scope)
315+
lhs = self.evaluate_in_scope(item.left, scope, target_name)
316+
rhs = self.evaluate_in_scope(item.right, scope, target_name)
317+
if lhs == "??" or rhs == "??":
318+
return "??"
301319
if isinstance(item.operator, cst.Add):
302320
try:
303321
return lhs + rhs

0 commit comments

Comments
 (0)