17
17
get_origin ,
18
18
)
19
19
from pathlib import Path
20
+ from operator import attrgetter
20
21
from collections .abc import Mapping , Callable
21
22
22
23
import pytest
23
- import sphinx_autodoc_typehints as sat
24
- from sphinx .ext import napoleon
25
24
from sphinx .errors import ExtensionError
26
- from sphinx_autodoc_typehints .patches import (
27
- napoleon_numpy_docstring_return_type_processor ,
28
- )
29
25
30
26
from scanpydoc .elegant_typehints ._formatting import typehints_formatter
31
- from scanpydoc .elegant_typehints ._return_tuple import process_docstring
32
27
33
28
34
29
if TYPE_CHECKING :
@@ -81,6 +76,16 @@ def app(make_app_setup: Callable[..., Sphinx]) -> Sphinx:
81
76
82
77
@pytest .fixture ()
83
78
def process_doc (app : Sphinx ) -> ProcessDoc :
79
+ listeners = sorted (
80
+ (l for l in app .events .listeners ["autodoc-process-docstring" ]),
81
+ key = attrgetter ("priority" ),
82
+ )
83
+ assert [f"{ l .handler .__module__ } .{ l .handler .__qualname__ } " for l in listeners ] == [
84
+ "sphinx.ext.napoleon._process_docstring" ,
85
+ "sphinx_autodoc_typehints.process_docstring" ,
86
+ "scanpydoc.elegant_typehints._return_tuple.process_docstring" ,
87
+ ]
88
+
84
89
def process (fn : Callable [..., Any ], * , run_napoleon : bool = False ) -> list [str ]:
85
90
lines = (inspect .getdoc (fn ) or "" ).split ("\n " )
86
91
if isinstance (fn , property ):
@@ -89,13 +94,13 @@ def process(fn: Callable[..., Any], *, run_napoleon: bool = False) -> list[str]:
89
94
name = fn .__name__
90
95
else :
91
96
name = "???"
92
- napoleon_numpy_docstring_return_type_processor (
93
- app , "function" , name , fn , None , lines
94
- )
95
- if run_napoleon :
96
- napoleon . _process_docstring ( app , "function" , name , fn , None , lines ) # noqa: SLF001
97
- sat . process_docstring ( app , "function" , name , fn , None , lines )
98
- process_docstring (app , "function" , name , fn , None , lines )
97
+ for listener in listeners :
98
+ if (
99
+ not run_napoleon
100
+ and listener . handler . __module__ == "sphinx.ext.napoleon"
101
+ ):
102
+ continue
103
+ listener . handler (app , "function" , name , fn , None , lines )
99
104
return lines
100
105
101
106
return process
@@ -402,6 +407,32 @@ def fn_test() -> None: # pragma: no cover
402
407
]
403
408
404
409
410
+ def test_return_tuple_anonymous (process_doc : ProcessDoc ) -> None :
411
+ def fn_test () -> tuple [int , str ]: # pragma: no cover
412
+ """
413
+ Returns
414
+ -------
415
+ :
416
+ An int!
417
+ :
418
+ A str!
419
+ """ # noqa: D401, D205
420
+ return (1 , "foo" )
421
+
422
+ lines = [
423
+ l
424
+ for l in process_doc (fn_test , run_napoleon = True )
425
+ if l
426
+ if not re .match (r"^:(rtype|param)( \w+)?:" , l )
427
+ ]
428
+ assert lines == [
429
+ ":returns: :py:class:`int`" ,
430
+ " An int!" ,
431
+ " :py:class:`str`" ,
432
+ " A str!" ,
433
+ ]
434
+
435
+
405
436
def test_return_nodoc (process_doc : ProcessDoc ) -> None :
406
437
def fn () -> tuple [int , str ]: # pragma: no cover
407
438
"""No return section."""
0 commit comments