2
2
import sys
3
3
import textwrap
4
4
import typing
5
- from typing import Any , AnyStr , Tuple , TypeVar , get_type_hints
5
+ from functools import partial
6
+ from typing import Any , AnyStr , Callable , Tuple , TypeVar , get_type_hints
6
7
7
8
from sphinx .util import logging
8
9
from sphinx .util .inspect import signature as Signature
@@ -84,9 +85,16 @@ def get_annotation_args(annotation, module: str, class_name: str) -> Tuple:
84
85
return getattr (annotation , '__args__' , ())
85
86
86
87
87
- def format_annotation (annotation ,
88
+ def format_annotation (annotation : Any ,
88
89
fully_qualified : bool = False ,
89
- simplify_optional_unions : bool = True ) -> str :
90
+ simplify_optional_unions : bool = True ,
91
+ typehints_formatter : Callable = lambda _annotation , ** _opts : None ) -> str :
92
+ # all options except for the formatter:
93
+ opts = dict (fully_qualified = fully_qualified , simplify_optional_unions = simplify_optional_unions )
94
+ formatted = typehints_formatter (annotation , ** opts )
95
+ if formatted is not None :
96
+ return formatted
97
+
90
98
# Special cases
91
99
if annotation is None or annotation is type (None ): # noqa: E721
92
100
return ':py:obj:`None`'
@@ -130,18 +138,18 @@ def format_annotation(annotation,
130
138
args_format = '\\ [:py:data:`{prefix}typing.Union`\\ [{{}}]]' .format (prefix = prefix )
131
139
args = tuple (x for x in args if x is not type (None )) # noqa: E721
132
140
elif full_name == 'typing.Callable' and args and args [0 ] is not ...:
141
+ opts_shortened = dict (opts , typehints_formatter = typehints_formatter )
142
+ del opts_shortened ['fully_qualified' ]
133
143
formatted_args = '\\ [\\ [' + ', ' .join (
134
- format_annotation (
135
- arg , simplify_optional_unions = simplify_optional_unions )
144
+ format_annotation (arg , ** opts_shortened )
136
145
for arg in args [:- 1 ]) + ']'
137
- formatted_args += ', ' + format_annotation (
138
- args [- 1 ], simplify_optional_unions = simplify_optional_unions ) + ']'
146
+ formatted_args += ', ' + format_annotation (args [- 1 ], ** opts_shortened ) + ']'
139
147
elif full_name == 'typing.Literal' :
140
148
formatted_args = '\\ [' + ', ' .join (repr (arg ) for arg in args ) + ']'
141
149
142
150
if args and not formatted_args :
143
151
formatted_args = args_format .format (', ' .join (
144
- format_annotation (arg , fully_qualified , simplify_optional_unions )
152
+ format_annotation (arg , ** opts , typehints_formatter = typehints_formatter )
145
153
for arg in args ))
146
154
147
155
return ':py:{role}:`{prefix}{full_name}`{formatted_args}' .format (
@@ -410,22 +418,29 @@ def process_docstring(app, what, name, obj, options, lines):
410
418
obj = obj .fget
411
419
412
420
if callable (obj ):
421
+
413
422
if inspect .isclass (obj ):
414
423
obj = getattr (obj , '__init__' )
415
424
416
425
obj = inspect .unwrap (obj )
417
426
type_hints = get_all_type_hints (obj , name )
427
+ if not type_hints :
428
+ return
429
+
430
+ fmt = partial (
431
+ format_annotation ,
432
+ fully_qualified = app .config .typehints_fully_qualified ,
433
+ simplify_optional_unions = app .config .simplify_optional_unions ,
434
+ typehints_formatter = app .config .typehints_formatter ,
435
+ )
418
436
419
437
for argname , annotation in type_hints .items ():
420
438
if argname == 'return' :
421
439
continue # this is handled separately later
422
440
if argname .endswith ('_' ):
423
441
argname = '{}\\ _' .format (argname [:- 1 ])
424
442
425
- formatted_annotation = format_annotation (
426
- annotation ,
427
- fully_qualified = app .config .typehints_fully_qualified ,
428
- simplify_optional_unions = app .config .simplify_optional_unions )
443
+ formatted_annotation = fmt (annotation )
429
444
430
445
searchfor = [':{} {}:' .format (field , argname )
431
446
for field in ('param' , 'parameter' , 'arg' , 'argument' )]
@@ -451,10 +466,7 @@ def process_docstring(app, what, name, obj, options, lines):
451
466
if what == 'method' and name .endswith ('.__init__' ):
452
467
return
453
468
454
- formatted_annotation = format_annotation (
455
- type_hints ['return' ], fully_qualified = app .config .typehints_fully_qualified ,
456
- simplify_optional_unions = app .config .simplify_optional_unions
457
- )
469
+ formatted_annotation = fmt (type_hints ['return' ])
458
470
459
471
insert_index = len (lines )
460
472
for i , line in enumerate (lines ):
@@ -485,6 +497,7 @@ def setup(app):
485
497
app .add_config_value ('typehints_fully_qualified' , False , 'env' )
486
498
app .add_config_value ('typehints_document_rtype' , True , 'env' )
487
499
app .add_config_value ('simplify_optional_unions' , True , 'env' )
500
+ app .add_config_value ('typehints_formatter' , lambda _annotation , ** _opts : None , 'env' )
488
501
app .connect ('builder-inited' , builder_ready )
489
502
app .connect ('autodoc-process-signature' , process_signature )
490
503
app .connect ('autodoc-process-docstring' , process_docstring )
0 commit comments