Code Monkey home page Code Monkey logo

Comments (4)

micheles avatar micheles commented on July 4, 2024

Yes, you can and you need to use the FunctionMaker class to do that. But there no examples in the docs for that. The function decorator.decorator is signature-changing because it takes a function with arguments func, *args, **kw and returns a function with a single argument func, so the documentation is correct.

from decorator.

mrmachine avatar mrmachine commented on July 4, 2024

I see now. It IS (can acts as) a signature changing decorator. It cannot CREATE signature changing decorators. Now the docs make sense.

I'd love for that distinction to be made a little clearer in the docs, and to see a new easy to use function equivalent to decorator that makes signature changing decorators.

While looking into this I discovered wrapt which does support and document the creation of signature changing decorators (called "adaptors"), so I'll probably switch to that, although I prefer the decorator syntax.

from decorator.

micheles avatar micheles commented on July 4, 2024

You may try this project https://github.com/smarie/python-makefun which is based on the decorator module.

from decorator.

smarie avatar smarie commented on July 4, 2024

Thanks @micheles for the redirection, yes I think that makefun would be appropriate for this use case. It was entirely inspired by decoratorand relies on the same trick that decorator uses, but it focuses solely on the function wrapping aspect so that it is de-coupled from the decorator creation problem.

Here is how you would create your decorator using makefun:

from inspect import signature, Parameter
from makefun import wraps, add_signature_parameters

def mydec(func):
    """
    A decorator to add 2 keyword arguments to the decorated function.

    :param func:
    :return:
    """
    # add parameters to the wrapped function signature
    func_sig = signature(func)
    new_kw_param = Parameter('new_kw', kind=Parameter.POSITIONAL_OR_KEYWORD, default=None)
    another_new_kw_param = Parameter('another_new_kw', kind=Parameter.POSITIONAL_OR_KEYWORD, default=None)
    new_sig = add_signature_parameters(func_sig, last=(new_kw_param, another_new_kw_param))

    # create the new function with modified signature
    @wraps(func, new_sig=new_sig)
    def new_func(*args, **kwargs):
        new_kw = kwargs.pop('new_kw')
        another_new_kw = kwargs.pop('another_new_kw')
        if new_kw:
            print(new_kw)

        return func(*args, **kwargs)

    # return the new function
    return new_func


# ------ Test
@mydec
def foo(a):
    return a + 1

assert foo(1) == 2
assert foo(1, new_kw='bar') == 2  # prints 'bar'
print(help(foo))                  # new signature

If you then wish to make the decorator creation step more easy, you can use decopatch. This is particularly relevant if you wish your decorator to have arguments, including optional ones. This is how you can do it :

from inspect import signature, Parameter
from decopatch import function_decorator, DECORATED
from makefun import wraps, add_signature_parameters

@function_decorator
def mydec(func=DECORATED, msg_to_print="hello"):
    """
    A decorator to add 2 keyword arguments to the decorated function.

    :param msg_to_print: the message to print
    :return:
    """
    # add parameters to the wrapped function signature
    func_sig = signature(func)
    new_kw_param = Parameter('new_kw', kind=Parameter.POSITIONAL_OR_KEYWORD, default=None)
    another_new_kw_param = Parameter('another_new_kw', kind=Parameter.POSITIONAL_OR_KEYWORD, default=None)
    new_sig = add_signature_parameters(func_sig, last=(new_kw_param, another_new_kw_param))

    # create the new function with modified signature
    @wraps(func, new_sig=new_sig)
    def new_func(*args, **kwargs):
        new_kw = kwargs.pop('new_kw')
        another_new_kw = kwargs.pop('another_new_kw')
        if new_kw:
            print("%s %s" % (msg_to_print, new_kw))

        return func(*args, **kwargs)

    # return the new function
    return new_func

# ------ Test
@mydec          # no parenthesis ! default behaviour
def foo(a):
    return a + 1

assert foo(1) == 2
assert foo(1, new_kw='bar') == 2  # prints 'hello bar'

@mydec(msg_to_print="oops")
def foo2(a):
    return a + 2

assert foo2(1) == 3
assert foo2(1, new_kw='bar') == 3  # prints 'oops bar'

Note: the helper function add_signature_parameters is still a bit ugly but there are open tickets about this. Feel free to comment or propose other ideas: smarie/python-makefun#41 smarie/python-makefun#42 smarie/python-makefun#43

from decorator.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.