diff --git a/CHANGES b/CHANGES index 8eb829101c1..4ab2ddabea7 100644 --- a/CHANGES +++ b/CHANGES @@ -92,6 +92,8 @@ Bugs fixed function * #10305: autodoc: Failed to extract optional forward-ref'ed typehints correctly via :confval:`autodoc_type_aliases` +* #10421: autodoc: :confval:`autodoc_preserve_defaults` doesn't work on class + methods * #10214: html: invalid language tag was generated if :confval:`language` contains a country code (ex. zh_CN) * #10236: html search: objects are duplicated in search result diff --git a/sphinx/ext/autodoc/preserve_defaults.py b/sphinx/ext/autodoc/preserve_defaults.py index 6c4ee8f0926..8b06a3af468 100644 --- a/sphinx/ext/autodoc/preserve_defaults.py +++ b/sphinx/ext/autodoc/preserve_defaults.py @@ -7,6 +7,7 @@ import ast import inspect import sys +from inspect import Parameter from typing import Any, Dict, List, Optional from sphinx.application import Sphinx @@ -96,8 +97,16 @@ def update_defvalue(app: Sphinx, obj: Any, bound_method: bool) -> None: if value is None: value = ast_unparse(default) # type: ignore parameters[i] = param.replace(default=DefaultValue(value)) + + if bound_method and inspect.ismethod(obj): + cls = inspect.Parameter('cls', Parameter.POSITIONAL_OR_KEYWORD) + parameters.insert(0, cls) + sig = sig.replace(parameters=parameters) - obj.__signature__ = sig + try: + obj.__signature__ = sig + except AttributeError: + obj.__dict__['__signature__'] = sig except (AttributeError, TypeError): # failed to update signature (ex. built-in or extension types) pass diff --git a/tests/roots/test-ext-autodoc/target/preserve_defaults.py b/tests/roots/test-ext-autodoc/target/preserve_defaults.py index fc83cf4fc3e..0cc3b4e2076 100644 --- a/tests/roots/test-ext-autodoc/target/preserve_defaults.py +++ b/tests/roots/test-ext-autodoc/target/preserve_defaults.py @@ -22,3 +22,9 @@ def meth(self, name: str = CONSTANT, sentinel: Any = SENTINEL, now: datetime = datetime.now(), color: int = 0xFFFFFF, *, kwarg1, kwarg2 = 0xFFFFFF) -> None: """docstring""" + + @classmethod + def clsmeth(cls, name: str = CONSTANT, sentinel: Any = SENTINEL, + now: datetime = datetime.now(), color: int = 0xFFFFFF, + *, kwarg1, kwarg2 = 0xFFFFFF) -> None: + """docstring""" diff --git a/tests/test_ext_autodoc_preserve_defaults.py b/tests/test_ext_autodoc_preserve_defaults.py index fe62c929eec..8a60f01df61 100644 --- a/tests/test_ext_autodoc_preserve_defaults.py +++ b/tests/test_ext_autodoc_preserve_defaults.py @@ -28,6 +28,15 @@ def test_preserve_defaults(app): ' docstring', '', '', + ' .. py:method:: Class.clsmeth(name: str = CONSTANT, sentinel: ~typing.Any = ' + 'SENTINEL, now: ~datetime.datetime = datetime.now(), color: int = %s, *, ' + 'kwarg1, kwarg2=%s) -> None' % (color, color), + ' :module: target.preserve_defaults', + ' :classmethod:', + '', + ' docstring', + '', + '', ' .. py:method:: Class.meth(name: str = CONSTANT, sentinel: ~typing.Any = ' 'SENTINEL, now: ~datetime.datetime = datetime.now(), color: int = %s, *, ' 'kwarg1, kwarg2=%s) -> None' % (color, color),