elif isinstance(annotation, typing.ForwardRef if sys.version_info >= (3, 7) else typing._ForwardRef):
return annotation.__forward_arg__
- # If the annotation is from the typing module, it could be a "bracketed"
- # type, in which case we want to recurse to its types as well. Otherwise
- # just get its name.
- elif hasattr(annotation, '__module__') and annotation.__module__ == 'typing':
- if sys.version_info >= (3, 7):
- name = annotation._name
- elif annotation is typing.Any:
- name = 'Any' # Any doesn't have __name__ in 3.6
- else:
- name = annotation.__name__
- if hasattr(annotation, '__args__'):
- return 'typing.{}[{}]'.format(name, ', '.join([extract_annotation(state, referrer_path, i) for i in annotation.__args__]))
+ # Generic type names -- use their name directly
+ elif isinstance(annotation, typing.TypeVar):
+ return annotation.__name__
+
+ # If the annotation is from the typing module, it ... gets complicated. It
+ # could be a "bracketed" type, in which case we want to recurse to its
+ # types as well.
+ elif (hasattr(annotation, '__module__') and annotation.__module__ == 'typing'):
+ # Optional or Union, handle those first
+ if hasattr(annotation, '__origin__') and annotation.__origin__ is typing.Union:
+ # FOR SOME REASON `annotation.__args__[1] is None` is always False
+ if len(annotation.__args__) == 2 and isinstance(None, annotation.__args__[1]):
+ name = 'typing.Optional'
+ args = annotation.__args__[:1]
+ else:
+ name = 'typing.Union'
+ args = annotation.__args__
+ elif sys.version_info >= (3, 7) and hasattr(annotation, '_name') and annotation._name:
+ name = 'typing.' + annotation._name
+ # Any doesn't have __args__
+ args = annotation.__args__ if hasattr(annotation, '__args__') else None
+ # Python 3.6 has __name__ instead of _name
+ elif sys.version_info < (3, 7) and hasattr(annotation, '__name__'):
+ name = 'typing.' + annotation.__name__
+ args = annotation.__args__
+ # Any doesn't have __name__ in 3.6
+ elif sys.version_info < (3, 7) and annotation is typing.Any:
+ name = 'typing.Any'
+ args = None
+ # Whoops, something we don't know yet. Warn and return a string
+ # representation at least.
+ else: # pragma: no cover
+ logging.warning("can't inspect annotation %s for %s, falling back to a string representation", annotation, '.'.join(referrer_path))
+ return str(annotation)
+
+ # Arguments of generic types, recurse inside
+ if args:
+ # For Callable, put the arguments into a nested list to separate
+ # them from the return type
+ if name == 'typing.Callable':
+ assert len(args) >= 1
+ return '{}[[{}], {}]'.format(name,
+ ', '.join([extract_annotation(state, referrer_path, i) for i in args[:-1]]),
+ extract_annotation(state, referrer_path, args[-1]))
+ else:
+ return '{}[{}]'.format(name, ', '.join([extract_annotation(state, referrer_path, i) for i in args]))
else:
- return 'typing.' + name
+ return name
# Things like (float, int) instead of Tuple[float, int] or using np.array
# instead of np.ndarray. Ignore with a warning.
third: str = 'hello') -> <a href="inspect_annotations.Foo.html" class="m-doc">Foo</a></span>
</dt>
<dd>An annotated function</dd>
+ <dt>
+ <span class="m-doc-wrap-bumper">def <a href="#annotation_any" class="m-doc-self" id="annotation_any">annotation_any</a>(</span><span class="m-doc-wrap">a: typing.Any)</span>
+ </dt>
+ <dd>Annotation with the Any type</dd>
+ <dt>
+ <span class="m-doc-wrap-bumper">def <a href="#annotation_callable" class="m-doc-self" id="annotation_callable">annotation_callable</a>(</span><span class="m-doc-wrap">a: typing.Callable[[float, int], str])</span>
+ </dt>
+ <dd>Annotation with the Callable type</dd>
+ <dt>
+ <span class="m-doc-wrap-bumper">def <a href="#annotation_callable_no_args" class="m-doc-self" id="annotation_callable_no_args">annotation_callable_no_args</a>(</span><span class="m-doc-wrap">a: typing.Callable[[], typing.Dict[int, float]])</span>
+ </dt>
+ <dd>Annotation with the Callable type w/o arguments</dd>
<dt>
<span class="m-doc-wrap-bumper">def <a href="#annotation_func_instead_of_type" class="m-doc-self" id="annotation_func_instead_of_type">annotation_func_instead_of_type</a>(</span><span class="m-doc-wrap">a)</span>
</dt>
<dd>Annotation with a function instead of a type, ignored</dd>
+ <dt>
+ <span class="m-doc-wrap-bumper">def <a href="#annotation_generic" class="m-doc-self" id="annotation_generic">annotation_generic</a>(</span><span class="m-doc-wrap">a: typing.List[Tp]) -> Tp</span>
+ </dt>
+ <dd>Annotation with a generic type</dd>
+ <dt>
+ <span class="m-doc-wrap-bumper">def <a href="#annotation_list_noparam" class="m-doc-self" id="annotation_list_noparam">annotation_list_noparam</a>(</span><span class="m-doc-wrap">a: typing.List[T])</span>
+ </dt>
+ <dd>Annotation with the unparametrized List type. 3.7 adds an implicit TypeVar to it, 3.6 not, emulate that to make the test pass on older versions</dd>
+ <dt>
+ <span class="m-doc-wrap-bumper">def <a href="#annotation_optional" class="m-doc-self" id="annotation_optional">annotation_optional</a>(</span><span class="m-doc-wrap">a: typing.Optional[float])</span>
+ </dt>
+ <dd>Annotation with the Optional type</dd>
<dt>
<span class="m-doc-wrap-bumper">def <a href="#annotation_tuple_instead_of_tuple" class="m-doc-self" id="annotation_tuple_instead_of_tuple">annotation_tuple_instead_of_tuple</a>(</span><span class="m-doc-wrap">a)</span>
</dt>
<dd>Annotation with a tuple instead of Tuple, ignored</dd>
+ <dt>
+ <span class="m-doc-wrap-bumper">def <a href="#annotation_union" class="m-doc-self" id="annotation_union">annotation_union</a>(</span><span class="m-doc-wrap">a: typing.Union[float, int])</span>
+ </dt>
+ <dd>Annotation with the Union type</dd>
<dt>
<span class="m-doc-wrap-bumper">def <a href="#args_kwargs" class="m-doc-self" id="args_kwargs">args_kwargs</a>(</span><span class="m-doc-wrap">a, b, *args, **kwargs)</span>
</dt>
-from typing import List, Tuple
+import sys
+
+from typing import List, Tuple, Dict, Any, Union, Optional, Callable, TypeVar
class Foo:
"""A class with properties"""
def annotation_func_instead_of_type(a: open):
"""Annotation with a function instead of a type, ignored"""
+def annotation_any(a: Any):
+ """Annotation with the Any type"""
+
+def annotation_union(a: Union[float, int]):
+ """Annotation with the Union type"""
+
+def annotation_list_noparam(a: List):
+ """Annotation with the unparametrized List type. 3.7 adds an implicit TypeVar to it, 3.6 not, emulate that to make the test pass on older versions"""
+if sys.version_info < (3, 7):
+ annotation_list_noparam.__annotations__['a'] = List[TypeVar('T')]
+
+_T = TypeVar('Tp')
+
+def annotation_generic(a: List[_T]) -> _T:
+ """Annotation with a generic type"""
+
+def annotation_optional(a: Optional[float]):
+ """Annotation with the Optional type"""
+
+def annotation_callable(a: Callable[[float, int], str]):
+ """Annotation with the Callable type"""
+
+def annotation_callable_no_args(a: Callable[[], Dict[int, float]]):
+ """Annotation with the Callable type w/o arguments"""
+
# Only possible with native code now, https://www.python.org/dev/peps/pep-0570/
#def positionals_only(positional_only, /, positional_kw):
#"""Function with explicitly delimited positional args"""