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

Functionmaker.create (from doc example) fails with kwarg-only functions #139

Closed
bmcfee opened this issue Jan 5, 2022 · 6 comments
Closed

Comments

@bmcfee
Copy link

bmcfee commented Jan 5, 2022

Possibly related to #138 , but I'm creating a new issue here in case the root cause is fundamentally different.

I'm using the third-party decorator example like so:

In [1]: def foo(a, *, b, c=0):
   ...:     return a + b * c
   ...: 

In [2]: from decorator import FunctionMaker

In [3]: def decorator_apply(dec, func):
   ...:      """
   ...:      Decorate a function by preserving the signature even if dec
   ...:      is not a signature-preserving decorator.
   ...:      """
   ...:      return FunctionMaker.create(
   ...:          func, 'return decfunc(%(signature)s)',
   ...:          dict(decfunc=dec(func)), __wrapped__=func)
   ...: 

In [4]: def dec(f):
   ...:     print(f)
   ...:     return f

In [5]: bar = decorator_apply(dec, foo)
<function foo at 0x7f520b0f5f70>
Error in generated code:
def foo(a, *, b=None, c=None):
    return decfunc(a, *, b=None, c=None)

╭──────────────────────────────────────────────────────────────────────────────╮
│  <decorator-gen-0>:2                                                         │
│     return decfunc(a, *, b=None, c=None)                                     │
│                        ▲                                                     │
╰──────────────────────────────────────────────────────────────────────────────╯
SyntaxError: iterable argument unpacking follows keyword argument unpacking

I'm on decorator==5.1.0, python 3.9.9.

Is there a recommended workaround here? Or is there something more fundamental that will prevent this from working?

@smarie
Copy link
Contributor

smarie commented Jan 6, 2022

Could makefun help you here until decorator includes this support ?

from inspect import signature
from makefun import wraps


def foo(a, *, b, c=0):
    return a + b * c


def dec(f):
    def non_preserving_func(*args, **kwargs):
        return f(*args, **kwargs)

    return non_preserving_func


def decorator_apply(deco, func):
    # use `makefun.wraps`
    return wraps(func)(deco(func))


# The decorator was not preserving the signature
print(signature(dec(foo)))

# We make it preserve the signature
bar = decorator_apply(dec, foo)
print(signature(bar))

leads:

(*args, **kwargs)
(a, *, b, c=0)

Hope this helps until this is fixed here.

@bmcfee
Copy link
Author

bmcfee commented Jan 6, 2022

@smarie that does the trick, thanks!

@micheles
Copy link
Owner

micheles commented Jan 7, 2022

That example was written 10+ years ago and at the time signatures like foo(a, *, b, c=0) did not exist. There is an easy solution:

def decorator_apply(dec, func):
    return FunctionMaker.create( func, 'return decfunc(%(shortsignature)s)',
    dict(decfunc=dec(func)), __wrapped__=func)

Try this and let me know how it goes. Happy New Year!

@smarie
Copy link
Contributor

smarie commented Jan 7, 2022

Even better if there is a direct solution @micheles ;)
Happy new years to both of you!

@bmcfee
Copy link
Author

bmcfee commented Jan 7, 2022

Awesome thanks!

I totally get that the example predates the named parameter construction. Might be a good idea to either update the example in the docs or add it to the notes later in the readme.

Happy new year!

@micheles
Copy link
Owner

micheles commented Jan 7, 2022

Already fixed the docs and made a release 5.1.1 ;-)

@micheles micheles closed this as completed Jan 7, 2022
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