Skip to content

Commit db7c6ff

Browse files
authored
fixed bugs in which functions and module names would appear in context; in which builtins were overwritten by context; added tokenization of vars
1 parent 16573bc commit db7c6ff

File tree

2 files changed

+41
-20
lines changed

2 files changed

+41
-20
lines changed

check50/assertions/rewrite.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,7 @@ def visit_Call(self, node):
190190
self.visit(kw)
191191

192192
def visit_Name(self, node):
193-
if not self._in_func_chain: # ignore Names of modules/libraries
194-
self.names.add(node.id)
195-
# self.names.add(node.id)
193+
self.names.add(node.id)
196194

197195
def _get_full_func_name(self, node):
198196
"""

check50/assertions/runtime.py

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
from check50 import Failure, Missing, Mismatch
22
import re
33
import inspect
4+
import tokenize
5+
import types, builtins
6+
from io import StringIO
7+
48

59
def check50_assert(src, msg_or_exc=None, cond_type="unknown", left=None, right=None, context=None):
610
"""
@@ -67,13 +71,27 @@ def check50_assert(src, msg_or_exc=None, cond_type="unknown", left=None, right=N
6771
except Exception as e:
6872
context[expr_str] = f"[error evaluating: {e}]"
6973

70-
# produces a string like "var1 = ..., var2 = ..., foo() = ..."
71-
context_str = ", ".join(f"{k} = {repr(v)}" for k, v in (context or {}).items())
74+
# filter out modules, functions, and built-ins
75+
def is_irrelevant_value(v):
76+
return isinstance(v, (types.ModuleType, types.FunctionType, types.BuiltinFunctionType))
77+
78+
def is_builtin_name(name):
79+
return name in dir(builtins)
7280

81+
filtered_context = {
82+
k: v for k, v in context.items()
83+
if not is_irrelevant_value(v) and not is_builtin_name(k.split("(")[0])
84+
}
85+
86+
# produces a string like "var1 = ..., var2 = ..., foo() = ..."
87+
context_str = ", ".join(f"{k} = {repr(v)}" for k, v in filtered_context.items())
88+
else:
89+
filtered_context = {}
90+
7391
# Since we've memoized the functions and variables once, now try and
7492
# evaluate the conditional by substituting the function calls/vars with
7593
# their results
76-
eval_src, eval_context = substitute_expressions(src, context)
94+
eval_src, eval_context = substitute_expressions(src, filtered_context)
7795

7896
# Merge globals with expression context for evaluation
7997
eval_globals = caller_globals.copy()
@@ -129,18 +147,23 @@ def substitute_expressions(src: str, context: dict) -> tuple[str, dict]:
129147
}
130148
```
131149
"""
132-
new_src = src
133-
new_context = {}
134-
135-
for i, expr in enumerate(sorted(context.keys(), key=len, reverse=True)):
136-
placeholder = f"__expr{i}"
137-
138-
# Use regex to replace only full matches of expr
139-
# Escape expr if it has special characters (like function calls)
140-
pattern = re.escape(expr)
141-
new_src, count = re.subn(rf'\b{pattern}\b', placeholder, new_src)
150+
tokens = tokenize.generate_tokens(StringIO(src).readline)
142151

143-
if count > 0:
144-
new_context[placeholder] = context[expr]
145-
146-
return new_src, new_context
152+
new_tokens = []
153+
new_context = {}
154+
placeholder_map = {}
155+
counter = 0
156+
157+
for tok_type, tok_string, start, end, line in tokens:
158+
if tok_string in context:
159+
if tok_string not in placeholder_map:
160+
placeholder = f"__expr{counter}"
161+
placeholder_map[tok_string] = placeholder
162+
new_context[placeholder] = context[tok_string]
163+
counter += 1
164+
new_tokens.append((tok_type, placeholder))
165+
else:
166+
new_tokens.append((tok_type, tok_string))
167+
168+
eval_src = tokenize.untokenize(new_tokens)
169+
return eval_src, new_context

0 commit comments

Comments
 (0)