mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Fix #8872: autodoc: stacked singledispatches are wrongly rendered
When multiple singledispatch decorators are stacked, the first typehints are copied to the subsequent definitions unexpectedly. Now autodoc generates a dummy function not to affect typehints to subsequent functions.
This commit is contained in:
parent
05abdad749
commit
caa6579dbd
2
CHANGES
2
CHANGES
@ -25,6 +25,8 @@ Features added
|
||||
Bugs fixed
|
||||
----------
|
||||
|
||||
* #8872: autodoc: stacked singledispatches are wrongly rendered
|
||||
|
||||
Testing
|
||||
--------
|
||||
|
||||
|
@ -1328,10 +1328,10 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
||||
if typ is object:
|
||||
pass # default implementation. skipped.
|
||||
else:
|
||||
self.annotate_to_first_argument(func, typ)
|
||||
|
||||
dispatchfunc = self.annotate_to_first_argument(func, typ)
|
||||
if dispatchfunc:
|
||||
documenter = FunctionDocumenter(self.directive, '')
|
||||
documenter.object = func
|
||||
documenter.object = dispatchfunc
|
||||
documenter.objpath = [None]
|
||||
sigs.append(documenter.format_signature())
|
||||
if overloaded:
|
||||
@ -1358,28 +1358,34 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
||||
|
||||
return overload.replace(parameters=parameters)
|
||||
|
||||
def annotate_to_first_argument(self, func: Callable, typ: Type) -> None:
|
||||
def annotate_to_first_argument(self, func: Callable, typ: Type) -> Optional[Callable]:
|
||||
"""Annotate type hint to the first argument of function if needed."""
|
||||
try:
|
||||
sig = inspect.signature(func, type_aliases=self.config.autodoc_type_aliases)
|
||||
except TypeError as exc:
|
||||
logger.warning(__("Failed to get a function signature for %s: %s"),
|
||||
self.fullname, exc)
|
||||
return
|
||||
return None
|
||||
except ValueError:
|
||||
return
|
||||
return None
|
||||
|
||||
if len(sig.parameters) == 0:
|
||||
return
|
||||
return None
|
||||
|
||||
def dummy():
|
||||
pass
|
||||
|
||||
params = list(sig.parameters.values())
|
||||
if params[0].annotation is Parameter.empty:
|
||||
params[0] = params[0].replace(annotation=typ)
|
||||
try:
|
||||
func.__signature__ = sig.replace(parameters=params) # type: ignore
|
||||
dummy.__signature__ = sig.replace(parameters=params) # type: ignore
|
||||
return dummy
|
||||
except (AttributeError, TypeError):
|
||||
# failed to update signature (ex. built-in or extension types)
|
||||
return
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class DecoratorDocumenter(FunctionDocumenter):
|
||||
@ -2118,11 +2124,11 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
||||
if typ is object:
|
||||
pass # default implementation. skipped.
|
||||
else:
|
||||
self.annotate_to_first_argument(func, typ)
|
||||
|
||||
dispatchmeth = self.annotate_to_first_argument(func, typ)
|
||||
if dispatchmeth:
|
||||
documenter = MethodDocumenter(self.directive, '')
|
||||
documenter.parent = self.parent
|
||||
documenter.object = func
|
||||
documenter.object = dispatchmeth
|
||||
documenter.objpath = [None]
|
||||
sigs.append(documenter.format_signature())
|
||||
if overloaded:
|
||||
@ -2158,27 +2164,34 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
||||
|
||||
return overload.replace(parameters=parameters)
|
||||
|
||||
def annotate_to_first_argument(self, func: Callable, typ: Type) -> None:
|
||||
def annotate_to_first_argument(self, func: Callable, typ: Type) -> Optional[Callable]:
|
||||
"""Annotate type hint to the first argument of function if needed."""
|
||||
try:
|
||||
sig = inspect.signature(func, type_aliases=self.config.autodoc_type_aliases)
|
||||
except TypeError as exc:
|
||||
logger.warning(__("Failed to get a method signature for %s: %s"),
|
||||
self.fullname, exc)
|
||||
return
|
||||
return None
|
||||
except ValueError:
|
||||
return
|
||||
return None
|
||||
|
||||
if len(sig.parameters) == 1:
|
||||
return
|
||||
return None
|
||||
|
||||
def dummy():
|
||||
pass
|
||||
|
||||
params = list(sig.parameters.values())
|
||||
if params[1].annotation is Parameter.empty:
|
||||
params[1] = params[1].replace(annotation=typ)
|
||||
try:
|
||||
func.__signature__ = sig.replace(parameters=params) # type: ignore
|
||||
dummy.__signature__ = sig.replace(parameters=params) # type: ignore
|
||||
return dummy
|
||||
except (AttributeError, TypeError):
|
||||
# failed to update signature (ex. built-in or extension types)
|
||||
return
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class NonDataDescriptorMixin(DataDocumenterMixinBase):
|
||||
|
@ -15,6 +15,7 @@ def func(arg, kwarg=None):
|
||||
|
||||
|
||||
@func.register(int)
|
||||
@func.register(float)
|
||||
def _func_int(arg, kwarg=None):
|
||||
"""A function for int."""
|
||||
pass
|
||||
|
@ -10,6 +10,7 @@ class Foo:
|
||||
pass
|
||||
|
||||
@meth.register(int)
|
||||
@meth.register(float)
|
||||
def _meth_int(self, arg, kwarg=None):
|
||||
"""A method for int."""
|
||||
pass
|
||||
|
@ -2080,6 +2080,7 @@ def test_singledispatch(app):
|
||||
'',
|
||||
'',
|
||||
'.. py:function:: func(arg, kwarg=None)',
|
||||
' func(arg: float, kwarg=None)',
|
||||
' func(arg: int, kwarg=None)',
|
||||
' func(arg: str, kwarg=None)',
|
||||
' :module: target.singledispatch',
|
||||
@ -2107,6 +2108,7 @@ def test_singledispatchmethod(app):
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: Foo.meth(arg, kwarg=None)',
|
||||
' Foo.meth(arg: float, kwarg=None)',
|
||||
' Foo.meth(arg: int, kwarg=None)',
|
||||
' Foo.meth(arg: str, kwarg=None)',
|
||||
' :module: target.singledispatchmethod',
|
||||
@ -2125,6 +2127,7 @@ def test_singledispatchmethod_automethod(app):
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:method:: Foo.meth(arg, kwarg=None)',
|
||||
' Foo.meth(arg: float, kwarg=None)',
|
||||
' Foo.meth(arg: int, kwarg=None)',
|
||||
' Foo.meth(arg: str, kwarg=None)',
|
||||
' :module: target.singledispatchmethod',
|
||||
|
@ -119,6 +119,7 @@ def test_singledispatch(app):
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:function:: func(arg, kwarg=None)',
|
||||
' func(arg: float, kwarg=None)',
|
||||
' func(arg: int, kwarg=None)',
|
||||
' func(arg: str, kwarg=None)',
|
||||
' :module: target.singledispatch',
|
||||
|
Loading…
Reference in New Issue
Block a user