Fix #9194: autodoc: Prepend the "typing" module name on the signature

To create hyperlinks to container types automatically, this prepends the
module names for the types under "typing" module.
This commit is contained in:
Takeshi KOMIYA 2021-12-24 10:50:04 +09:00
parent 0a5783f75b
commit bdbad40f57
8 changed files with 90 additions and 51 deletions

View File

@ -774,7 +774,7 @@ def stringify_signature(sig: inspect.Signature, show_annotation: bool = True,
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(stringify_annotation(param.annotation, unqualified_typehints)) arg.write(stringify_annotation(param.annotation, unqualified_typehints, True))
if param.default is not param.empty: if param.default is not param.empty:
if show_annotation and param.annotation is not param.empty: if show_annotation and param.annotation is not param.empty:
arg.write(' = ') arg.write(' = ')
@ -794,7 +794,7 @@ def stringify_signature(sig: inspect.Signature, show_annotation: bool = True,
show_return_annotation is False): show_return_annotation is False):
return '(%s)' % ', '.join(args) return '(%s)' % ', '.join(args)
else: else:
annotation = stringify_annotation(sig.return_annotation, unqualified_typehints) annotation = stringify_annotation(sig.return_annotation, unqualified_typehints, True)
return '(%s) -> %s' % (', '.join(args), annotation) return '(%s) -> %s' % (', '.join(args), annotation)

View File

