|
22 | 22 | from functools import lru_cache, reduce, partial
|
23 | 23 | from itertools import tee, groupby
|
24 | 24 | from types import ModuleType
|
25 |
| -from typing import ( |
26 |
| - cast, Any, Callable, Dict, Generator, Iterable, List, Mapping, Optional, Set, Tuple, |
27 |
| - Type, TypeVar, Union, |
| 25 | +from typing import ( # noqa: F401 |
| 26 | + cast, Any, Callable, Dict, Generator, Iterable, List, Mapping, NewType, |
| 27 | + Optional, Set, Tuple, Type, TypeVar, Union, |
28 | 28 | )
|
29 | 29 | from warnings import warn
|
30 | 30 |
|
@@ -1242,20 +1242,43 @@ def _link_inheritance(self):
|
1242 | 1242 |
|
1243 | 1243 | def _formatannotation(annot):
|
1244 | 1244 | """
|
1245 |
| - Format annotation, properly handling NewType types |
| 1245 | + Format typing annotation with better handling of `typing.NewType`, |
| 1246 | + `typing.Optional`, `nptyping.NDArray` and other types. |
1246 | 1247 |
|
1247 |
| - >>> import typing |
1248 |
| - >>> _formatannotation(typing.NewType('MyType', str)) |
| 1248 | + # >>> import typing |
| 1249 | + >>> _formatannotation(NewType('MyType', str)) |
1249 | 1250 | 'MyType'
|
| 1251 | + >>> _formatannotation(Optional[Tuple[Optional[int], None]]) |
| 1252 | + 'Optional[Tuple[Optional[int], None]]' |
1250 | 1253 | """
|
1251 |
| - module = getattr(annot, '__module__', '') |
1252 |
| - is_newtype = (getattr(annot, '__qualname__', '').startswith('NewType.') and |
1253 |
| - module == 'typing') |
1254 |
| - if is_newtype: |
1255 |
| - return annot.__name__ |
1256 |
| - if module.startswith('nptyping'): # GH-231 |
1257 |
| - return repr(annot) |
1258 |
| - return inspect.formatannotation(annot) |
| 1254 | + class force_repr(str): |
| 1255 | + __repr__ = str.__str__ |
| 1256 | + |
| 1257 | + def maybe_replace_reprs(a): |
| 1258 | + # NoneType -> None |
| 1259 | + if a is type(None): # noqa: E721 |
| 1260 | + return force_repr('None') |
| 1261 | + # Union[T, None] -> Optional[T] |
| 1262 | + if (getattr(a, '__origin__', None) is typing.Union and |
| 1263 | + len(a.__args__) == 2 and |
| 1264 | + a.__args__[1] is type(None)): # noqa: E721 |
| 1265 | + t = inspect.formatannotation(maybe_replace_reprs(a.__args__[0])) |
| 1266 | + return force_repr(f'Optional[{t}]') |
| 1267 | + # typing.NewType('T', foo) -> T |
| 1268 | + module = getattr(a, '__module__', '') |
| 1269 | + if module == 'typing' and getattr(a, '__qualname__', '').startswith('NewType.'): |
| 1270 | + return force_repr(a.__name__) |
| 1271 | + # nptyping.types._ndarray.NDArray -> NDArray[(Any,), Int[64]] # GH-231 |
| 1272 | + if module.startswith('nptyping.'): |
| 1273 | + return force_repr(repr(a)) |
| 1274 | + # Recurse into args |
| 1275 | + try: |
| 1276 | + a = a.copy_with(tuple([maybe_replace_reprs(arg) for arg in a.__args__])) |
| 1277 | + except Exception: |
| 1278 | + pass # Not a typing._GenericAlias |
| 1279 | + return a |
| 1280 | + |
| 1281 | + return str(inspect.formatannotation(maybe_replace_reprs(annot))) |
1259 | 1282 |
|
1260 | 1283 |
|
1261 | 1284 | class Function(Doc):
|
|
0 commit comments