On 11 Apr 2005 03:31:23 -0700, [EMAIL PROTECTED] (Sebastien de Menten) wrote:
>Jeremy Bowers <[EMAIL PROTECTED]> wrote in message news:<[EMAIL PROTECTED]>... >> On Fri, 08 Apr 2005 09:32:37 +0000, Sébastien de Menten wrote: >> >> > Hi, >> > >> > When I need to make sense of a python exception, I often need to parse the >> > string exception in order to retrieve the data. >> >> What exactly are you doing with this info? (Every time I started to do >> this, I found a better way. Perhaps one of them will apply for you.) >> >> (As a general comment, I'd point out that you don't have to check the >> entire error message; checking for a descriptive substring, while still >> not "safe", is at least safe*r*.) > >I have symbolic expressions in a dictionnary like: > >dct = dict( a = "b**2 + c", b = "cos(2.3) + sin(v)", v = "4", c = >"some_very_expensive_function(v)") > >I want to build a function that finds the links between all those >expressions (think about computing dependencies between cells in a >spreadsheet). > Here's a way of finding the symbols being used in the expressions, and dependencies: (no guarantees, I haven't explored any special corner cases or caught syntax errors etc ;-) And note that eval is not safe even for expressions, if you don't know what's in the string. A safer approach would be to use compiler.parse and a visitor to extract names from the ast. >>> dct = dict( ... a = "b**2 + c", ... b = "cos(2.3) + sin(v)", ... v = "4", ... c = "some_very_expensive_function(v)") >>> >>> symdep = dict((sym, sorted(eval('lambda:'+expr).func_code.co_names)) for >>> sym, expr in dct.items()) >>> >>> def tree(startsym, depdict): ... seen = set() ... def _tree(sym, level=0): ... print '%s%s'%(' '*level, sym) ... seen.add(sym) ... for dep in depdict.get(sym, []): _tree(dep, level+1) ... for sym in sorted(depdict.keys()): ... if sym in seen: continue ... _tree(sym) ... >>> tree(sorted(symdep.keys())[0], symdep) a b cos sin v c some_very_expensive_function v Skipping fancy visitor stuff ;-), getting names brute force from the ast, should be safer than eval('lambda:'+expr).func_code.co_names): (not tested beyond what you see ;-) >>> def getnames(expr): ... ast = compiler.parse(expr, 'eval') ... names = [] ... def descend(node): ... if isinstance(node, compiler.ast.Name): names.append(node.name) ... if not hasattr(node, 'getChildren'): return ... for child in node.getChildren(): descend(child) ... descend(ast) ... return sorted(names) ... >>> getnames('a*b+c**foo(x)') ['a', 'b', 'c', 'foo', 'x'] >>> for v,x in sorted(dct.items()): ... print '%5s: %s'%(v, getnames(x)) ... a: ['b', 'c'] b: ['cos', 'sin', 'v'] c: ['some_very_expensive_function', 'v'] v: [] >All I do is: >def link(name): > dependencies = {} > while True: > try: > eval(dct[name], globals(), dependencies) > except NameError,e: > dependencies[e.args[0][6:-16]] = 1 > else: > return dependencies > >globals() can be replaced by a custom dictionnary for security >purposes. I would use something getnames above, and put getnames(expr) in the expression for symdep = dict(...) in place of sorted(eval('lambda:'+expr).func_code.co_names)) >variation on the theme can: > - check SyntaxError and give interlligent feedback to user (BTW, >SyntaxError args are much smarter) compiler.parse will raise SyntaxError in getnames, so you could make your own message, e.g., >>> try: getnames('a*b+c***foo(x)') # *** is syntax error ... except SyntaxError, e: ... print 'SyntaxError: %s<<--BROKE HERE-->>%s' % (e.text[:e.offset], e.text[e.offset:]) ... SyntaxError: a*b+c***<<--BROKE HERE-->>foo(x) > - find or/and eval recursively the whole tree and keep in cache >values,... > HTH Regards, Bengt Richter -- http://mail.python.org/mailman/listinfo/python-list