This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author Viktor Roytman
Recipients Viktor Roytman, levkivskyi, lukasz.langa, ncoghlan, xtreak
Date 2020-02-21.21:21:43
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1582320103.6.0.588155685636.issue39679@roundup.psfhosted.org>
In-reply-to
Content
I tried to apply this change but it didn't work, failing with this error

    $ ~/.pyenv/versions/3.8.1/bin/python -m bad_classmethod_as_documented
    Traceback (most recent call last):
      File "/home/viktor/.pyenv/versions/3.8.1/lib/python3.8/runpy.py", line 193, in _run_module_as_main
        return _run_code(code, main_globals, None,
      File "/home/viktor/.pyenv/versions/3.8.1/lib/python3.8/runpy.py", line 86, in _run_code
        exec(code, run_globals)
      File "/home/viktor/scratch/bad_classmethod_as_documented.py", line 4, in <module>
        class Negator:
      File "/home/viktor/scratch/bad_classmethod_as_documented.py", line 12, in Negator
        def _(cls, arg: int):
      File "/home/viktor/.pyenv/versions/3.8.1/lib/python3.8/functools.py", line 1006, in register
        return self.dispatcher.register(cls, func=method)
      File "/home/viktor/.pyenv/versions/3.8.1/lib/python3.8/functools.py", line 959, in register
        argname, cls = next(iter(get_type_hints(func).items()))
      File "/home/viktor/.pyenv/versions/3.8.1/lib/python3.8/typing.py", line 1252, in get_type_hints
        raise TypeError('{!r} is not a module, class, method, '
    TypeError: <classmethod object at 0x7f84e1e69c40> is not a module, class, method, or function.

After digging around a bit, this diff seems to work (not sure if there's a better way to do it) (also this one doesn't seem to care whether @staticmethod is applied to the implementation methods):

$ diff -u functools-orig.py functools.py
--- functools-orig.py	2020-02-21 16:14:56.141934001 -0500
+++ functools.py	2020-02-21 16:17:19.959905849 -0500
@@ -843,14 +843,18 @@
         if func is None:
             if isinstance(cls, type):
                 return lambda f: register(cls, f)
-            ann = getattr(cls, '__annotations__', {})
+            if isinstance(cls, (classmethod, staticmethod)):
+                ann = getattr(cls.__func__, '__annotations__', {})
+                func = cls.__func__
+            else:
+                ann = getattr(cls, '__annotations__', {})
+                func = cls
             if not ann:
                 raise TypeError(
                     f"Invalid first argument to `register()`: {cls!r}. "
                     f"Use either `@register(some_class)` or plain `@register` "
                     f"on an annotated function."
                 )
-            func = cls
 
             # only import typing if annotation parsing is necessary
             from typing import get_type_hints
@@ -908,6 +912,8 @@
     def __get__(self, obj, cls=None):
         def _method(*args, **kwargs):
             method = self.dispatcher.dispatch(args[0].__class__)
+            if isinstance(self.func, classmethod):
+                return method.__get__(obj, cls)(cls, *args, **kwargs)
             return method.__get__(obj, cls)(*args, **kwargs)
 
         _method.__isabstractmethod__ = self.__isabstractmethod__
History
Date User Action Args
2020-02-21 21:21:43Viktor Roytmansetrecipients: + Viktor Roytman, ncoghlan, lukasz.langa, levkivskyi, xtreak
2020-02-21 21:21:43Viktor Roytmansetmessageid: <1582320103.6.0.588155685636.issue39679@roundup.psfhosted.org>
2020-02-21 21:21:43Viktor Roytmanlinkissue39679 messages
2020-02-21 21:21:43Viktor Roytmancreate