Is it possible to do something like
c = MyObj()
c.eval("func1(42)+func2(24)")
in Python..i.e. have func1() and func2() be evaluated within the scope of the object 'c' (if they were member functions within that class definition)? I can't do a simple parsing, since for my application the eval strings can become arbitrarily complicated. I guess doing some magic with the ast module might do the trick, but due to the dirth of literature on ast, I'm not sure where to look:
import ast
class MyTransformer(ast.NodeTransformer):
def visit_Name(self, node):
# do a generic_visit so that child nodes are processed
ast.NodeVisitor.generic_visit(self, node)
return ast.copy_location(
# do something magical with names that are functions, so that they become
# method calls to a Formula object
newnode,
node
)
class Formula(object):
def LEFT(self, s, n):
return s[:n]
def RIGHT(self, s, n):
return s[0-n:]
def CONCAT(self, *args, **kwargs):
return ''.join([arg for arg in args])
def main():
evalString = "CONCAT(LEFT('Hello', 2), RIGHT('World', 3))"
# we want to emulate something like Formula().eval(evalString)
node = ast.parse(evalString, mode='eval')
MyTransformer().visit(node)
ast.fix_missing_locations(node)
print eval(compile(node, '<string>', mode='eval'))
eval() is considered insecure because it allows you (or your users) to dynamically execute arbitrary Python code. This is considered bad programming practice because the code that you're reading (or writing) is not the code that you'll execute.
literal_eval may be a safer alternative. literal_eval() would only evaluate literals, not algebraic expressions.
So, I advice you to do something like this:
>>> class S(object):
... def add(self, a, b):
... return a + b
...
>>> filter(lambda (k,v): not k.startswith("__"), S.__dict__.items())
[('add', <function add at 0x109cec500>)]
>>> target = S()
>>> map(lambda (k, f): (k, f.__get__(target, S)), filter(lambda (k,v): not k.startswith("__"), S.__dict__.items()))
[('add', <bound method S.add of <__main__.S object at 0x109ce4ed0>>)]
>>> dict(_)
{'add': <bound method S.add of <__main__.S object at 0x109ce4ed0>>}
>>> eval("add(45, 10) + add(10, 1)", _, {})
66
Seems like that you need. Let me explain how this works.
eval
accepts locals and globals as parameters. globals
dictionary of all "valuable" bounded methods.S
class definition. How to get all "valuable" methods? Simple filter
names from S.__dict__
in order to check whether method name starts from __
or not (you see, that as result we get list with 1 item - add
function).target
= instance of S
class which will be "eval context".__dict__
stores functions, each function is non-data descriptor and bounded method can be fetched simply with func.__get__(obj, type(obj))
. This operation is performed in map
.dict
from it.globals
to eval
function.I hope, this will help.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With