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