Skip to content

Instantly share code, notes, and snippets.

@EricWF
Last active August 29, 2015 14:25
Show Gist options
  • Select an option

  • Save EricWF/361704883909e29a998a to your computer and use it in GitHub Desktop.

Select an option

Save EricWF/361704883909e29a998a to your computer and use it in GitHub Desktop.
available_
import ast
import decimal
class FeatureTransformer(ast.NodeTransformer):
ALLOWED_NAMES = set(['True', 'False', 'None'])
ALLOWED_NODE_TYPES = set([
'Num', # allow numbers too
'Name', # an identifier...
'Load', # loads a value of a variable with given identifier
'Call', # allow defined(...)
'UnaryOp',
'Not',
'BoolOp',
'And',
'Or',
'Compare',
'Eq',
'NotEq',
'Lt',
'Gt',
'LtE',
'GtE'
])
def __init__(self, allow_names):
self.m_allowed_names = dict(allow_names)
def visit_Name(self, node):
def replace_id(ids):
return self.generic_visit(
ast.copy_location(
ast.parse(repr(ids[node.id]), mode='eval'),
node))
if node.id in self.ALLOWED_NAMES:
if node.id == 'None':
node.id = 'False'
return node
elif node.id in self.m_allowed_names:
val = self.m_allowed_names[node.id]
new_node = ast.parse(repr(val), mode='eval').body
return self.generic_visit(
ast.copy_location(new_node, node))
else:
return ast.copy_location(ast.Num(n=0), node)
def visit_Call(self, node):
if node.func.id != 'defined':
raise RuntimeError("Invalid call expression: function %s not allowed" \
% node.func.id)
if len(node.args) != 1 or len(node.keywords) != 0 \
or node.starargs or node.kwargs:
raise RuntimeError("Invalid call expression: defined takes exactly one arg.")
arg = node.args[0]
argtype = type(arg).__name__
if type(arg).__name__ != 'Name':
raise RuntimeError("Invalid call to defined!")
isDef = bool(arg.id in self.m_allowed_names)
return ast.copy_location(ast.Num(n=isDef), node)
def generic_visit(self, node):
nodetype = type(node).__name__
if nodetype not in self.ALLOWED_NODE_TYPES:
raise RuntimeError("Invalid expression: %s not allowed" % nodetype)
return ast.NodeTransformer.generic_visit(self, node)
def evaluate(self, strExpr, debug=False):
tree = ast.parse(strExpr, mode='eval')
if debug:
print('%r' % ast.dump(tree))
tree.body = self.visit(tree.body)
clause = compile(tree, '<AST>', 'eval')
result = eval(clause, dict())
return bool(result)
my_source = "True, False, None, not True, not False, True and True, True and False"
my_source += ", True or False, False or False"
my_source += ", a and b, (b and c) or not a, c == b, c <= d"
my_source += ", not a, c != b, c < b, c > b, not a, defined(a), defined(n)"
my_source += ", not defined(a), not defined(n)"
my_transformer = FeatureTransformer({'a': True, 'b': 0, 'c': 1, 'd': -1 })
test_list = my_source.split(',')
print('%r' % my_transformer.m_allowed_names)
for t in test_list:
t = t.strip()
result = my_transformer.evaluate(t, debug=True)
print("Had = %r, Got = '%r'" % (t, result))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment