Skip to content
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Contributors
* Andi Albrecht -- agogo theme
* Antonio Valentino -- qthelp builder, docstring inheritance
* Antti Kaihola -- doctest extension (skipif option)
* Barak Katzir -- autodoc improvements
* Barry Warsaw -- setup command improvements
* Bart Kamphorst -- warning improvements
* Ben Egan -- Napoleon improvements & viewcode improvements
Expand Down
6 changes: 6 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ Deprecated
Features added
--------------

* #10351, #10359: autodoc: overloaded function or method can now be customized in the
'autodoc-before-process-signature' and 'autodoc-process-signature' events.
Patch by Barak Katzir.
* #13173: Add a new ``duplicate_declaration`` warning type,
with ``duplicate_declaration.c`` and ``duplicate_declaration.cpp`` subtypes.
Patch by Julien Lecomte and Adam Turner.
Expand Down Expand Up @@ -115,6 +118,9 @@ Features added
Bugs fixed
----------

* #9813: autodoc: partial bugfix, :confval:`autodoc_type_aliases` is now supported
by overload signatures of functions and methods.
Patch by Barak Katzir.
* #12463: autosummary: Respect an empty module ``__all__``.
Patch by Valentin Pratz
* #13060: HTML Search: use ``Map`` to store per-file term scores.
Expand Down
52 changes: 26 additions & 26 deletions sphinx/ext/autodoc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import re
import sys
from inspect import Parameter, Signature
from typing import TYPE_CHECKING, Any, NewType, TypeVar
from typing import TYPE_CHECKING, Any, NewType, TypeVar, get_overloads

from docutils.statemachine import StringList

Expand Down Expand Up @@ -1473,6 +1473,7 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ

objtype = 'function'
member_order = 30
overload_impl_sig: Signature | None = None

@classmethod
def can_document_member(
Expand All @@ -1498,6 +1499,8 @@ def format_args(self, **kwargs: Any) -> str:
sig = inspect.signature(
self.object, type_aliases=self.config.autodoc_type_aliases
)
if self.overload_impl_sig is not None:
sig = self.merge_default_value(self.overload_impl_sig, sig)
args = stringify_signature(sig, **kwargs)
except TypeError as exc:
logger.warning(
Expand Down Expand Up @@ -1559,15 +1562,14 @@ def format_signature(self, **kwargs: Any) -> str:
actual = inspect.signature(
self.object, type_aliases=self.config.autodoc_type_aliases
)
__globals__ = safe_getattr(self.object, '__globals__', {})
for overload in self.analyzer.overloads['.'.join(self.objpath)]:
overload = self.merge_default_value(actual, overload)
overload = evaluate_signature(
overload, __globals__, self.config.autodoc_type_aliases
)

sig = stringify_signature(overload, **kwargs)
sigs.append(sig)
overload_kwargs = kwargs | {'show_annotation': True}
for overload_func in get_overloads(self.object):
documenter = type(self)(self.directive, '')
documenter.object = overload_func
documenter.objpath = ['']
# pass actual implementation signature to merge default values later
documenter.overload_impl_sig = actual
sigs.append(documenter.format_signature(**overload_kwargs))

return '\n'.join(sigs)

Expand All @@ -1576,7 +1578,7 @@ def merge_default_value(self, actual: Signature, overload: Signature) -> Signatu
parameters = list(overload.parameters.values())
for i, param in enumerate(parameters):
actual_param = actual.parameters.get(param.name)
if actual_param and param.default == '...':
if actual_param and param.default in {'...', ...}:
parameters[i] = param.replace(default=actual_param.default)

return overload.replace(parameters=parameters)
Expand Down Expand Up @@ -2390,6 +2392,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
directivetype = 'method'
member_order = 50
priority = 1 # must be more than FunctionDocumenter
overload_impl_sig: Signature | None = None

@classmethod
def can_document_member(
Expand Down Expand Up @@ -2449,6 +2452,8 @@ def format_args(self, **kwargs: Any) -> str:
bound_method=True,
type_aliases=self.config.autodoc_type_aliases,
)
if self.overload_impl_sig is not None:
sig = self.merge_default_value(self.overload_impl_sig, sig)
args = stringify_signature(sig, **kwargs)
except TypeError as exc:
logger.warning(
Expand Down Expand Up @@ -2537,20 +2542,15 @@ def format_signature(self, **kwargs: Any) -> str:
type_aliases=self.config.autodoc_type_aliases,
)

__globals__ = safe_getattr(self.object, '__globals__', {})
for overload in self.analyzer.overloads['.'.join(self.objpath)]:
overload = self.merge_default_value(actual, overload)
overload = evaluate_signature(
overload, __globals__, self.config.autodoc_type_aliases
)

if not inspect.isstaticmethod(
self.object, cls=self.parent, name=self.object_name
):
parameters = list(overload.parameters.values())
overload = overload.replace(parameters=parameters[1:])
sig = stringify_signature(overload, **kwargs)
sigs.append(sig)
overload_kwargs = kwargs | {'show_annotation': True}
for overload_func in get_overloads(self.object):
documenter = type(self)(self.directive, '')
documenter.object = overload_func
documenter.objpath = ['']
documenter.parent = self.parent
# pass actual implementation signature to merge default values later
documenter.overload_impl_sig = actual
sigs.append(documenter.format_signature(**overload_kwargs))

return '\n'.join(sigs)

Expand All @@ -2559,7 +2559,7 @@ def merge_default_value(self, actual: Signature, overload: Signature) -> Signatu
parameters = list(overload.parameters.values())
for i, param in enumerate(parameters):
actual_param = actual.parameters.get(param.name)
if actual_param and param.default == '...':
if actual_param and param.default in {'...', Ellipsis}:
parameters[i] = param.replace(default=actual_param.default)

return overload.replace(parameters=parameters)
Expand Down
50 changes: 45 additions & 5 deletions tests/roots/test-ext-autodoc/target/autodoc_type_aliases.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
from typing import TYPE_CHECKING, overload

if TYPE_CHECKING:
from typing import Optional
import fractions as frac
from typing import Optional, Union

myint = int
myfrac = float

#: docstring
variable: myint
Expand All @@ -22,17 +24,17 @@ def read(r: io.BytesIO) -> io.StringIO:
"""docstring"""


def sum(x: myint, y: myint) -> myint:
def sum(x: myfrac, y: myfrac) -> myfrac:
"""docstring"""
return x + y


@overload
def mult(x: myint, y: myint) -> myint: ...
def mult(x: int, y: int) -> int: ...


@overload
def mult(x: float, y: float) -> float: ...
def mult(x: myfrac, y: myfrac) -> myfrac: ...


def mult(x, y):
Expand All @@ -44,7 +46,45 @@ class Foo:
"""docstring"""

#: docstring
attr1: myint
attr1: Union[frac.Fraction, myint] # NoQA: UP007

def __init__(self):
self.attr2: myint = None #: docstring

def method1(self, x: Union[frac.Fraction, myfrac]) -> Union[frac.Fraction, myfrac]: # NoQA: UP007
"""docstring"""
return self.attr1 * x

@overload
def method2(self, x: frac.Fraction) -> frac.Fraction: ...

@overload
def method2(self, x: myfrac) -> myfrac: ...

@overload
def method2(
self,
x: Union[frac.Fraction, myfrac], # NoQA: UP007
) -> Union[frac.Fraction, myfrac]: ... # NoQA: UP007

def method2(self, x):
"""docstring"""
return self.attr2 * x


@overload
def prod(x: tuple[float, myfrac]) -> float: ...


@overload
def prod(x: tuple[frac.Fraction, myfrac]) -> frac.Fraction: ...


def prod(x):
"""docstring"""
return x[0] * x[1]


def print_value(x: Union[frac.Fraction, myfrac]) -> None: # NoQA: UP007
"""docstring"""
print('value:', x)
75 changes: 65 additions & 10 deletions tests/test_extensions/test_ext_autodoc_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1361,7 +1361,7 @@ def test_autodoc_type_aliases(app: SphinxTestApp) -> None:
'',
' .. py:attribute:: Foo.attr1',
' :module: target.autodoc_type_aliases',
' :type: int',
' :type: ~fractions.Fraction | int',
'',
' docstring',
'',
Expand All @@ -1373,20 +1373,47 @@ def test_autodoc_type_aliases(app: SphinxTestApp) -> None:
' docstring',
'',
'',
' .. py:method:: Foo.method1(x: ~fractions.Fraction | float) -> ~fractions.Fraction | float',
' :module: target.autodoc_type_aliases',
'',
' docstring',
'',
'',
' .. py:method:: Foo.method2(x: ~fractions.Fraction) -> ~fractions.Fraction',
' Foo.method2(x: float) -> float',
' Foo.method2(x: ~fractions.Fraction | float) -> ~fractions.Fraction | float',
' :module: target.autodoc_type_aliases',
'',
' docstring',
'',
'',
'.. py:function:: mult(x: int, y: int) -> int',
' mult(x: float, y: float) -> float',
' :module: target.autodoc_type_aliases',
'',
' docstring',
'',
'',
'.. py:function:: print_value(x: ~fractions.Fraction | float) -> None',
' :module: target.autodoc_type_aliases',
'',
' docstring',
'',
'',
'.. py:function:: prod(x: tuple[float, float]) -> float',
' prod(x: tuple[~fractions.Fraction, float]) -> ~fractions.Fraction',
' :module: target.autodoc_type_aliases',
'',
' docstring',
'',
'',
'.. py:function:: read(r: ~io.BytesIO) -> ~io.StringIO',
' :module: target.autodoc_type_aliases',
'',
' docstring',
'',
'',
'.. py:function:: sum(x: int, y: int) -> int',
'.. py:function:: sum(x: float, y: float) -> float',
' :module: target.autodoc_type_aliases',
'',
' docstring',
Expand Down Expand Up @@ -1418,6 +1445,7 @@ def test_autodoc_type_aliases(app: SphinxTestApp) -> None:
# define aliases
app.config.autodoc_type_aliases = {
'myint': 'myint',
'myfrac': 'my.module.myfrac',
'io.StringIO': 'my.module.StringIO',
}
actual = do_autodoc(app, 'module', 'target.autodoc_type_aliases', options)
Expand All @@ -1434,7 +1462,7 @@ def test_autodoc_type_aliases(app: SphinxTestApp) -> None:
'',
' .. py:attribute:: Foo.attr1',
' :module: target.autodoc_type_aliases',
' :type: myint',
" :type: ~fractions.Fraction | TypeAliasForwardRef('myint')",
'',
' docstring',
'',
Expand All @@ -1446,8 +1474,35 @@ def test_autodoc_type_aliases(app: SphinxTestApp) -> None:
' docstring',
'',
'',
'.. py:function:: mult(x: myint, y: myint) -> myint',
' mult(x: float, y: float) -> float',
" .. py:method:: Foo.method1(x: ~fractions.Fraction | TypeAliasForwardRef('my.module.myfrac')) -> ~fractions.Fraction | TypeAliasForwardRef('my.module.myfrac')",
' :module: target.autodoc_type_aliases',
'',
' docstring',
'',
'',
' .. py:method:: Foo.method2(x: ~fractions.Fraction) -> ~fractions.Fraction',
' Foo.method2(x: my.module.myfrac) -> my.module.myfrac',
" Foo.method2(x: ~fractions.Fraction | TypeAliasForwardRef('my.module.myfrac')) -> ~fractions.Fraction | TypeAliasForwardRef('my.module.myfrac')",
' :module: target.autodoc_type_aliases',
'',
' docstring',
'',
'',
'.. py:function:: mult(x: int, y: int) -> int',
' mult(x: my.module.myfrac, y: my.module.myfrac) -> my.module.myfrac',
' :module: target.autodoc_type_aliases',
'',
' docstring',
'',
'',
".. py:function:: print_value(x: ~fractions.Fraction | TypeAliasForwardRef('my.module.myfrac')) -> None",
' :module: target.autodoc_type_aliases',
'',
' docstring',
'',
'',
".. py:function:: prod(x: tuple[float, TypeAliasForwardRef('my.module.myfrac')]) -> float",
" prod(x: tuple[~fractions.Fraction, TypeAliasForwardRef('my.module.myfrac')]) -> ~fractions.Fraction",
' :module: target.autodoc_type_aliases',
'',
' docstring',
Expand All @@ -1459,7 +1514,7 @@ def test_autodoc_type_aliases(app: SphinxTestApp) -> None:
' docstring',
'',
'',
'.. py:function:: sum(x: myint, y: myint) -> myint',
'.. py:function:: sum(x: my.module.myfrac, y: my.module.myfrac) -> my.module.myfrac',
' :module: target.autodoc_type_aliases',
'',
' docstring',
Expand Down Expand Up @@ -1495,7 +1550,7 @@ def test_autodoc_type_aliases(app: SphinxTestApp) -> None:
srcdir='autodoc_typehints_description_and_type_aliases',
confoverrides={
'autodoc_typehints': 'description',
'autodoc_type_aliases': {'myint': 'myint'},
'autodoc_type_aliases': {'myfrac': 'my.module.myfrac'},
},
)
def test_autodoc_typehints_description_and_type_aliases(app: SphinxTestApp) -> None:
Expand All @@ -1511,12 +1566,12 @@ def test_autodoc_typehints_description_and_type_aliases(app: SphinxTestApp) -> N
' docstring\n'
'\n'
' Parameters:\n'
' * **x** (*myint*)\n'
' * **x** (*my.module.myfrac*)\n'
'\n'
' * **y** (*myint*)\n'
' * **y** (*my.module.myfrac*)\n'
'\n'
' Return type:\n'
' myint\n'
' my.module.myfrac\n'
)


Expand Down