diff options
Diffstat (limited to 'sphinx/util/typing.py')
-rw-r--r-- | sphinx/util/typing.py | 154 |
1 files changed, 153 insertions, 1 deletions
diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index 1b2ec3f60..ccceefed6 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -8,7 +8,9 @@ :license: BSD, see LICENSE for details. """ -from typing import Any, Callable, Dict, List, Tuple, Union +import sys +import typing +from typing import Any, Callable, Dict, List, Tuple, TypeVar, Union from docutils import nodes from docutils.parsers.rst.states import Inliner @@ -35,3 +37,153 @@ TitleGetter = Callable[[nodes.Node], str] # inventory data on memory Inventory = Dict[str, Dict[str, Tuple[str, str, str, str]]] + + +def stringify(annotation: Any) -> str: + """Stringify type annotation object.""" + if isinstance(annotation, str): + return annotation + elif isinstance(annotation, TypeVar): # type: ignore + return annotation.__name__ + elif not annotation: + return repr(annotation) + elif annotation is NoneType: # type: ignore + return 'None' + elif getattr(annotation, '__module__', None) == 'builtins': + return annotation.__qualname__ + elif annotation is Ellipsis: + return '...' + + if sys.version_info >= (3, 7): # py37+ + return _stringify_py37(annotation) + else: + return _stringify_py36(annotation) + + +def _stringify_py37(annotation: Any) -> str: + """stringify() for py37+.""" + module = getattr(annotation, '__module__', None) + if module == 'typing': + if getattr(annotation, '_name', None): + qualname = annotation._name + elif getattr(annotation, '__qualname__', None): + qualname = annotation.__qualname__ + elif getattr(annotation, '__forward_arg__', None): + qualname = annotation.__forward_arg__ + else: + qualname = stringify(annotation.__origin__) # ex. Union + elif hasattr(annotation, '__qualname__'): + qualname = '%s.%s' % (module, annotation.__qualname__) + else: + qualname = repr(annotation) + + if getattr(annotation, '__args__', None): + if qualname == 'Union': + if len(annotation.__args__) == 2 and annotation.__args__[1] is NoneType: # type: ignore # NOQA + return 'Optional[%s]' % stringify(annotation.__args__[0]) + else: + args = ', '.join(stringify(a) for a in annotation.__args__) + return '%s[%s]' % (qualname, args) + elif qualname == 'Callable': + args = ', '.join(stringify(a) for a in annotation.__args__[:-1]) + returns = stringify(annotation.__args__[-1]) + return '%s[[%s], %s]' % (qualname, args, returns) + elif annotation._special: + return qualname + else: + args = ', '.join(stringify(a) for a in annotation.__args__) + return '%s[%s]' % (qualname, args) + + return qualname + + +def _stringify_py36(annotation: Any) -> str: + """stringify() for py35 and py36.""" + module = getattr(annotation, '__module__', None) + if module == 'typing': + if getattr(annotation, '_name', None): + qualname = annotation._name + elif getattr(annotation, '__qualname__', None): + qualname = annotation.__qualname__ + elif getattr(annotation, '__forward_arg__', None): + qualname = annotation.__forward_arg__ + elif getattr(annotation, '__origin__', None): + qualname = stringify(annotation.__origin__) # ex. Union + else: + qualname = repr(annotation).replace('typing.', '') + elif hasattr(annotation, '__qualname__'): + qualname = '%s.%s' % (module, annotation.__qualname__) + else: + qualname = repr(annotation) + + if (isinstance(annotation, typing.TupleMeta) and # type: ignore + not hasattr(annotation, '__tuple_params__')): # for Python 3.6 + params = annotation.__args__ + if params: + param_str = ', '.join(stringify(p) for p in params) + return '%s[%s]' % (qualname, param_str) + else: + return qualname + elif isinstance(annotation, typing.GenericMeta): + params = None + if hasattr(annotation, '__args__'): + # for Python 3.5.2+ + if annotation.__args__ is None or len(annotation.__args__) <= 2: # type: ignore # NOQA + params = annotation.__args__ # type: ignore + else: # typing.Callable + args = ', '.join(stringify(arg) for arg + in annotation.__args__[:-1]) # type: ignore + result = stringify(annotation.__args__[-1]) # type: ignore + return '%s[[%s], %s]' % (qualname, args, result) + elif hasattr(annotation, '__parameters__'): + # for Python 3.5.0 and 3.5.1 + params = annotation.__parameters__ # type: ignore + if params is not None: + param_str = ', '.join(stringify(p) for p in params) + return '%s[%s]' % (qualname, param_str) + elif (hasattr(typing, 'UnionMeta') and + isinstance(annotation, typing.UnionMeta) and # type: ignore + hasattr(annotation, '__union_params__')): # for Python 3.5 + params = annotation.__union_params__ + if params is not None: + if len(params) == 2 and params[1] is NoneType: # type: ignore + return 'Optional[%s]' % stringify(params[0]) + else: + param_str = ', '.join(stringify(p) for p in params) + return '%s[%s]' % (qualname, param_str) + elif (hasattr(annotation, '__origin__') and + annotation.__origin__ is typing.Union): # for Python 3.5.2+ + params = annotation.__args__ + if params is not None: + if len(params) == 2 and params[1] is NoneType: # type: ignore + return 'Optional[%s]' % stringify(params[0]) + else: + param_str = ', '.join(stringify(p) for p in params) + return 'Union[%s]' % param_str + elif (isinstance(annotation, typing.CallableMeta) and # type: ignore + getattr(annotation, '__args__', None) is not None and + hasattr(annotation, '__result__')): # for Python 3.5 + # Skipped in the case of plain typing.Callable + args = annotation.__args__ + if args is None: + return qualname + elif args is Ellipsis: + args_str = '...' + else: + formatted_args = (stringify(a) for a in args) + args_str = '[%s]' % ', '.join(formatted_args) + return '%s[%s, %s]' % (qualname, + args_str, + stringify(annotation.__result__)) + elif (isinstance(annotation, typing.TupleMeta) and # type: ignore + hasattr(annotation, '__tuple_params__') and + hasattr(annotation, '__tuple_use_ellipsis__')): # for Python 3.5 + params = annotation.__tuple_params__ + if params is not None: + param_strings = [stringify(p) for p in params] + if annotation.__tuple_use_ellipsis__: + param_strings.append('...') + return '%s[%s]' % (qualname, + ', '.join(param_strings)) + + return qualname |