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

Make this work with staticmethods / classmethods #1

Open
pganssle opened this issue Dec 8, 2017 · 2 comments
Open

Make this work with staticmethods / classmethods #1

pganssle opened this issue Dec 8, 2017 · 2 comments
Labels

Comments

@pganssle
Copy link
Member

pganssle commented Dec 8, 2017

TL;DR: We should probably support either @primary as the outer decorator and/or add a @primary.classmethod variant.

There's two ways this could compose with the staticmethod / classmethod decorator, and they have different failure modes:

class DivisionBase:
    @classmethod
    @primary
    def divide(cls, x, y):
        return x / y

    @divide.variant('round')
    def divide(cls, x, y):
        return round(x / y)

@classmethod as outer decorator fails because divide becomes a classmethod, not a VariantFunction, and doesn't have its fancy variant decorator anymore:

----> 7     @divide.variant('round')
      8     def divide(cls, x, y):
      9         return round(x / y)

AttributeError: 'classmethod' object has no attribute 'variant'

I think there's no obvious way around that. The other way is with primary as the outer decorator:

class DivisionBase:
    @primary
    @classmethod
    def divide(cls, x, y):
        return x / y

    @divide.variant('round')
    def divide(cls, x, y):
        return round(x / y)

This one will succeed, but the way it's implemented now I think it's being called incorrectly:

In [12]: DivisionBase.divide(3, 4)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-12-38b40b30b265> in <module>()
----> 1 DivisionBase.divide(3, 4)
     37 
     38     def __call__(self, *args, **kwargs):
---> 39         return self.__main_form__(*args, **kwargs)
     40 
     41     def _add_variant(self, var_name, vfunc):

TypeError: 'classmethod' object is not callable

I think that this one we can get working, it's just a matter of playing with it in the descriptor or something. Alternatively, we can specifically support a classmethod and staticmethod variant for the @primary and @x.variant variants. The disadvantage to both of these is that it's counter-intuitive for the order of decorators to matter like this, so no matter what happens we'll probably get questions.

@pganssle pganssle added the bug label Dec 8, 2017
@pganssle
Copy link
Member Author

Considering that singledispatch doesn't even work with instance methods, we can probably do this one after the first release.

@justanr
Copy link

justanr commented May 13, 2018

@pganssle I've gotten single dispatch to written with instance methods through descriptor trickery.

https://github.com/justanr/objtoolz/blob/master/objtoolz/descriptors/singledispatch.py

It's possible to accomplish the original intent of this issue but you might run into some issues with staticmethods on Py2.

https://github.com/justanr/objtoolz/blob/master/objtoolz/descriptors/undescriptor.py

That's the source for the undescript helper in the singledispatch descriptor. It doesn't work with py2 staticmethods as when they are resolved through the descriptor protocol, they return a unique instance of a function (in py3 it returns the same instance, iirc).

If these are useful to you, feel free to use them. objtoolz is MIT licensed but I never published it to pypi. I've mostly used it as storage for object trickery.

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

No branches or pull requests

2 participants