mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Add sphinx.util.typing:stringify() to represent annotations as string
This commit is contained in:
parent
2f7823e1a6
commit
05daa3c7ce
3
CHANGES
3
CHANGES
@ -21,6 +21,9 @@ Deprecated
|
|||||||
* ``sphinx.roles.Index``
|
* ``sphinx.roles.Index``
|
||||||
* ``sphinx.util.detect_encoding()``
|
* ``sphinx.util.detect_encoding()``
|
||||||
* ``sphinx.util.get_module_source()``
|
* ``sphinx.util.get_module_source()``
|
||||||
|
* ``sphinx.util.inspect.Signature.format_annotation()``
|
||||||
|
* ``sphinx.util.inspect.Signature.format_annotation_new()``
|
||||||
|
* ``sphinx.util.inspect.Signature.format_annotation_old()``
|
||||||
|
|
||||||
Features added
|
Features added
|
||||||
--------------
|
--------------
|
||||||
|
@ -81,6 +81,21 @@ The following is a list of deprecated interfaces.
|
|||||||
- 4.0
|
- 4.0
|
||||||
- N/A
|
- N/A
|
||||||
|
|
||||||
|
* - ``sphinx.util.inspect.Signature.format_annotation()``
|
||||||
|
- 2.4
|
||||||
|
- 4.0
|
||||||
|
- ``sphinx.util.typing.stringify()``
|
||||||
|
|
||||||
|
* - ``sphinx.util.inspect.Signature.format_annotation_new()``
|
||||||
|
- 2.4
|
||||||
|
- 4.0
|
||||||
|
- ``sphinx.util.typing.stringify()``
|
||||||
|
|
||||||
|
* - ``sphinx.util.inspect.Signature.format_annotation_old()``
|
||||||
|
- 2.4
|
||||||
|
- 4.0
|
||||||
|
- ``sphinx.util.typing.stringify()``
|
||||||
|
|
||||||
* - ``sphinx.builders.gettext.POHEADER``
|
* - ``sphinx.builders.gettext.POHEADER``
|
||||||
- 2.3
|
- 2.3
|
||||||
- 4.0
|
- 4.0
|
||||||
|
@ -22,9 +22,9 @@ from inspect import ( # NOQA
|
|||||||
from io import StringIO
|
from io import StringIO
|
||||||
from typing import Any, Callable, Mapping, List, Tuple
|
from typing import Any, Callable, Mapping, List, Tuple
|
||||||
|
|
||||||
from sphinx.deprecation import RemovedInSphinx30Warning
|
from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
from sphinx.util.typing import NoneType
|
from sphinx.util.typing import stringify as stringify_annotation
|
||||||
|
|
||||||
if sys.version_info > (3, 7):
|
if sys.version_info > (3, 7):
|
||||||
from types import (
|
from types import (
|
||||||
@ -403,11 +403,11 @@ class Signature:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def format_args(self, show_annotation: bool = True) -> str:
|
def format_args(self, show_annotation: bool = True) -> str:
|
||||||
def format_param_annotation(param: inspect.Parameter) -> str:
|
def get_annotation(param: inspect.Parameter) -> Any:
|
||||||
if isinstance(param.annotation, str) and param.name in self.annotations:
|
if isinstance(param.annotation, str) and param.name in self.annotations:
|
||||||
return self.format_annotation(self.annotations[param.name])
|
return self.annotations[param.name]
|
||||||
else:
|
else:
|
||||||
return self.format_annotation(param.annotation)
|
return param.annotation
|
||||||
|
|
||||||
args = []
|
args = []
|
||||||
last_kind = None
|
last_kind = None
|
||||||
@ -431,7 +431,7 @@ class Signature:
|
|||||||
arg.write(param.name)
|
arg.write(param.name)
|
||||||
if show_annotation and param.annotation is not param.empty:
|
if show_annotation and param.annotation is not param.empty:
|
||||||
arg.write(': ')
|
arg.write(': ')
|
||||||
arg.write(format_param_annotation(param))
|
arg.write(stringify_annotation(get_annotation(param)))
|
||||||
if param.default is not param.empty:
|
if param.default is not param.empty:
|
||||||
if param.annotation is param.empty or show_annotation is False:
|
if param.annotation is param.empty or show_annotation is False:
|
||||||
arg.write('=')
|
arg.write('=')
|
||||||
@ -444,13 +444,13 @@ class Signature:
|
|||||||
arg.write(param.name)
|
arg.write(param.name)
|
||||||
if show_annotation and param.annotation is not param.empty:
|
if show_annotation and param.annotation is not param.empty:
|
||||||
arg.write(': ')
|
arg.write(': ')
|
||||||
arg.write(format_param_annotation(param))
|
arg.write(stringify_annotation(get_annotation(param)))
|
||||||
elif param.kind == param.VAR_KEYWORD:
|
elif param.kind == param.VAR_KEYWORD:
|
||||||
arg.write('**')
|
arg.write('**')
|
||||||
arg.write(param.name)
|
arg.write(param.name)
|
||||||
if show_annotation and param.annotation is not param.empty:
|
if show_annotation and param.annotation is not param.empty:
|
||||||
arg.write(': ')
|
arg.write(': ')
|
||||||
arg.write(format_param_annotation(param))
|
arg.write(stringify_annotation(get_annotation(param)))
|
||||||
|
|
||||||
args.append(arg.getvalue())
|
args.append(arg.getvalue())
|
||||||
last_kind = param.kind
|
last_kind = param.kind
|
||||||
@ -459,164 +459,29 @@ class Signature:
|
|||||||
return '(%s)' % ', '.join(args)
|
return '(%s)' % ', '.join(args)
|
||||||
else:
|
else:
|
||||||
if 'return' in self.annotations:
|
if 'return' in self.annotations:
|
||||||
annotation = self.format_annotation(self.annotations['return'])
|
annotation = stringify_annotation(self.annotations['return'])
|
||||||
else:
|
else:
|
||||||
annotation = self.format_annotation(self.return_annotation)
|
annotation = stringify_annotation(self.return_annotation)
|
||||||
|
|
||||||
return '(%s) -> %s' % (', '.join(args), annotation)
|
return '(%s) -> %s' % (', '.join(args), annotation)
|
||||||
|
|
||||||
def format_annotation(self, annotation: Any) -> str:
|
def format_annotation(self, annotation: Any) -> str:
|
||||||
"""Return formatted representation of a type annotation.
|
"""Return formatted representation of a type annotation."""
|
||||||
|
warnings.warn('format_annotation() is deprecated',
|
||||||
Show qualified names for types and additional details for types from
|
RemovedInSphinx40Warning)
|
||||||
the ``typing`` module.
|
return stringify_annotation(annotation)
|
||||||
|
|
||||||
Displaying complex types from ``typing`` relies on its private API.
|
|
||||||
"""
|
|
||||||
if isinstance(annotation, str):
|
|
||||||
return annotation
|
|
||||||
elif isinstance(annotation, typing.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 self.format_annotation_new(annotation)
|
|
||||||
else:
|
|
||||||
return self.format_annotation_old(annotation)
|
|
||||||
|
|
||||||
def format_annotation_new(self, annotation: Any) -> str:
|
def format_annotation_new(self, annotation: Any) -> str:
|
||||||
"""format_annotation() for py37+"""
|
"""format_annotation() for py37+"""
|
||||||
module = getattr(annotation, '__module__', None)
|
warnings.warn('format_annotation_new() is deprecated',
|
||||||
if module == 'typing':
|
RemovedInSphinx40Warning)
|
||||||
if getattr(annotation, '_name', None):
|
return stringify_annotation(annotation)
|
||||||
qualname = annotation._name
|
|
||||||
elif getattr(annotation, '__qualname__', None):
|
|
||||||
qualname = annotation.__qualname__
|
|
||||||
elif getattr(annotation, '__forward_arg__', None):
|
|
||||||
qualname = annotation.__forward_arg__
|
|
||||||
else:
|
|
||||||
qualname = self.format_annotation(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]' % self.format_annotation(annotation.__args__[0])
|
|
||||||
else:
|
|
||||||
args = ', '.join(self.format_annotation(a) for a in annotation.__args__)
|
|
||||||
return '%s[%s]' % (qualname, args)
|
|
||||||
elif qualname == 'Callable':
|
|
||||||
args = ', '.join(self.format_annotation(a) for a in annotation.__args__[:-1])
|
|
||||||
returns = self.format_annotation(annotation.__args__[-1])
|
|
||||||
return '%s[[%s], %s]' % (qualname, args, returns)
|
|
||||||
elif annotation._special:
|
|
||||||
return qualname
|
|
||||||
else:
|
|
||||||
args = ', '.join(self.format_annotation(a) for a in annotation.__args__)
|
|
||||||
return '%s[%s]' % (qualname, args)
|
|
||||||
|
|
||||||
return qualname
|
|
||||||
|
|
||||||
def format_annotation_old(self, annotation: Any) -> str:
|
def format_annotation_old(self, annotation: Any) -> str:
|
||||||
"""format_annotation() for py36 or below"""
|
"""format_annotation() for py36 or below"""
|
||||||
module = getattr(annotation, '__module__', None)
|
warnings.warn('format_annotation_old() is deprecated',
|
||||||
if module == 'typing':
|
RemovedInSphinx40Warning)
|
||||||
if getattr(annotation, '_name', None):
|
return stringify_annotation(annotation)
|
||||||
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 = self.format_annotation(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(self.format_annotation(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(self.format_annotation(arg) for arg
|
|
||||||
in annotation.__args__[:-1]) # type: ignore
|
|
||||||
result = self.format_annotation(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(self.format_annotation(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]' % self.format_annotation(params[0])
|
|
||||||
else:
|
|
||||||
param_str = ', '.join(self.format_annotation(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]' % self.format_annotation(params[0])
|
|
||||||
else:
|
|
||||||
param_str = ', '.join(self.format_annotation(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 = (self.format_annotation(a) for a in args)
|
|
||||||
args_str = '[%s]' % ', '.join(formatted_args)
|
|
||||||
return '%s[%s, %s]' % (qualname,
|
|
||||||
args_str,
|
|
||||||
self.format_annotation(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 = [self.format_annotation(p) for p in params]
|
|
||||||
if annotation.__tuple_use_ellipsis__:
|
|
||||||
param_strings.append('...')
|
|
||||||
return '%s[%s]' % (qualname,
|
|
||||||
', '.join(param_strings))
|
|
||||||
|
|
||||||
return qualname
|
|
||||||
|
|
||||||
|
|
||||||
def getdoc(obj: Any, attrgetter: Callable = safe_getattr,
|
def getdoc(obj: Any, attrgetter: Callable = safe_getattr,
|
||||||
|
@ -8,7 +8,9 @@
|
|||||||
:license: BSD, see LICENSE for details.
|
: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 import nodes
|
||||||
from docutils.parsers.rst.states import Inliner
|
from docutils.parsers.rst.states import Inliner
|
||||||
@ -35,3 +37,153 @@ TitleGetter = Callable[[nodes.Node], str]
|
|||||||
|
|
||||||
# inventory data on memory
|
# inventory data on memory
|
||||||
Inventory = Dict[str, Dict[str, Tuple[str, str, str, str]]]
|
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
|
||||||
|
98
tests/test_util_typing.py
Normal file
98
tests/test_util_typing.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
"""
|
||||||
|
test_util_typing
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Tests util.typing functions.
|
||||||
|
|
||||||
|
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
|
||||||
|
:license: BSD, see LICENSE for details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from numbers import Integral
|
||||||
|
from typing import Any, Dict, List, TypeVar, Union, Callable, Tuple, Optional
|
||||||
|
|
||||||
|
from sphinx.util.typing import stringify
|
||||||
|
|
||||||
|
|
||||||
|
class MyClass1:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MyClass2(MyClass1):
|
||||||
|
__qualname__ = '<MyClass2>'
|
||||||
|
|
||||||
|
|
||||||
|
def test_stringify():
|
||||||
|
assert stringify(int) == "int"
|
||||||
|
assert stringify(str) == "str"
|
||||||
|
assert stringify(None) == "None"
|
||||||
|
assert stringify(Integral) == "numbers.Integral"
|
||||||
|
assert stringify(Any) == "Any"
|
||||||
|
|
||||||
|
|
||||||
|
def test_stringify_type_hints_containers():
|
||||||
|
assert stringify(List) == "List"
|
||||||
|
assert stringify(Dict) == "Dict"
|
||||||
|
assert stringify(List[int]) == "List[int]"
|
||||||
|
assert stringify(List[str]) == "List[str]"
|
||||||
|
assert stringify(Dict[str, float]) == "Dict[str, float]"
|
||||||
|
assert stringify(Tuple[str, str, str]) == "Tuple[str, str, str]"
|
||||||
|
assert stringify(Tuple[str, ...]) == "Tuple[str, ...]"
|
||||||
|
assert stringify(List[Dict[str, Tuple]]) == "List[Dict[str, Tuple]]"
|
||||||
|
|
||||||
|
|
||||||
|
def test_stringify_type_hints_string():
|
||||||
|
assert stringify("int") == "int"
|
||||||
|
assert stringify("str") == "str"
|
||||||
|
assert stringify(List["int"]) == "List[int]"
|
||||||
|
assert stringify("Tuple[str]") == "Tuple[str]"
|
||||||
|
assert stringify("unknown") == "unknown"
|
||||||
|
|
||||||
|
|
||||||
|
def test_stringify_type_hints_Callable():
|
||||||
|
assert stringify(Callable) == "Callable"
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 7):
|
||||||
|
assert stringify(Callable[[str], int]) == "Callable[[str], int]"
|
||||||
|
assert stringify(Callable[..., int]) == "Callable[[...], int]"
|
||||||
|
else:
|
||||||
|
assert stringify(Callable[[str], int]) == "Callable[str, int]"
|
||||||
|
assert stringify(Callable[..., int]) == "Callable[..., int]"
|
||||||
|
|
||||||
|
|
||||||
|
def test_stringify_type_hints_Union():
|
||||||
|
assert stringify(Optional[int]) == "Optional[int]"
|
||||||
|
assert stringify(Union[str, None]) == "Optional[str]"
|
||||||
|
assert stringify(Union[int, str]) == "Union[int, str]"
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 7):
|
||||||
|
assert stringify(Union[int, Integral]) == "Union[int, numbers.Integral]"
|
||||||
|
assert (stringify(Union[MyClass1, MyClass2]) ==
|
||||||
|
"Union[test_util_typing.MyClass1, test_util_typing.<MyClass2>]")
|
||||||
|
else:
|
||||||
|
assert stringify(Union[int, Integral]) == "numbers.Integral"
|
||||||
|
assert stringify(Union[MyClass1, MyClass2]) == "test_util_typing.MyClass1"
|
||||||
|
|
||||||
|
|
||||||
|
def test_stringify_type_hints_typevars():
|
||||||
|
T = TypeVar('T')
|
||||||
|
T_co = TypeVar('T_co', covariant=True)
|
||||||
|
T_contra = TypeVar('T_contra', contravariant=True)
|
||||||
|
|
||||||
|
assert stringify(T) == "T"
|
||||||
|
assert stringify(T_co) == "T_co"
|
||||||
|
assert stringify(T_contra) == "T_contra"
|
||||||
|
assert stringify(List[T]) == "List[T]"
|
||||||
|
|
||||||
|
|
||||||
|
def test_stringify_type_hints_custom_class():
|
||||||
|
assert stringify(MyClass1) == "test_util_typing.MyClass1"
|
||||||
|
assert stringify(MyClass2) == "test_util_typing.<MyClass2>"
|
||||||
|
|
||||||
|
|
||||||
|
def test_stringify_type_hints_alias():
|
||||||
|
MyStr = str
|
||||||
|
MyTuple = Tuple[str, str]
|
||||||
|
assert stringify(MyStr) == "str"
|
||||||
|
assert stringify(MyTuple) == "Tuple[str, str]" # type: ignore
|
Loading…
Reference in New Issue
Block a user