Lora
Lora

Reputation: 75

How to decorate (overloaded) singledispatchmethod

I want to be able to stack a decorator on singledispatchmethod. If that is not possible, at least on the individual registered functions.

There is probably a simple and elegant solution. I have not found it... yet.

Extending the sample from the official documentation:

from functools import singledispatchmethod

def eavesdrop(method):  # my beautiful decoration
    def wrapper(self, arg):
        print(f"Negating {arg}")
        return method(self, arg)
    return wrapper

def workaround(method, arg):  # no beauty, no decoration
    print(f"Negating {arg}")
    return method(arg)

class Negator:
    # @eavesdrop  # AttributeError: 'function' object has no attribute 'register'
    @singledispatchmethod
    # @eavesdrop  # does nothing
    def neg(self, arg):
        raise NotImplementedError("Cannot negate a")

    # @eavesdrop   # does nothing
    @neg.register
    # @eavesdrop   # TypeError: Invalid first argument to `register()`: <function double... 
    def _(self, arg: int):
        return -arg

    @neg.register
    def _(self, arg: bool):
        return not arg

    @neg.register
    def _(self, arg: float):
        return workaround(lambda v: -v, arg)  # use only in case of despair

I hope to find the solution here. Thanks!

Upvotes: 1

Views: 284

Answers (1)

jsbueno
jsbueno

Reputation: 110561

It is just a matter of preserving the properties of the decorated function when wrapping it with your decorator. In this case, you have to carry over the annotations of the original method, so that the machinery of singledispatchmethod can see it.

The simpler way to do it is functools.wraps:


from functools import singledispatchmethod, wraps

def eavesdrop(method):  # my beautiful decoration
    @wraps(method)
    def wrapper(self, arg):
        print(f"Negating {arg}")
        return method(self, arg)
    return wrapper

class Negator:
    @singledispatchmethod
    @eavesdrop
    def neg(self, arg):
        raise NotImplementedError("Cannot negate a")

    @neg.register
    @eavesdrop  
    def _(self, arg: int):
        return -arg

    @neg.register
    @eavesdrop
    def _(self, arg: bool):
        return not arg

    @neg.register
    @eavesdrop
    def _(self, arg: float):
        return workaround(lambda v: -v, arg)  # use o

Upvotes: 1

Related Questions