diff options
author | Claudiu Popa <pcmanticore@gmail.com> | 2019-05-20 09:36:51 +0200 |
---|---|---|
committer | Claudiu Popa <pcmanticore@gmail.com> | 2019-05-20 09:36:51 +0200 |
commit | 07d5e37b9d2bded9eb98e47dacd1feff6af0188a (patch) | |
tree | 7eb5bd616a30cc200bef807658956126231c027a | |
parent | 50a670b1eee2a28c771daceeb169a0db4805ddae (diff) | |
download | pylint-git-07d5e37b9d2bded9eb98e47dacd1feff6af0188a.tar.gz |
Support fully qualified typing imports for type annotations.
Close #2915
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | pylint/checkers/variables.py | 97 | ||||
-rw-r--r-- | pylint/test/functional/unused_typing_imports.py | 19 | ||||
-rw-r--r-- | pylint/test/functional/unused_typing_imports.rc | 1 |
4 files changed, 108 insertions, 13 deletions
@@ -7,6 +7,10 @@ What's New in Pylint 2.4.0? Release date: TBA +* Support fully qualified typing imports for type annotations. + + Close #2915 + * Exclude ``__dict__`` from ``attribute-defined-outside-init`` * Fix pointer on spelling check when the error are more than one time in the same line. diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 345aa76de..26a7966ef 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -62,6 +62,63 @@ PY3K = sys.version_info >= (3, 0) METACLASS_NAME_TRANSFORMS = {"_py_abc": "abc"} TYPING_TYPE_CHECKS_GUARDS = frozenset({"typing.TYPE_CHECKING", "TYPE_CHECKING"}) BUILTIN_RANGE = "builtins.range" +TYPING_MODULE = "typing" +TYPING_NAMES = frozenset( + { + "Any", + "Callable", + "ClassVar", + "Generic", + "Optional", + "Tuple", + "Type", + "TypeVar", + "Union", + "AbstractSet", + "ByteString", + "Container", + "ContextManager", + "Hashable", + "ItemsView", + "Iterable", + "Iterator", + "KeysView", + "Mapping", + "MappingView", + "MutableMapping", + "MutableSequence", + "MutableSet", + "Sequence", + "Sized", + "ValuesView", + "Awaitable", + "AsyncIterator", + "AsyncIterable", + "Coroutine", + "Collection", + "AsyncGenerator", + "AsyncContextManager", + "Reversible", + "SupportsAbs", + "SupportsBytes", + "SupportsComplex", + "SupportsFloat", + "SupportsInt", + "SupportsRound", + "Counter", + "Deque", + "Dict", + "DefaultDict", + "List", + "Set", + "FrozenSet", + "NamedTuple", + "Generator", + "AnyStr", + "Text", + "Pattern", + } +) def _is_from_future_import(stmt, name): @@ -771,6 +828,11 @@ class VariablesChecker(BaseChecker): # Filter special objects (__doc__, __all__) etc., # because they can be imported for exporting. continue + + if imported_name in self._type_annotation_names: + # Most likely a typing import if it wasn't used so far. + continue + if as_name == "_": continue if as_name is None: @@ -1647,18 +1709,31 @@ class VariablesChecker(BaseChecker): return def _store_type_annotation_node(self, type_annotation): - - if isinstance(type_annotation, astroid.Name): + """Given a type annotation, store all the name nodes it refers to""" + if ( + isinstance(type_annotation, astroid.Name) + and type_annotation.name in TYPING_NAMES + ): self._type_annotation_names.append(type_annotation.name) - else: - self._type_annotation_names.extend( - list( - ( - annotation.name - for annotation in type_annotation.nodes_of_class(astroid.Name) - ) - ) - ) + return + + if not isinstance(type_annotation, astroid.Subscript): + return + + # Check if it is namespaced by typing or not. + if ( + isinstance(type_annotation.value, astroid.Attribute) + and isinstance(type_annotation.value.expr, astroid.Name) + and type_annotation.value.expr.name == TYPING_MODULE + ): + self._type_annotation_names.append(TYPING_MODULE) + return + + self._type_annotation_names.extend( + annotation.name + for annotation in type_annotation.nodes_of_class(astroid.Name) + if annotation.name in TYPING_NAMES + ) def _store_type_annotation_names(self, node): type_annotation = node.type_annotation diff --git a/pylint/test/functional/unused_typing_imports.py b/pylint/test/functional/unused_typing_imports.py index e2fd4ae8a..6edca7153 100644 --- a/pylint/test/functional/unused_typing_imports.py +++ b/pylint/test/functional/unused_typing_imports.py @@ -6,7 +6,18 @@ which means we were never processing them. """ import re -from typing import Optional, Callable, Iterable, Any, List, Tuple, Set, NamedTuple, Pattern +import typing +from typing import ( + Any, + Callable, + Iterable, + List, + NamedTuple, + Optional, + Pattern, + Set, + Tuple, +) def func1(arg: Optional[Callable]=None): @@ -37,3 +48,9 @@ with ContextManager() as SOME_DICT: # type: Set[int] def func_test_type_comment(param): # type: (NamedTuple) -> Tuple[NamedTuple, Pattern] return param, re.compile('good') + + +def typing_fully_qualified(): + variable = None # type: typing.Optional[str] + other_variable: 'typing.Optional[str]' = None + return variable, other_variable diff --git a/pylint/test/functional/unused_typing_imports.rc b/pylint/test/functional/unused_typing_imports.rc index dca49456e..0ba2b6333 100644 --- a/pylint/test/functional/unused_typing_imports.rc +++ b/pylint/test/functional/unused_typing_imports.rc @@ -1,3 +1,2 @@ [testoptions] min_pyver=3.6 -max_pyver=3.7
\ No newline at end of file |