diff --git a/AUTHORS.rst b/AUTHORS.rst index b702c64c0f9..31d6ec3cbf5 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -120,6 +120,7 @@ Contributors * Tim Hoffmann -- theme improvements * Tim Pillinger -- documentation improvements * Valentin Heinisch -- warning types improvement +* Vedant Kumar -- autodoc fixes * Victor Wheeler -- documentation improvements * Vince Salvino -- JavaScript search improvements * Will Maier -- directory HTML builder diff --git a/CHANGES.rst b/CHANGES.rst index ad6d698341a..7a2ff711aad 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,6 +13,11 @@ Bugs fixed English stemmer) and Dutch (which uses the Dutch Porter stemmer). Patch by Hugo van Kemenade +* #14576: autodoc: Fix an :py:exc:`IndexError` when an + ``autodoc-process-signature`` event handler returns a signature + for a data object. + Patch by Vedant Kumar. + Release 9.1.0 (released Dec 31, 2025) ===================================== diff --git a/sphinx/ext/autodoc/_dynamic/_signatures.py b/sphinx/ext/autodoc/_dynamic/_signatures.py index de55c44fb9f..8586799d018 100644 --- a/sphinx/ext/autodoc/_dynamic/_signatures.py +++ b/sphinx/ext/autodoc/_dynamic/_signatures.py @@ -125,7 +125,14 @@ def _format_signatures( ): if len(result) == 2 and isinstance(result[0], str): args, retann = result - signatures[0] = (args, retann if isinstance(retann, str) else '') + replacement = (args, retann if isinstance(retann, str) else '') + if signatures: + signatures[0] = replacement + else: + # No signature was introspected (e.g. for data objects), + # but an event handler returned a replacement signature. + # See: https://github.com/sphinx-doc/sphinx/issues/14576 + signatures.append(replacement) if props.obj_type in {'module', 'data', 'type'}: signatures[1:] = () # discard all signatures save the first diff --git a/tests/test_ext_autodoc/test_ext_autodoc_signatures.py b/tests/test_ext_autodoc/test_ext_autodoc_signatures.py index f3f87e4d38a..dd00d11070c 100644 --- a/tests/test_ext_autodoc/test_ext_autodoc_signatures.py +++ b/tests/test_ext_autodoc/test_ext_autodoc_signatures.py @@ -355,3 +355,56 @@ def func(x: int, y: int) -> int: # type: ignore[empty-body] assert captured == [ (app, 'function', '.func', func, options, '(x: int, y: int)', 'int') ] + + +def test_autodoc_process_signature_data_object() -> None: + # https://github.com/sphinx-doc/sphinx/issues/14576 + # No signature is introspected for data objects, but a handler for + # ``autodoc-process-signature`` may still return a replacement + # signature; this must not raise an IndexError. + from sphinx.ext.autodoc._property_types import _AssignStatementProperties + + def process_signature(*args: Any) -> tuple[str, str | None]: + return '()', None + + events = FakeEvents() + events.connect('autodoc-process-signature', process_signature) + + class SigBug: + class_var: Any + + def __call__(self) -> None: + pass + + sig_bug = SigBug() + + props = _AssignStatementProperties( + obj_type='data', + module_name='', + parts=('sig_bug',), + docstring_lines=(), + value=sig_bug, + annotation='', + class_var=False, + instance_var=False, + _obj=sig_bug, + _obj___module__=None, + _obj_is_generic_alias=False, + _obj_is_attribute_descriptor=False, + _obj_is_mock=False, + _obj_is_sentinel=None, + _obj_repr_rst='', + _obj_type_annotation=None, + ) + + signatures = _format_signatures( + autodoc_annotations={}, + config=_AutodocConfig(), + docstrings=None, + events=events, + get_attr=safe_getattr, + options=_AutoDocumenterOptions(), + parent=None, + props=props, + ) + assert signatures == [('()', '')]