@ -299,11 +299,12 @@ def _restify_py36(cls: Optional[Type]) -> str:
return ':py:obj:`%s.%s`' % (cls.__module__, qualname) return ':py:obj:`%s.%s`' % (cls.__module__, qualname)
def stringify(annotation: Any, smartref: bool = False) -> str: def stringify(annotation: Any, smartref: bool = False, show_typing: bool = False) -> str:
"""Stringify type annotation object. """Stringify type annotation object.
:param smartref: If true, add "~" prefix to the result to remove the leading :param smartref: If true, add "~" prefix to the result to remove the leading
module and class names from the reference text module and class names from the reference text
:param show_typing: If true, do not suppress the "typing" module name
""" """
from sphinx.util import inspect # lazy loading from sphinx.util import inspect # lazy loading
@ -319,7 +320,7 @@ def stringify(annotation: Any, smartref: bool = False) -> str:
else: else:
return annotation return annotation
elif isinstance(annotation, TypeVar): elif isinstance(annotation, TypeVar):
if annotation.__module__ == 'typing': if show_typing is False and annotation.__module__ == 'typing':
return annotation.__name__ return annotation.__name__
else: else:
return prefix + '.'.join([annotation.__module__, annotation.__name__]) return prefix + '.'.join([annotation.__module__, annotation.__name__])
@ -347,12 +348,12 @@ def stringify(annotation: Any, smartref: bool = False) -> str:
return '...' return '...'
if sys.version_info >= (3, 7): # py37+ if sys.version_info >= (3, 7): # py37+
return _stringify_py37(annotation, smartref) return _stringify_py37(annotation, smartref, show_typing)
else: else:
return _stringify_py36(annotation, smartref) return _stringify_py36(annotation, smartref, show_typing)
def _stringify_py37(annotation: Any, smartref: bool = False) -> str: def _stringify_py37(annotation: Any, smartref: bool = False, show_typing: bool = False) -> str:
"""stringify() for py37+.""" """stringify() for py37+."""
module = getattr(annotation, '__module__', None) module = getattr(annotation, '__module__', None)
modprefix = '' modprefix = ''
@ -364,10 +365,12 @@ def _stringify_py37(annotation: Any, smartref: bool = False) -> str:
elif getattr(annotation, '__qualname__', None): elif getattr(annotation, '__qualname__', None):
qualname = annotation.__qualname__ qualname = annotation.__qualname__
else: else:
qualname = stringify(annotation.__origin__) # ex. Union qualname = stringify(annotation.__origin__).replace('typing.', '') # ex. Union
if smartref: if smartref:
modprefix = '~%s.' % module modprefix = '~%s.' % module
elif show_typing:
modprefix = '%s.' % module
elif hasattr(annotation, '__qualname__'): elif hasattr(annotation, '__qualname__'):
if smartref: if smartref:
modprefix = '~%s.' % module modprefix = '~%s.' % module
@ -376,7 +379,7 @@ def _stringify_py37(annotation: Any, smartref: bool = False) -> str:
qualname = annotation.__qualname__ qualname = annotation.__qualname__
elif hasattr(annotation, '__origin__'): elif hasattr(annotation, '__origin__'):
# instantiated generic provided by a user # instantiated generic provided by a user
qualname = stringify(annotation.__origin__, smartref) qualname = stringify(annotation.__origin__, smartref, show_typing)
elif UnionType and isinstance(annotation, UnionType): # types.Union (for py3.10+) elif UnionType and isinstance(annotation, UnionType): # types.Union (for py3.10+)
qualname = 'types.Union' qualname = 'types.Union'
else: else:
@ -391,13 +394,15 @@ def _stringify_py37(annotation: Any, smartref: bool = False) -> str:
elif qualname in ('Optional', 'Union'): elif qualname in ('Optional', 'Union'):
if len(annotation.__args__) > 1 and annotation.__args__[-1] is NoneType: if len(annotation.__args__) > 1 and annotation.__args__[-1] is NoneType:
if len(annotation.__args__) > 2: if len(annotation.__args__) > 2:
args = ', '.join(stringify(a, smartref) for a in annotation.__args__[:-1]) args = ', '.join(stringify(a, smartref, show_typing) for a
in annotation.__args__[:-1])
return '%sOptional[%sUnion[%s]]' % (modprefix, modprefix, args) return '%sOptional[%sUnion[%s]]' % (modprefix, modprefix, args)
else: else:
return '%sOptional[%s]' % (modprefix, return '%sOptional[%s]' % (modprefix, stringify(annotation.__args__[0],
stringify(annotation.__args__[0], smartref)) smartref, show_typing))
else: else:
args = ', '.join(stringify(a, smartref) for a in annotation.__args__) args = ', '.join(stringify(a, smartref, show_typing) for a
in annotation.__args__)
return '%sUnion[%s]' % (modprefix, args) return '%sUnion[%s]' % (modprefix, args)
elif qualname == 'types.Union': elif qualname == 'types.Union':
if len(annotation.__args__) > 1 and None in annotation.__args__: if len(annotation.__args__) > 1 and None in annotation.__args__:
@ -406,25 +411,27 @@ def _stringify_py37(annotation: Any, smartref: bool = False) -> str:
else: else:
return ' | '.join(stringify(a) for a in annotation.__args__) return ' | '.join(stringify(a) for a in annotation.__args__)
elif qualname == 'Callable': elif qualname == 'Callable':
args = ', '.join(stringify(a, smartref) for a in annotation.__args__[:-1]) args = ', '.join(stringify(a, smartref, show_typing) for a
returns = stringify(annotation.__args__[-1], smartref) in annotation.__args__[:-1])
returns = stringify(annotation.__args__[-1], smartref, show_typing)
return '%s%s[[%s], %s]' % (modprefix, qualname, args, returns) return '%s%s[[%s], %s]' % (modprefix, qualname, args, returns)
elif qualname == 'Literal': elif qualname == 'Literal':
args = ', '.join(repr(a) for a in annotation.__args__) args = ', '.join(repr(a) for a in annotation.__args__)
return '%s%s[%s]' % (modprefix, qualname, args) return '%s%s[%s]' % (modprefix, qualname, args)
elif str(annotation).startswith('typing.Annotated'): # for py39+ elif str(annotation).startswith('typing.Annotated'): # for py39+
return stringify(annotation.__args__[0], smartref) return stringify(annotation.__args__[0], smartref, show_typing)
elif all(is_system_TypeVar(a) for a in annotation.__args__): elif all(is_system_TypeVar(a) for a in annotation.__args__):
# Suppress arguments if all system defined TypeVars (ex. Dict[KT, VT]) # Suppress arguments if all system defined TypeVars (ex. Dict[KT, VT])
return modprefix + qualname return modprefix + qualname
else: else:
args = ', '.join(stringify(a, smartref) for a in annotation.__args__) args = ', '.join(stringify(a, smartref, show_typing) for a
in annotation.__args__)
return '%s%s[%s]' % (modprefix, qualname, args) return '%s%s[%s]' % (modprefix, qualname, args)
return modprefix + qualname return modprefix + qualname
def _stringify_py36(annotation: Any, smartref: bool = False) -> str: def _stringify_py36(annotation: Any, smartref: bool = False, show_typing: bool = False) -> str:
"""stringify() for py36.""" """stringify() for py36."""
module = getattr(annotation, '__module__', None) module = getattr(annotation, '__module__', None)
modprefix = '' modprefix = ''
@ -442,6 +449,8 @@ def _stringify_py36(annotation: Any, smartref: bool = False) -> str:
if smartref: if smartref:
modprefix = '~%s.' % module modprefix = '~%s.' % module
elif show_typing:
modprefix = '%s.' % module
elif hasattr(annotation, '__qualname__'): elif hasattr(annotation, '__qualname__'):
if smartref: if smartref:
modprefix = '~%s.' % module modprefix = '~%s.' % module
@ -455,7 +464,7 @@ def _stringify_py36(annotation: Any, smartref: bool = False) -> str:
not hasattr(annotation, '__tuple_params__')): # for Python 3.6 not hasattr(annotation, '__tuple_params__')): # for Python 3.6
params = annotation.__args__ params = annotation.__args__
if params: if params:
param_str = ', '.join(stringify(p, smartref) for p in params) param_str = ', '.join(stringify(p, smartref, show_typing) for p in params)
return '%s%s[%s]' % (modprefix, qualname, param_str) return '%s%s[%s]' % (modprefix, qualname, param_str)
else: else:
return modprefix + qualname return modprefix + qualname
@ -466,12 +475,12 @@ def _stringify_py36(annotation: Any, smartref: bool = False) -> str:
elif annotation.__origin__ == Generator: # type: ignore elif annotation.__origin__ == Generator: # type: ignore
params = annotation.__args__ # type: ignore params = annotation.__args__ # type: ignore
else: # typing.Callable else: # typing.Callable
args = ', '.join(stringify(arg, smartref) for arg args = ', '.join(stringify(arg, smartref, show_typing) for arg
in annotation.__args__[:-1]) # type: ignore in annotation.__args__[:-1]) # type: ignore
result = stringify(annotation.__args__[-1]) # type: ignore result = stringify(annotation.__args__[-1]) # type: ignore
return '%s%s[[%s], %s]' % (modprefix, qualname, args, result) return '%s%s[[%s], %s]' % (modprefix, qualname, args, result)
if params is not None: if params is not None:
param_str = ', '.join(stringify(p, smartref) for p in params) param_str = ', '.join(stringify(p, smartref, show_typing) for p in params)
return '%s%s[%s]' % (modprefix, qualname, param_str) return '%s%s[%s]' % (modprefix, qualname, param_str)
elif (hasattr(annotation, '__origin__') and elif (hasattr(annotation, '__origin__') and
annotation.__origin__ is typing.Union): annotation.__origin__ is typing.Union):
@ -479,12 +488,13 @@ def _stringify_py36(annotation: Any, smartref: bool = False) -> str:
if params is not None: if params is not None:
if len(params) > 1 and params[-1] is NoneType: if len(params) > 1 and params[-1] is NoneType:
if len(params) > 2: if len(params) > 2:
param_str = ", ".join(stringify(p, smartref) for p in params[:-1]) param_str = ", ".join(stringify(p, smartref, show_typing) for p
in params[:-1])
return '%sOptional[%sUnion[%s]]' % (modprefix, modprefix, param_str) return '%sOptional[%sUnion[%s]]' % (modprefix, modprefix, param_str)
else: else:
return '%sOptional[%s]' % (modprefix, stringify(params[0])) return '%sOptional[%s]' % (modprefix, stringify(params[0]))
else: else:
param_str = ', '.join(stringify(p, smartref) for p in params) param_str = ', '.join(stringify(p, smartref, show_typing) for p in params)
return '%sUnion[%s]' % (modprefix, param_str) return '%sUnion[%s]' % (modprefix, param_str)
return modprefix + qualname return modprefix + qualname

View File

@ -162,7 +162,7 @@ def test_wrapped_function_contextmanager(app):
actual = do_autodoc(app, 'function', 'target.wrappedfunction.feeling_good') actual = do_autodoc(app, 'function', 'target.wrappedfunction.feeling_good')
assert list(actual) == [ assert list(actual) == [
'', '',
'.. py:function:: feeling_good(x: int, y: int) -> Generator', '.. py:function:: feeling_good(x: int, y: int) -> typing.Generator',
' :module: target.wrappedfunction', ' :module: target.wrappedfunction',
'', '',
" You'll feel better in this context!", " You'll feel better in this context!",

View File

@ -130,4 +130,4 @@ def test_subclass_of_mocked_object(app):
options = {'members': None} options = {'members': None}
actual = do_autodoc(app, 'module', 'target.need_mocks', options) actual = do_autodoc(app, 'module', 'target.need_mocks', options)
assert '.. py:class:: Inherited(*args: Any, **kwargs: Any)' in actual assert '.. py:class:: Inherited(*args: typing.Any, **kwargs: typing.Any)' in actual

View File

@ -612,7 +612,7 @@ def test_autodoc_typehints_signature(app):
' :type: int', ' :type: int',
'', '',
'', '',
'.. py:class:: Math(s: str, o: Optional[Any] = None)', '.. py:class:: Math(s: str, o: typing.Optional[typing.Any] = None)',
' :module: target.typehints', ' :module: target.typehints',
'', '',
'', '',
@ -677,7 +677,8 @@ def test_autodoc_typehints_signature(app):
' :module: target.typehints', ' :module: target.typehints',
'', '',
'', '',
'.. py:function:: tuple_args(x: Tuple[int, Union[int, str]]) -> Tuple[int, int]', '.. py:function:: tuple_args(x: typing.Tuple[int, typing.Union[int, str]]) '
'-> typing.Tuple[int, int]',
' :module: target.typehints', ' :module: target.typehints',
'', '',
] ]

View File

@ -36,15 +36,15 @@ def test_preserve_defaults(app):
' docstring', ' docstring',
'', '',
'', '',
' .. py:method:: Class.meth(name: str = CONSTANT, sentinel: Any = SENTINEL, ' ' .. py:method:: Class.meth(name: str = CONSTANT, sentinel: typing.Any = '
'now: datetime.datetime = datetime.now(), color: int = %s) -> None' % color, 'SENTINEL, now: datetime.datetime = datetime.now(), color: int = %s) -> None' % color,
' :module: target.preserve_defaults', ' :module: target.preserve_defaults',
'', '',
' docstring', ' docstring',
'', '',
'', '',
'.. py:function:: foo(name: str = CONSTANT, sentinel: Any = SENTINEL, now: ' '.. py:function:: foo(name: str = CONSTANT, sentinel: typing.Any = SENTINEL, '
'datetime.datetime = datetime.now(), color: int = %s) -> None' % color, 'now: datetime.datetime = datetime.now(), color: int = %s) -> None' % color,
' :module: target.preserve_defaults', ' :module: target.preserve_defaults',
'', '',
' docstring', ' docstring',

View File

@ -157,21 +157,22 @@ def test_signature_annotations():
# Generic types with concrete parameters # Generic types with concrete parameters
sig = inspect.signature(f1) sig = inspect.signature(f1)
assert stringify_signature(sig) == '(x: List[int]) -> List[int]' assert stringify_signature(sig) == '(x: typing.List[int]) -> typing.List[int]'
# TypeVars and generic types with TypeVars # TypeVars and generic types with TypeVars
sig = inspect.signature(f2) sig = inspect.signature(f2)
if sys.version_info < (3, 7): if sys.version_info < (3, 7):
assert stringify_signature(sig) == '(x: List[T], y: List[T_co], z: T) -> List[T_contra]' assert stringify_signature(sig) == ('(x: typing.List[T], y: typing.List[T_co], z: T) '
'-> typing.List[T_contra]')
else: else:
assert stringify_signature(sig) == ('(x: List[tests.typing_test_data.T],' assert stringify_signature(sig) == ('(x: typing.List[tests.typing_test_data.T],'
' y: List[tests.typing_test_data.T_co],' ' y: typing.List[tests.typing_test_data.T_co],'
' z: tests.typing_test_data.T' ' z: tests.typing_test_data.T'
') -> List[tests.typing_test_data.T_contra]') ') -> typing.List[tests.typing_test_data.T_contra]')
# Union types # Union types
sig = inspect.signature(f3) sig = inspect.signature(f3)
assert stringify_signature(sig) == '(x: Union[str, numbers.Integral]) -> None' assert stringify_signature(sig) == '(x: typing.Union[str, numbers.Integral]) -> None'
# Quoted annotations # Quoted annotations
sig = inspect.signature(f4) sig = inspect.signature(f4)
@ -187,18 +188,18 @@ def test_signature_annotations():
# Space around '=' for defaults # Space around '=' for defaults
sig = inspect.signature(f7) sig = inspect.signature(f7)
assert stringify_signature(sig) == '(x: Optional[int] = None, y: dict = {}) -> None' assert stringify_signature(sig) == '(x: typing.Optional[int] = None, y: dict = {}) -> None'
# Callable types # Callable types
sig = inspect.signature(f8) sig = inspect.signature(f8)
assert stringify_signature(sig) == '(x: Callable[[int, str], int]) -> None' assert stringify_signature(sig) == '(x: typing.Callable[[int, str], int]) -> None'
sig = inspect.signature(f9) sig = inspect.signature(f9)
assert stringify_signature(sig) == '(x: Callable) -> None' assert stringify_signature(sig) == '(x: typing.Callable) -> None'
# Tuple types # Tuple types
sig = inspect.signature(f10) sig = inspect.signature(f10)
assert stringify_signature(sig) == '(x: Tuple[int, str], y: Tuple[int, ...]) -> None' assert stringify_signature(sig) == '(x: typing.Tuple[int, str], y: typing.Tuple[int, ...]) -> None'
# Instance annotations # Instance annotations
sig = inspect.signature(f11) sig = inspect.signature(f11)
@ -206,24 +207,24 @@ def test_signature_annotations():
# tuple with more than two items # tuple with more than two items
sig = inspect.signature(f12) sig = inspect.signature(f12)
assert stringify_signature(sig) == '() -> Tuple[int, str, int]' assert stringify_signature(sig) == '() -> typing.Tuple[int, str, int]'
# optional # optional
sig = inspect.signature(f13) sig = inspect.signature(f13)
assert stringify_signature(sig) == '() -> Optional[str]' assert stringify_signature(sig) == '() -> typing.Optional[str]'
# optional union # optional union
sig = inspect.signature(f20) sig = inspect.signature(f20)
assert stringify_signature(sig) in ('() -> Optional[Union[int, str]]', assert stringify_signature(sig) in ('() -> typing.Optional[typing.Union[int, str]]',
'() -> Optional[Union[str, int]]') '() -> typing.Optional[typing.Union[str, int]]')
# Any # Any
sig = inspect.signature(f14) sig = inspect.signature(f14)
assert stringify_signature(sig) == '() -> Any' assert stringify_signature(sig) == '() -> typing.Any'
# ForwardRef # ForwardRef
sig = inspect.signature(f15) sig = inspect.signature(f15)
assert stringify_signature(sig) == '(x: Unknown, y: int) -> Any' assert stringify_signature(sig) == '(x: Unknown, y: int) -> typing.Any'
# keyword only arguments (1) # keyword only arguments (1)
sig = inspect.signature(f16) sig = inspect.signature(f16)
@ -234,7 +235,8 @@ def test_signature_annotations():
assert stringify_signature(sig) == '(*, arg3, arg4)' assert stringify_signature(sig) == '(*, arg3, arg4)'
sig = inspect.signature(f18) sig = inspect.signature(f18)
assert stringify_signature(sig) == '(self, arg1: Union[int, Tuple] = 10) -> List[Dict]' assert stringify_signature(sig) == ('(self, arg1: typing.Union[int, typing.Tuple] = 10) -> '
'typing.List[typing.Dict]')
# annotations for variadic and keyword parameters # annotations for variadic and keyword parameters
sig = inspect.signature(f19) sig = inspect.signature(f19)
@ -246,10 +248,10 @@ def test_signature_annotations():
# type hints by string # type hints by string
sig = inspect.signature(Node.children) sig = inspect.signature(Node.children)
assert stringify_signature(sig) == '(self) -> List[tests.typing_test_data.Node]' assert stringify_signature(sig) == '(self) -> typing.List[tests.typing_test_data.Node]'
sig = inspect.signature(Node.__init__) sig = inspect.signature(Node.__init__)
assert stringify_signature(sig) == '(self, parent: Optional[tests.typing_test_data.Node]) -> None' assert stringify_signature(sig) == '(self, parent: typing.Optional[tests.typing_test_data.Node]) -> None'
# show_annotation is False # show_annotation is False
sig = inspect.signature(f7) sig = inspect.signature(f7)
@ -257,7 +259,7 @@ def test_signature_annotations():
# show_return_annotation is False # show_return_annotation is False
sig = inspect.signature(f7) sig = inspect.signature(f7)
assert stringify_signature(sig, show_return_annotation=False) == '(x: Optional[int] = None, y: dict = {})' assert stringify_signature(sig, show_return_annotation=False) == '(x: typing.Optional[int] = None, y: dict = {})'
# unqualified_typehints is True # unqualified_typehints is True
sig = inspect.signature(f7) sig = inspect.signature(f7)

View File

@ -197,41 +197,53 @@ def test_stringify():
assert stringify(TracebackType, True) == "~types.TracebackType" assert stringify(TracebackType, True) == "~types.TracebackType"
assert stringify(Any, False) == "Any" assert stringify(Any, False) == "Any"
assert stringify(Any, False, True) == "typing.Any"
assert stringify(Any, True) == "~typing.Any" assert stringify(Any, True) == "~typing.Any"
def test_stringify_type_hints_containers(): def test_stringify_type_hints_containers():
assert stringify(List, False) == "List" assert stringify(List, False) == "List"
assert stringify(List, False, True) == "typing.List"
assert stringify(List, True) == "~typing.List" assert stringify(List, True) == "~typing.List"
assert stringify(Dict, False) == "Dict" assert stringify(Dict, False) == "Dict"
assert stringify(Dict, False, True) == "typing.Dict"
assert stringify(Dict, True) == "~typing.Dict" assert stringify(Dict, True) == "~typing.Dict"
assert stringify(List[int], False) == "List[int]" assert stringify(List[int], False) == "List[int]"
assert stringify(List[int], False, True) == "typing.List[int]"
assert stringify(List[int], True) == "~typing.List[int]" assert stringify(List[int], True) == "~typing.List[int]"
assert stringify(List[str], False) == "List[str]" assert stringify(List[str], False) == "List[str]"
assert stringify(List[str], False, True) == "typing.List[str]"
assert stringify(List[str], True) == "~typing.List[str]" assert stringify(List[str], True) == "~typing.List[str]"
assert stringify(Dict[str, float], False) == "Dict[str, float]" assert stringify(Dict[str, float], False) == "Dict[str, float]"
assert stringify(Dict[str, float], False, True) == "typing.Dict[str, float]"
assert stringify(Dict[str, float], True) == "~typing.Dict[str, float]" assert stringify(Dict[str, float], True) == "~typing.Dict[str, float]"
assert stringify(Tuple[str, str, str], False) == "Tuple[str, str, str]" assert stringify(Tuple[str, str, str], False) == "Tuple[str, str, str]"
assert stringify(Tuple[str, str, str], False, True) == "typing.Tuple[str, str, str]"
assert stringify(Tuple[str, str, str], True) == "~typing.Tuple[str, str, str]" assert stringify(Tuple[str, str, str], True) == "~typing.Tuple[str, str, str]"
assert stringify(Tuple[str, ...], False) == "Tuple[str, ...]" assert stringify(Tuple[str, ...], False) == "Tuple[str, ...]"
assert stringify(Tuple[str, ...], False, True) == "typing.Tuple[str, ...]"
assert stringify(Tuple[str, ...], True) == "~typing.Tuple[str, ...]" assert stringify(Tuple[str, ...], True) == "~typing.Tuple[str, ...]"
assert stringify(Tuple[()], False) == "Tuple[()]" assert stringify(Tuple[()], False) == "Tuple[()]"
assert stringify(Tuple[()], False, True) == "typing.Tuple[()]"
assert stringify(Tuple[()], True) == "~typing.Tuple[()]" assert stringify(Tuple[()], True) == "~typing.Tuple[()]"
assert stringify(List[Dict[str, Tuple]], False) == "List[Dict[str, Tuple]]" assert stringify(List[Dict[str, Tuple]], False) == "List[Dict[str, Tuple]]"
assert stringify(List[Dict[str, Tuple]], False, True) == "typing.List[typing.Dict[str, typing.Tuple]]"
assert stringify(List[Dict[str, Tuple]], True) == "~typing.List[~typing.Dict[str, ~typing.Tuple]]" assert stringify(List[Dict[str, Tuple]], True) == "~typing.List[~typing.Dict[str, ~typing.Tuple]]"
assert stringify(MyList[Tuple[int, int]], False) == "tests.test_util_typing.MyList[Tuple[int, int]]" assert stringify(MyList[Tuple[int, int]], False) == "tests.test_util_typing.MyList[Tuple[int, int]]"
assert stringify(MyList[Tuple[int, int]], False, True) == "tests.test_util_typing.MyList[typing.Tuple[int, int]]"
assert stringify(MyList[Tuple[int, int]], True) == "~tests.test_util_typing.MyList[~typing.Tuple[int, int]]" assert stringify(MyList[Tuple[int, int]], True) == "~tests.test_util_typing.MyList[~typing.Tuple[int, int]]"
assert stringify(Generator[None, None, None], False) == "Generator[None, None, None]" assert stringify(Generator[None, None, None], False) == "Generator[None, None, None]"
assert stringify(Generator[None, None, None], False, True) == "typing.Generator[None, None, None]"
assert stringify(Generator[None, None, None], True) == "~typing.Generator[None, None, None]" assert stringify(Generator[None, None, None], True) == "~typing.Generator[None, None, None]"
@ -288,45 +300,58 @@ def test_stringify_type_hints_string():
def test_stringify_type_hints_Callable(): def test_stringify_type_hints_Callable():
assert stringify(Callable, False) == "Callable" assert stringify(Callable, False) == "Callable"
assert stringify(Callable, False, True) == "typing.Callable"
assert stringify(Callable, True) == "~typing.Callable" assert stringify(Callable, True) == "~typing.Callable"
if sys.version_info >= (3, 7): if sys.version_info >= (3, 7):
assert stringify(Callable[[str], int], False) == "Callable[[str], int]" assert stringify(Callable[[str], int], False) == "Callable[[str], int]"
assert stringify(Callable[[str], int], False, True) == "typing.Callable[[str], int]"
assert stringify(Callable[[str], int], True) == "~typing.Callable[[str], int]" assert stringify(Callable[[str], int], True) == "~typing.Callable[[str], int]"
assert stringify(Callable[..., int], False) == "Callable[[...], int]" assert stringify(Callable[..., int], False) == "Callable[[...], int]"
assert stringify(Callable[..., int], False, True) == "typing.Callable[[...], int]"
assert stringify(Callable[..., int], True) == "~typing.Callable[[...], int]" assert stringify(Callable[..., int], True) == "~typing.Callable[[...], int]"
else: else:
assert stringify(Callable[[str], int], False) == "Callable[str, int]" assert stringify(Callable[[str], int], False) == "Callable[str, int]"
assert stringify(Callable[[str], int], False, True) == "typing.Callable[str, int]"
assert stringify(Callable[[str], int], True) == "~typing.Callable[str, int]" assert stringify(Callable[[str], int], True) == "~typing.Callable[str, int]"
assert stringify(Callable[..., int], False) == "Callable[..., int]" assert stringify(Callable[..., int], False) == "Callable[..., int]"
assert stringify(Callable[..., int], False, True) == "typing.Callable[..., int]"
assert stringify(Callable[..., int], True) == "~typing.Callable[..., int]" assert stringify(Callable[..., int], True) == "~typing.Callable[..., int]"
def test_stringify_type_hints_Union(): def test_stringify_type_hints_Union():
assert stringify(Optional[int], False) == "Optional[int]" assert stringify(Optional[int], False) == "Optional[int]"
assert stringify(Optional[int], False, True) == "typing.Optional[int]"
assert stringify(Optional[int], True) == "~typing.Optional[int]" assert stringify(Optional[int], True) == "~typing.Optional[int]"
assert stringify(Union[str, None], False) == "Optional[str]" assert stringify(Union[str, None], False) == "Optional[str]"
assert stringify(Union[str, None], False, True) == "typing.Optional[str]"
assert stringify(Union[str, None], True) == "~typing.Optional[str]" assert stringify(Union[str, None], True) == "~typing.Optional[str]"
assert stringify(Union[int, str], False) == "Union[int, str]" assert stringify(Union[int, str], False) == "Union[int, str]"
assert stringify(Union[int, str], False, True) == "typing.Union[int, str]"
assert stringify(Union[int, str], True) == "~typing.Union[int, str]" assert stringify(Union[int, str], True) == "~typing.Union[int, str]"
if sys.version_info >= (3, 7): if sys.version_info >= (3, 7):
assert stringify(Union[int, Integral], False) == "Union[int, numbers.Integral]" assert stringify(Union[int, Integral], False) == "Union[int, numbers.Integral]"
assert stringify(Union[int, Integral], False, True) == "typing.Union[int, numbers.Integral]"
assert stringify(Union[int, Integral], True) == "~typing.Union[int, ~numbers.Integral]" assert stringify(Union[int, Integral], True) == "~typing.Union[int, ~numbers.Integral]"
assert (stringify(Union[MyClass1, MyClass2], False) == assert (stringify(Union[MyClass1, MyClass2], False) ==
"Union[tests.test_util_typing.MyClass1, tests.test_util_typing.<MyClass2>]") "Union[tests.test_util_typing.MyClass1, tests.test_util_typing.<MyClass2>]")
assert (stringify(Union[MyClass1, MyClass2], False, True) ==
"typing.Union[tests.test_util_typing.MyClass1, tests.test_util_typing.<MyClass2>]")
assert (stringify(Union[MyClass1, MyClass2], True) == assert (stringify(Union[MyClass1, MyClass2], True) ==
"~typing.Union[~tests.test_util_typing.MyClass1, ~tests.test_util_typing.<MyClass2>]") "~typing.Union[~tests.test_util_typing.MyClass1, ~tests.test_util_typing.<MyClass2>]")
else: else:
assert stringify(Union[int, Integral], False) == "numbers.Integral" assert stringify(Union[int, Integral], False) == "numbers.Integral"
assert stringify(Union[int, Integral], False, True) == "numbers.Integral"
assert stringify(Union[int, Integral], True) == "~numbers.Integral" assert stringify(Union[int, Integral], True) == "~numbers.Integral"
assert stringify(Union[MyClass1, MyClass2], False) == "tests.test_util_typing.MyClass1" assert stringify(Union[MyClass1, MyClass2], False) == "tests.test_util_typing.MyClass1"
assert stringify(Union[MyClass1, MyClass2], False, True) == "tests.test_util_typing.MyClass1"
assert stringify(Union[MyClass1, MyClass2], True) == "~tests.test_util_typing.MyClass1" assert stringify(Union[MyClass1, MyClass2], True) == "~tests.test_util_typing.MyClass1"
@ -391,6 +416,7 @@ def test_stringify_type_hints_alias():
def test_stringify_type_Literal(): def test_stringify_type_Literal():
from typing import Literal # type: ignore from typing import Literal # type: ignore
assert stringify(Literal[1, "2", "\r"], False) == "Literal[1, '2', '\\r']" assert stringify(Literal[1, "2", "\r"], False) == "Literal[1, '2', '\\r']"
assert stringify(Literal[1, "2", "\r"], False, True) == "typing.Literal[1, '2', '\\r']"
assert stringify(Literal[1, "2", "\r"], True) == "~typing.Literal[1, '2', '\\r']" assert stringify(Literal[1, "2", "\r"], True) == "~typing.Literal[1, '2', '\\r']"