""" This module implements the functionality to take any Python expression as a string and fix all numbers and other things before evaluating it, thus 1/2 returns Integer(1)/Integer(2) We use the ast module for this. It is well documented at docs.python.org. Some tips to understand how this works: use dump() to get a nice representation of any node. Then write a string of what you want to get, e.g. "Integer(1)", parse it, dump it and you'll see that you need to do "Call(Name('Integer', Load()), [node], [], None, None)". You do not need to bother with lineno and col_offset, just call fix_missing_locations() before returning the node. """ from sympy.core.basic import Basic from sympy.core.sympify import SympifyError from ast import parse, NodeTransformer, Call, Name, Load, \ fix_missing_locations, Constant, Tuple class Transform(NodeTransformer): def __init__(self, local_dict, global_dict): NodeTransformer.__init__(self) self.local_dict = local_dict self.global_dict = global_dict def visit_Constant(self, node): if isinstance(node.value, int): return fix_missing_locations(Call(func=Name('Integer', Load()), args=[node], keywords=[])) elif isinstance(node.value, float): return fix_missing_locations(Call(func=Name('Float', Load()), args=[node], keywords=[])) return node def visit_Name(self, node): if node.id in self.local_dict: return node elif node.id in self.global_dict: name_obj = self.global_dict[node.id] if isinstance(name_obj, (Basic, type)) or callable(name_obj): return node elif node.id in ['True', 'False']: return node return fix_missing_locations(Call(func=Name('Symbol', Load()), args=[Constant(node.id)], keywords=[])) def visit_Lambda(self, node): args = [self.visit(arg) for arg in node.args.args] body = self.visit(node.body) n = Call(func=Name('Lambda', Load()), args=[Tuple(args, Load()), body], keywords=[]) return fix_missing_locations(n) def parse_expr(s, local_dict): """ Converts the string "s" to a SymPy expression, in local_dict. It converts all numbers to Integers before feeding it to Python and automatically creates Symbols. """ global_dict = {} exec('from sympy import *', global_dict) try: a = parse(s.strip(), mode="eval") except SyntaxError: raise SympifyError("Cannot parse %s." % repr(s)) a = Transform(local_dict, global_dict).visit(a) e = compile(a, "", "eval") return eval(e, global_dict, local_dict)