summaryrefslogtreecommitdiff
path: root/sphinx/util/typing.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/util/typing.py')
-rw-r--r--sphinx/util/typing.py174
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)