I am trying to use the following code for a template system, which comes "Beginning Python: From Novice to Professional", 3Ed by Magnus Lie Hetland:
# template.py
import fileinput, re
field_pat = re.compile(r'\[(. ?)\]')
scope = {}
def replacement(match):
code = match.group(1)
try:
return str(eval(code, scope))
except SyntaxError:
return ''
lines = []
for line in fileinput.input():
lines.append(line)
text = ''.join(lines)
print(field_pat.sub(replacement, text))
The book describes this text file, named number.txt
:
[x = 2]
[y = 3]
The sum of [x] and [y] is [x y].
When I tried to replace x & y with numbers using the template,
> python template.py number.txt
I was expecting to see The sum of 2 and 3 is 5.
Instead, however, I get this error message:
Traceback (most recent call last):
File "D:\Python\template.py", line 18, in <module>
print(field_pat.sub(replacement, text))
File "D:\Python\template.py", line 9, in replacement
return str(eval(code, scope))
File "<string>", line 1, in <module>
NameError: name 'x' is not defined
Why does this occur? I am using Python 3.10.0, if it matters.
CodePudding user response:
eval()
only works on expressions, but assignment is a statement in Python. exec()
works on statements.
So what you could do is try parsing an expression, then fall back to a statement.
def replacement(match):
code = match.group(1)
try:
return str(eval(code, scope)) # Try evaluating expression
except SyntaxError:
exec(code, scope) # Execute statement
return ''
>>> text = '[x = 2][y = 3]The sum of [x] and [y] is [x y].'
>>> field_pat.sub(replacement, text)
'The sum of 2 and 3 is 5.'
To be clear, any assignments will be made to scope
, which is acting as the global scope.
By the way, if your input is untrusted, eval()
is dangerous, and by extension, so is exec()
. I hope you're only using this code for learning purposes.