Hooking exceptions outside of call stack
Josiah Carlson
josiah.carlson at sbcglobal.net
Sun Jun 10 10:59:01 EDT 2007
Warren Stringer wrote:
> Josiah Carlson wrote:
>> >>> foo = type(foo)(foo.func_code, d, foo.func_name, foo.func_defaults,
>> foo.func_closure)
>
> Wow! I've never seen that, before. Is there documentation for `type(n)(...)`
> somewhere? I did find a very useful "Decorator for Binding Constants, by
> Raymond Hettinger", that uses this technique.
type(obj) gets you the type of the object. To discover what it takes to
construct a function object, I just used...
>>> help(type(foo))
Help on class function in module __builtin__:
class function(object)
| function(code, globals[, name[, argdefs[, closure]]])
|
| Create a function object from a code object and a dictionary.
| The optional name string overrides the name from the code object.
| The optional argdefs tuple specifies the default argument values.
| The optional closure tuple supplies the bindings for free variables.
...
> The calls are made from embedded classes that are constructed on the fly:
>
> class a(object): ...
> class b(object): ...
> class c(object): ...
>
> for `a.b[c]=1`, a.__getattr__('b') is called but 'c' need to be resolved as
> an object before a.b.__getitem__(c) is called. Could a.b.__init__ set
> globals so that 'c' gets resolved? Is this global namespace the same as the
> `globals()` namespace?
I don't believe there is any confusion as to *why* you are getting the
error. It seems plain to everyone involved. You are getting the error
because c is not defined in the namespace in which you are executing
'a.b[c] = 1'. Unless you modify the namespace in which it is running...
>>> class a(object):
... class b(object):
... class c(object):
... pass
...
>>> def merged_namespace(*ns):
... try:
... __builtins__
... namespaces = ns + (globals(),__builtins__.__dict__)
... except NameError:
... namespaces = ns + (globals(),__builtin__)
... ns = {}
... for i in reversed(namespaces):
... ns.update(i)
... return ns
...
>>> class m_level_namespace(dict):
... def __init__(self, *ns):
... try:
... __builtins__
... self.namespaces = ns + (globals(),__builtins__.__dict__)
... except NameError:
... self.namespaces = ns + (globals(),__builtin__)
... def __getitem__(self, key):
... for i in self.namespaces:
... print "..."
... if key in i:
... return i[key]
... raise NameError("Name %r not found"%(key,))
... def __setitem__(self, key, value):
... self.ns[0][key] = value
... def __delitem__(self, key):
... del self.ns[0][key]
...
>>> def foo():
... print type(globals())
... a.b[c] = 1
...
>>> ns = m_level_namespace(a.b.__dict__)
>>>
>>> foo1 = type(foo)(foo.func_code, ns, foo.func_name,
foo.func_defaults, foo.func_closure)
>>>
>>> foo1()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in foo
NameError: global name 'type' is not defined
>>>
>>> ns = merged_namespace(a.b.__dict__)
>>>
>>> foo2 = type(foo)(foo.func_code, ns, foo.func_name,
foo.func_defaults, foo.func_closure)
>>>
>>> foo2()
<type 'dict'>
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in foo
TypeError: 'type' object does not support item assignment
>>>
Due to the vagaries of Python's lookup mechanism, the m_level_namespace
class doesn't work as a method of trying to build a name resolution
order. However, if you are OK with the limitations of needing to merge
namespaces, then the merged_namespace function should work for you (it
doesn't pick up changed globals, builtins, your class contents, etc.)
With all of that said, you still shouldn't be trying to do a.b[c] . You
should instead be doing a.b[a.b.c] . Is it cumbersome? Yes. but it's
the only mechanism that really makes sense (hacking namespaces is a
great way of breaking your application in new and unexpected ways).
- Josiah
More information about the Python-list
mailing list