diff options
Diffstat (limited to 'sphinx/util/typing.py')
-rw-r--r-- | sphinx/util/typing.py | 174 |
1 files changed, 53 insertions, 121 deletions
diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index db754bb03..fcecb8bb1 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -11,18 +11,21 @@ import sys import typing from struct import Struct -from typing import Any, Callable, Dict, Generator, List, Optional, Tuple, TypeVar, Union +from types import TracebackType +from typing import Any, Callable, Dict, Generator, List, Optional, Tuple, Type, TypeVar, Union from docutils import nodes from docutils.parsers.rst.states import Inliner +from sphinx.deprecation import RemovedInSphinx60Warning, deprecated_alias + if sys.version_info > (3, 7): from typing import ForwardRef else: from typing import _ForwardRef # type: ignore class ForwardRef: - """A pseudo ForwardRef class for py35 and py36.""" + """A pseudo ForwardRef class for py36.""" def __init__(self, arg: Any, is_argument: bool = True) -> None: self.arg = arg @@ -40,8 +43,12 @@ if False: from typing import Type # NOQA # for python3.5.1 -# An entry of Directive.option_spec -DirectiveOption = Callable[[str], Any] +# builtin classes that have incorrect __module__ +INVALID_BUILTIN_CLASSES = { + Struct: 'struct.Struct', # Before Python 3.9 + TracebackType: 'types.TracebackType', +} + # Text like nodes which are initialized with text and rawsource TextlikeNode = Union[nodes.Text, nodes.TextElement] @@ -56,6 +63,9 @@ PathMatcher = Callable[[str], bool] RoleFunction = Callable[[str, str, str, int, Inliner, Dict[str, Any], List[str]], Tuple[List[nodes.Node], List[nodes.system_message]]] +# A option spec for directive +OptionSpec = Dict[str, Callable[[Optional[str]], Any]] + # title getter functions for enumerable nodes (see sphinx.domains.std) TitleGetter = Callable[[nodes.Node], str] @@ -83,9 +93,6 @@ def get_type_hints(obj: Any, globalns: Dict = None, localns: Dict = None) -> Dic except KeyError: # a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084) return {} - except AttributeError: - # AttributeError is raised on 3.5.2 (fixed by 3.5.3) - return {} def is_system_TypeVar(typ: Any) -> bool: @@ -94,7 +101,7 @@ def is_system_TypeVar(typ: Any) -> bool: return modname == 'typing' and isinstance(typ, TypeVar) -def restify(cls: Optional["Type"]) -> str: +def restify(cls: Optional[Type]) -> str: """Convert python class to a reST reference.""" from sphinx.util import inspect # lazy loading @@ -102,9 +109,8 @@ def restify(cls: Optional["Type"]) -> str: return ':obj:`None`' elif cls is Ellipsis: return '...' - elif cls is Struct: - # Before Python 3.9, struct.Struct class has incorrect __module__. - return ':class:`struct.Struct`' + elif cls in INVALID_BUILTIN_CLASSES: + return ':class:`%s`' % INVALID_BUILTIN_CLASSES[cls] elif inspect.isNewType(cls): return ':class:`%s`' % cls.__name__ elif types_Union and isinstance(cls, types_Union): @@ -122,7 +128,7 @@ def restify(cls: Optional["Type"]) -> str: return _restify_py36(cls) -def _restify_py37(cls: Optional["Type"]) -> str: +def _restify_py37(cls: Optional[Type]) -> str: """Convert python class to a reST reference.""" from sphinx.util import inspect # lazy loading @@ -177,7 +183,7 @@ def _restify_py37(cls: Optional["Type"]) -> str: return ':obj:`%s.%s`' % (cls.__module__, cls.__name__) -def _restify_py36(cls: Optional["Type"]) -> str: +def _restify_py36(cls: Optional[Type]) -> str: module = getattr(cls, '__module__', None) if module == 'typing': if getattr(cls, '_name', None): @@ -196,7 +202,7 @@ def _restify_py36(cls: Optional["Type"]) -> str: qualname = repr(cls) if (isinstance(cls, typing.TupleMeta) and # type: ignore - not hasattr(cls, '__tuple_params__')): # for Python 3.6 + not hasattr(cls, '__tuple_params__')): params = cls.__args__ if params: param_str = ', '.join(restify(p) for p in params) @@ -204,40 +210,22 @@ def _restify_py36(cls: Optional["Type"]) -> str: else: return ':class:`%s`' % qualname elif isinstance(cls, typing.GenericMeta): - params = None - if hasattr(cls, '__args__'): - # for Python 3.5.2+ - if cls.__args__ is None or len(cls.__args__) <= 2: # type: ignore # NOQA - params = cls.__args__ # type: ignore - elif cls.__origin__ == Generator: # type: ignore - params = cls.__args__ # type: ignore - else: # typing.Callable - args = ', '.join(restify(arg) for arg in cls.__args__[:-1]) # type: ignore - result = restify(cls.__args__[-1]) # type: ignore - return ':class:`%s`\\ [[%s], %s]' % (qualname, args, result) - elif hasattr(cls, '__parameters__'): - # for Python 3.5.0 and 3.5.1 - params = cls.__parameters__ # type: ignore + if cls.__args__ is None or len(cls.__args__) <= 2: # type: ignore # NOQA + params = cls.__args__ # type: ignore + elif cls.__origin__ == Generator: # type: ignore + params = cls.__args__ # type: ignore + else: # typing.Callable + args = ', '.join(restify(arg) for arg in cls.__args__[:-1]) # type: ignore + result = restify(cls.__args__[-1]) # type: ignore + return ':class:`%s`\\ [[%s], %s]' % (qualname, args, result) if params: param_str = ', '.join(restify(p) for p in params) return ':class:`%s`\\ [%s]' % (qualname, param_str) else: return ':class:`%s`' % qualname - elif (hasattr(typing, 'UnionMeta') and - isinstance(cls, typing.UnionMeta) and # type: ignore - hasattr(cls, '__union_params__')): # for Python 3.5 - params = cls.__union_params__ - if params is not None: - if len(params) == 2 and params[1] is NoneType: - return ':obj:`Optional`\\ [%s]' % restify(params[0]) - else: - param_str = ', '.join(restify(p) for p in params) - return ':obj:`%s`\\ [%s]' % (qualname, param_str) - else: - return ':obj:`%s`' % qualname elif (hasattr(cls, '__origin__') and - cls.__origin__ is typing.Union): # for Python 3.5.2+ + cls.__origin__ is typing.Union): params = cls.__args__ if params is not None: if len(params) > 1 and params[-1] is NoneType: @@ -251,31 +239,6 @@ def _restify_py36(cls: Optional["Type"]) -> str: return ':obj:`Union`\\ [%s]' % param_str else: return ':obj:`Union`' - elif (isinstance(cls, typing.CallableMeta) and # type: ignore - getattr(cls, '__args__', None) is not None and - hasattr(cls, '__result__')): # for Python 3.5 - # Skipped in the case of plain typing.Callable - args = cls.__args__ - if args is None: - return qualname - elif args is Ellipsis: - args_str = '...' - else: - formatted_args = (restify(a) for a in args) # type: ignore - args_str = '[%s]' % ', '.join(formatted_args) - - return ':class:`%s`\\ [%s, %s]' % (qualname, args_str, stringify(cls.__result__)) - elif (isinstance(cls, typing.TupleMeta) and # type: ignore - hasattr(cls, '__tuple_params__') and - hasattr(cls, '__tuple_use_ellipsis__')): # for Python 3.5 - params = cls.__tuple_params__ - if params is not None: - param_strings = [restify(p) for p in params] - if cls.__tuple_use_ellipsis__: - param_strings.append('...') - return ':class:`%s`\\ [%s]' % (qualname, ', '.join(param_strings)) - else: - return ':class:`%s`' % qualname elif hasattr(cls, '__qualname__'): if cls.__module__ == 'typing': return ':class:`%s`' % cls.__qualname__ @@ -309,7 +272,10 @@ def stringify(annotation: Any) -> str: else: return annotation elif isinstance(annotation, TypeVar): - return annotation.__name__ + if annotation.__module__ == 'typing': + return annotation.__name__ + else: + return '.'.join([annotation.__module__, annotation.__name__]) elif inspect.isNewType(annotation): # Could not get the module where it defiend return annotation.__name__ @@ -317,14 +283,13 @@ def stringify(annotation: Any) -> str: return repr(annotation) elif annotation is NoneType: return 'None' + elif annotation in INVALID_BUILTIN_CLASSES: + return INVALID_BUILTIN_CLASSES[annotation] elif (getattr(annotation, '__module__', None) == 'builtins' and hasattr(annotation, '__qualname__')): return annotation.__qualname__ elif annotation is Ellipsis: return '...' - elif annotation is Struct: - # Before Python 3.9, struct.Struct class has incorrect __module__. - return 'struct.Struct' if sys.version_info >= (3, 7): # py37+ return _stringify_py37(annotation) @@ -393,7 +358,7 @@ def _stringify_py37(annotation: Any) -> str: def _stringify_py36(annotation: Any) -> str: - """stringify() for py35 and py36.""" + """stringify() for py36.""" module = getattr(annotation, '__module__', None) if module == 'typing': if getattr(annotation, '_name', None): @@ -421,35 +386,20 @@ def _stringify_py36(annotation: Any) -> str: 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 - elif annotation.__origin__ == Generator: # type: ignore - 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 annotation.__args__ is None or len(annotation.__args__) <= 2: # type: ignore # NOQA + params = annotation.__args__ # type: ignore + elif annotation.__origin__ == Generator: # type: ignore + 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) 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: - 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+ + annotation.__origin__ is typing.Union): params = annotation.__args__ if params is not None: if len(params) > 1 and params[-1] is NoneType: @@ -461,30 +411,12 @@ def _stringify_py36(annotation: Any) -> str: 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 + + +deprecated_alias('sphinx.util.typing', + { + 'DirectiveOption': Callable[[str], Any], + }, + RemovedInSphinx60Warning) |