Skip to content

Commit

Permalink
Add support for singledispatchmethod class methods in MethodDocumenter
Browse files Browse the repository at this point in the history
  • Loading branch information
picnixz committed Apr 5, 2023
1 parent 609b2f2 commit 4ac2d68
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 1 deletion.
5 changes: 4 additions & 1 deletion sphinx/ext/autodoc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2168,7 +2168,8 @@ def add_directive_header(self, sig: str) -> None:
self.add_line(' :abstractmethod:', sourcename)
if inspect.iscoroutinefunction(obj) or inspect.isasyncgenfunction(obj):
self.add_line(' :async:', sourcename)
if inspect.isclassmethod(obj):
if (inspect.isclassmethod(obj) or
inspect.is_singledispatch_method(obj) and inspect.isclassmethod(obj.func)):
self.add_line(' :classmethod:', sourcename)
if inspect.isstaticmethod(obj, cls=self.parent, name=self.object_name):
self.add_line(' :staticmethod:', sourcename)
Expand Down Expand Up @@ -2200,6 +2201,8 @@ def format_signature(self, **kwargs: Any) -> str:
if typ is object:
pass # default implementation. skipped.
else:
if inspect.isclassmethod(func):
func = func.__func__
dispatchmeth = self.annotate_to_first_argument(func, typ)
if dispatchmeth:
documenter = MethodDocumenter(self.directive, '')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import sys
from functools import singledispatchmethod


class Foo:
"""docstring"""

@singledispatchmethod
@classmethod
def class_meth(cls, arg, kwarg=None):
"""A class method for general use."""
pass

@class_meth.register(int)
@class_meth.register(float)
@classmethod
def _class_meth_int(cls, arg, kwarg=None):
"""A class method for int."""
pass

@class_meth.register(str)
@classmethod
def _class_meth_str(cls, arg, kwarg=None):
"""A class method for str."""
pass

if sys.version_info[:2] >= (3, 9):
# registration of a singledispatch'ed classmethod via
# type annotation is not supported prior to Python 3.9
#
# See: https://github.com/python/cpython/issues/83860

@class_meth.register
@classmethod
def _class_meth_dict(cls, arg: dict, kwarg=None):
"""A class method for dict."""
# This function tests for specifying type through annotations
pass
96 changes: 96 additions & 0 deletions tests/test_ext_autodoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2084,6 +2084,102 @@ def test_singledispatchmethod_automethod(app):
]


@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_singledispatchmethod_classmethod(app):
options = {"members": None}
actual = do_autodoc(app, 'module', 'target.singledispatchmethod_classmethod', options)

if sys.version_info[:2] >= (3, 9):
assert list(actual) == [
'',
'.. py:module:: target.singledispatchmethod_classmethod',
'',
'',
'.. py:class:: Foo()',
' :module: target.singledispatchmethod_classmethod',
'',
' docstring',
'',
'',
' .. py:method:: Foo.class_meth(arg, kwarg=None)',
' Foo.class_meth(arg: float, kwarg=None)',
' Foo.class_meth(arg: int, kwarg=None)',
' Foo.class_meth(arg: str, kwarg=None)',
' Foo.class_meth(arg: dict, kwarg=None)',
' :module: target.singledispatchmethod_classmethod',
' :classmethod:',
'',
' A class method for general use.',
'',
]
else:
# Registration of a dispatcher via annotation is not supported
# prior to Python 3.9 and the docstring of a singledispatch'ed
# classmethod is (as of Sphinx 6.1.3) not extracted properly.
import textwrap

assert list(actual) == [
'',
'.. py:module:: target.singledispatchmethod_classmethod',
'',
'',
'.. py:class:: Foo()',
' :module: target.singledispatchmethod_classmethod',
'',
' docstring',
'',
'',
' .. py:method:: Foo.class_meth',
' Foo.class_meth(arg: float, kwarg=None)',
' Foo.class_meth(arg: int, kwarg=None)',
' Foo.class_meth(arg: str, kwarg=None)',
' :module: target.singledispatchmethod_classmethod',
' :classmethod:',
'',
*textwrap.indent(classmethod.__doc__, ' ' * 6).splitlines(),
'',
]


@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_singledispatchmethod_classmethod_automethod(app):
options = {}
actual = do_autodoc(app, 'method', 'target.singledispatchmethod_classmethod.Foo.class_meth', options)

if sys.version_info[:2] >= (3, 9):
assert list(actual) == [
'',
'.. py:method:: Foo.class_meth(arg, kwarg=None)',
' Foo.class_meth(arg: float, kwarg=None)',
' Foo.class_meth(arg: int, kwarg=None)',
' Foo.class_meth(arg: str, kwarg=None)',
' Foo.class_meth(arg: dict, kwarg=None)',
' :module: target.singledispatchmethod_classmethod',
' :classmethod:',
'',
' A class method for general use.',
'',
]
else:
# Registration of a dispatcher via annotation is not supported
# prior to Python 3.9 and the docstring of a singledispatch'ed
# classmethod is (as of Sphinx 6.1.3) not extracted properly.
import textwrap

assert list(actual) == [
'',
'.. py:method:: Foo.class_meth',
' Foo.class_meth(arg: float, kwarg=None)',
' Foo.class_meth(arg: int, kwarg=None)',
' Foo.class_meth(arg: str, kwarg=None)',
' :module: target.singledispatchmethod_classmethod',
' :classmethod:',
'',
*textwrap.indent(classmethod.__doc__, ' ' * 3).splitlines(),
'',
]


@pytest.mark.skipif(sys.version_info[:2] >= (3, 11),
reason=('cython does not support python-3.11 yet. '
'see https://github.com/cython/cython/issues/4365'))
Expand Down

0 comments on commit 4ac2d68

Please sign in to comment.