args attribute of Exception objects
Bengt Richter
bokr at oz.net
Mon Apr 11 18:20:31 EDT 2005
On 11 Apr 2005 03:31:23 -0700, sdementen at hotmail.com (Sebastien de Menten) wrote:
>Jeremy Bowers <jerf at jerf.org> wrote in message news:<pan.2005.04.08.16.07.30.85408 at jerf.org>...
>> 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
More information about the Python-list
mailing list