[Python-ideas] make __closure__ writable
Yury Selivanov
yselivanov.ml at gmail.com
Tue Mar 20 15:56:10 CET 2012
Because usually you write decorators as functions, not classes. And
when you do the former style, you usually do it in the following way:
def decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
functools.wraps(wrapper, func)
return wrapper
Now, let's use it:
@decorator
def some_func(): pass
OK. At this point, 'some_func' object has a '__wrapped__' attribute,
that points to original 'some_func' function. But whatever you write
to 'some_func.__wrapped__' won't change anything, as the 'wrapper'
will continue to call old 'some_func'. Instead of assigning something
to __wrapped__, we need to change it in-place, by doing
'__wrapped__.__closure__ = new_closure'.
On 2012-03-20, at 10:43 AM, Mark Shannon wrote:
> 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.
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> http://mail.python.org/mailman/listinfo/python-ideas
More information about the Python-ideas
mailing list