21
21
from scanpydoc import elegant_typehints
22
22
23
23
24
- def _format_full (annotation : Type [Any ], fully_qualified : bool = False ):
24
+ def _format_full (
25
+ annotation : Type [Any ],
26
+ fully_qualified : bool = False ,
27
+ simplify_optional_unions : bool = True ,
28
+ ):
25
29
if inspect .isclass (annotation ) and annotation .__module__ == "builtins" :
26
- return _format_orig (annotation , fully_qualified )
30
+ return _format_orig (annotation , fully_qualified , simplify_optional_unions )
27
31
28
32
origin = get_origin (annotation )
29
33
tilde = "" if fully_qualified else "~"
30
34
31
35
annotation_cls = annotation if inspect .isclass (annotation ) else type (annotation )
32
36
if annotation_cls .__module__ == "typing" :
33
- return _format_orig (annotation , fully_qualified )
37
+ return _format_orig (annotation , fully_qualified , simplify_optional_unions )
34
38
35
39
# Only if this is a real class we override sphinx_autodoc_typehints
36
40
if inspect .isclass (annotation ) or inspect .isclass (origin ):
@@ -40,14 +44,22 @@ def _format_full(annotation: Type[Any], fully_qualified: bool = False):
40
44
if override is not None :
41
45
return f":py:{ role } :`{ tilde } { override } `"
42
46
43
- return _format_orig (annotation , fully_qualified )
47
+ return _format_orig (annotation , fully_qualified , simplify_optional_unions )
44
48
45
49
46
- def _format_terse (annotation : Type [Any ], fully_qualified : bool = False ) -> str :
50
+ def _format_terse (
51
+ annotation : Type [Any ],
52
+ fully_qualified : bool = False ,
53
+ simplify_optional_unions : bool = True ,
54
+ ) -> str :
47
55
origin = get_origin (annotation )
48
56
args = get_args (annotation )
49
57
tilde = "" if fully_qualified else "~"
50
- fmt = partial (_format_terse , fully_qualified = fully_qualified )
58
+ fmt = partial (
59
+ _format_terse ,
60
+ fully_qualified = fully_qualified ,
61
+ simplify_optional_unions = simplify_optional_unions ,
62
+ )
51
63
52
64
# display `Union[A, B]` as `A | B`
53
65
if origin is Union :
@@ -74,10 +86,14 @@ def _format_terse(annotation: Type[Any], fully_qualified: bool = False) -> str:
74
86
if origin is Literal :
75
87
return f"{{{ ', ' .join (map (repr , args ))} }}"
76
88
77
- return _format_full (annotation , fully_qualified )
89
+ return _format_full (annotation , fully_qualified , simplify_optional_unions )
78
90
79
91
80
- def format_annotation (annotation : Type [Any ], fully_qualified : bool = False ) -> str :
92
+ def format_annotation (
93
+ annotation : Type [Any ],
94
+ fully_qualified : bool = False ,
95
+ simplify_optional_unions : bool = True ,
96
+ ) -> str :
81
97
r"""Generate reStructuredText containing links to the types.
82
98
83
99
Unlike :func:`sphinx_autodoc_typehints.format_annotation`,
@@ -87,6 +103,9 @@ def format_annotation(annotation: Type[Any], fully_qualified: bool = False) -> s
87
103
annotation: A type or class used as type annotation.
88
104
fully_qualified: If links should be formatted as fully qualified
89
105
(e.g. ``:py:class:`foo.Bar```) or not (e.g. ``:py:class:`~foo.Bar```).
106
+ simplify_optional_unions: If Unions should be minimized if they contain
107
+ 3 or more elements one of which is ``None``. (If ``True``, e.g.
108
+ ``Optional[Union[str, int]]`` becomes ``Union[str, int, None]``)
90
109
91
110
Returns:
92
111
reStructuredText describing the type
@@ -100,7 +119,7 @@ def format_annotation(annotation: Type[Any], fully_qualified: bool = False) -> s
100
119
curframe = inspect .currentframe ()
101
120
calframe = inspect .getouterframes (curframe , 2 )
102
121
if calframe [1 ][3 ] == "process_docstring" :
103
- annot_fmt = format_both (annotation , fully_qualified )
122
+ annot_fmt = format_both (annotation , fully_qualified , simplify_optional_unions )
104
123
if elegant_typehints .annotate_defaults :
105
124
variables = calframe [1 ].frame .f_locals
106
125
sig = inspect .signature (variables ["obj" ])
@@ -111,14 +130,17 @@ def format_annotation(annotation: Type[Any], fully_qualified: bool = False) -> s
111
130
annot_fmt += f" (default: ``{ _escape (repr (default ))} ``)"
112
131
return annot_fmt
113
132
else : # recursive use
114
- return _format_full (annotation , fully_qualified )
133
+ return _format_full (annotation , fully_qualified , simplify_optional_unions )
115
134
116
135
117
- def format_both (annotation : Type [Any ], fully_qualified : bool = False ):
118
- return (
119
- f":annotation-terse:`{ _escape (_format_terse (annotation , fully_qualified ))} `\\ "
120
- f":annotation-full:`{ _escape (_format_full (annotation , fully_qualified ))} `"
121
- )
136
+ def format_both (
137
+ annotation : Type [Any ],
138
+ fully_qualified : bool = False ,
139
+ simplify_optional_unions : bool = True ,
140
+ ) -> str :
141
+ terse = _format_terse (annotation , fully_qualified , simplify_optional_unions )
142
+ full = _format_full (annotation , fully_qualified , simplify_optional_unions )
143
+ return f":annotation-terse:`{ _escape (terse )} `\\ :annotation-full:`{ _escape (full )} `"
122
144
123
145
124
146
def _role_annot (
0 commit comments