1
1
from check50 import Failure , Missing , Mismatch
2
- import re
3
2
import inspect
4
3
import tokenize
5
4
import types , builtins
@@ -56,6 +55,9 @@ def check50_assert(src, msg_or_exc=None, cond_type="unknown", left=None, right=N
56
55
:raises check50.Failure: If msg_or_exc is a string, or if cond_type is \
57
56
unrecognized.
58
57
"""
58
+ if context is None :
59
+ context = {}
60
+
59
61
# Grab the global and local variables as of now
60
62
caller_frame = inspect .currentframe ().f_back
61
63
caller_globals = caller_frame .f_globals
@@ -71,7 +73,9 @@ def check50_assert(src, msg_or_exc=None, cond_type="unknown", left=None, right=N
71
73
except Exception as e :
72
74
context [expr_str ] = f"[error evaluating: { e } ]"
73
75
74
- # filter out modules, functions, and built-ins
76
+ # filter out modules, functions, and built-ins, which is needed to avoid
77
+ # overwriting function definitions in evaluaton and avoid useless string
78
+ # output
75
79
def is_irrelevant_value (v ):
76
80
return isinstance (v , (types .ModuleType , types .FunctionType , types .BuiltinFunctionType ))
77
81
@@ -87,7 +91,7 @@ def is_builtin_name(name):
87
91
context_str = ", " .join (f"{ k } = { repr (v )} " for k , v in filtered_context .items ())
88
92
else :
89
93
filtered_context = {}
90
-
94
+
91
95
# Since we've memoized the functions and variables once, now try and
92
96
# evaluate the conditional by substituting the function calls/vars with
93
97
# their results
@@ -147,11 +151,12 @@ def substitute_expressions(src: str, context: dict) -> tuple[str, dict]:
147
151
}
148
152
```
149
153
"""
154
+ # Parse the src into a stream of tokens
150
155
tokens = tokenize .generate_tokens (StringIO (src ).readline )
151
156
152
157
new_tokens = []
153
158
new_context = {}
154
- placeholder_map = {}
159
+ placeholder_map = {} # used for duplicates in src (i.e. x == x => __expr0 == __expr0)
155
160
counter = 0
156
161
157
162
for tok_type , tok_string , start , end , line in tokens :
@@ -161,8 +166,13 @@ def substitute_expressions(src: str, context: dict) -> tuple[str, dict]:
161
166
placeholder_map [tok_string ] = placeholder
162
167
new_context [placeholder ] = context [tok_string ]
163
168
counter += 1
169
+ else :
170
+ # Avoid creating a new __expr{i} variable if it has already been seen
171
+ placeholder = placeholder_map [tok_string ]
164
172
new_tokens .append ((tok_type , placeholder ))
165
173
else :
174
+ # Anything not found in the context dictionary is placed here,
175
+ # including keywords, whitespace, operators, etc.
166
176
new_tokens .append ((tok_type , tok_string ))
167
177
168
178
eval_src = tokenize .untokenize (new_tokens )
0 commit comments