The system I work with allows users to specify a Python Boolean expression as a string (in a configuration file). The system takes the string, converts it to a Python AST object and then evaluates the expression based on input data.
There are occasional circumstances when the Boolean expression may result in an error condition (e.g. input data does not have the right number of entries). The requirement is that if a user wraps the Boolean expression in fail_on_error(...)
, the exception will be caught and converted to False
(e.g.):
assert test('fail_on_error(True)') == True
assert test('fail_on_error(False)') == False
assert test('fail_on_error(list()[0])') == False # index out of range
This could be done using regular expressions, parsing the expression string, but I'd prefer to do this the "right way" with Python's ast
module. Unfortunately I'm running into problems.
The Code
class ReplaceWithTry(ast.NodeTransformer):
def visit_Call(self, node):
if node.func.id != 'fail_on_error':
return node
# Get (first) argument to 'fail_on_error' and return its evaluated expression
return_expr = ast.Return(value=node.args[0])
except_handler = ast.ExceptHandler(type=None,
name=None,
body=ast.Return(value=ast.NameConstant(value=False))])
return ast.Try(body=[return_expr],
handlers=[except_handler],
orelse=[],
finalbody=[])
def test(expr_str):
syntax_tree = ast.parse(expr_str, '<string>', 'eval')
new_tree = ast.fix_missing_locations(ReplaceWithTry().visit(syntax_tree))
ast_obj = compile(new_tree, '<string>', 'eval')
return eval(ast_obj)
The Error
I'm getting the following error:
knoepfel$ python replace_with_try.py
Traceback (most recent call last):
File "replace_with_try.py", line 24, in <module>
assert test('fail_on_error(True)')
File "replace_with_try.py", line 21, in test
ast_obj = compile(new_tree, '<string>', 'eval')
TypeError: expected some sort of expr, but got <ast.Try object at 0x103da33a0>
Obviously I'm missing something. Any ideas?