Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

__code__ attribute no longer copied/passed through in decorator>=5 #129

Closed
bergtholdt opened this issue Jul 19, 2021 · 3 comments
Closed

Comments

@bergtholdt
Copy link

Consider this simple example:

import decorator

def decor(func, *args, **kwargs):
    return func(*args, **kwargs)

@decorator.decorator(decor)
def test(a, b, c=1, d=2):
    pass

With decorator 4.4.2 I get:

test.__code__.co_argcount
4
>>> test.__code__.co_varnames
('a', 'b', 'c', 'd')

With decorator 5.0.9 I get:

>>> test.__code__.co_argcount
0
>>> test.__code__.co_varnames
('args', 'kw')

This breaks all my method decorators that I use with enthought traits library since they rely on func.code.co_argcount for trait change notification.

Is there a workaround I can do to my wrappers, without changing anything in the enthought traits library (like introducing inspect module there)?

@micheles
Copy link
Owner

micheles commented Jul 20, 2021

Version 5 of the decorator module relies on the Signature object in the standard library which is a leaking abstraction and fails if one looks at low level objects like the __code__ objects. For the moment I would suggest you to stick with version 4. Otherwise you should define your decorators with the FunctionMaker class that still uses exec and would pass the __code__ attribute, but it requires some work.

@micheles
Copy link
Owner

micheles commented Sep 11, 2021

I am adding the following function in version 5.1:

def decoratorx(caller):
    """
    A version of "decorator" implemented via "exec" and not via the
    Signature object. Use this if you are want to preserve the `.__code__`
    object properties (https://github.com/micheles/decorator/issues/129).
    """
    def dec(func):
        return FunctionMaker.create(
            func,
            "return _call_(_func_, %(shortsignature)s)",
            dict(_call_=caller, _func_=func),
            __wrapped__=func, __qualname__=func.__qualname__)
    return dec

Then things will work as you want if you define

@decorator.decoratorx(decor)
def test(a, b, c=1, d=2):
    pass

However, rather than using decoratorx, you should fix your introspection routines to use inspect.Signature without fiddling with the __code__ object.

@bersbersbers
Copy link

For the record, decoratorx is very helpful also with PySide6 signals. See GrahamDumpleton/wrapt#243 and/or https://stackoverflow.com/q/76596620/880783 if interested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants