[Python-ideas] make __closure__ writable

Mark Shannon mark at hotpy.org
Tue Mar 20 15:43:06 CET 2012


Yury Selivanov wrote:
> I did provide such example earlier in this thread.  I'm copying and
> pasting it to this mail.  Please read the example carefully, as it
> explains why returning new types.FunctionType() is not enough.
> 
> ----
> 
> Yes, your approach will work if your decorator is the only one applied.
> But, as I said, if you have many of them (see below), you can't just
> return a new function out of your decorator, you need to change the
> underlying "in-place".  Consider the following:
> 
> def modifier(func):
>  orig_func = func
> 
>  while func.__wrapped__:
>    func = func.__wrapped__
> 
>  # patch func.__code__ and func.__closure__
>  return orig_func # no need to wrap anything
> 
> def some_decorator(func):
>  def wrapper(*args, **kwargs):
>      # some code
>      return func(*args, **kwargs)
>  functools.wraps(wrapper, func)
>  return wrapper
> 
> @modifier
> @some_decorator
> def foo():
>  # this code needs to be verified/augmented/etc
> 
> So, in the above snippet, if you don't want to discard the
> @some_decorator by returning a new function object, you need to modify 
> the 'foo' from the @modifier.
> 
> In a complex framework, where you can't guarantee that your magic
> decorator will always be called first, rewriting the __closure__ 
> attribute is the only way.  

So why won't this work?

def f_with_new_closure(f, closure):
     return types.FunctionType(f.__code__,
                               f.__globals__,
                               f.__name__,
                               f.__defaults__,
                               closure)

def modifier(func, closure):
     if func.__wrapped__:
	while func.__wrapped__.__wrapped__:
  	    func = func.__wrapped__
	func.__wrapped__ = f_with_new_closure(func.__wrapped__,
                                               closure)
     else:
         return f_with_new_closure(func, closure)
     if func.__wrapped__:
	return f_with_new_closure(func,
		   f_with_new_closure(func.__wrapped__))
     else:
	return f_with_new_closure(func, closure)

Cheers,
Mark.



More information about the Python-ideas mailing list