diff options
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | pylint/checkers/utils.py | 35 | ||||
-rw-r--r-- | tests/functional/p/postponed_evaluation_pep585.py | 15 | ||||
-rw-r--r-- | tests/functional/p/postponed_evaluation_pep585.txt | 4 | ||||
-rw-r--r-- | tests/functional/p/postponed_evaluation_pep585_error.py | 15 | ||||
-rw-r--r-- | tests/functional/p/postponed_evaluation_pep585_error.txt | 6 | ||||
-rw-r--r-- | tests/functional/p/postponed_evaluation_pep585_py39.py | 15 |
7 files changed, 80 insertions, 15 deletions
@@ -90,6 +90,11 @@ Release date: Undefined * Fix issue that caused emacs pylint to fail when used with tramp +* Improve check for invalid PEP 585 syntax inside functions + if postponed evaluation of type annotations is enabled + +* Improve check for invalid PEP 585 syntax as default function arguments + What's New in Pylint 2.7.4? =========================== diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 2e1c659ed..4fefef61c 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -1341,23 +1341,34 @@ def is_class_subscriptable_pep585_with_postponed_evaluation_enabled( """Check if class is subscriptable with PEP 585 and postponed evaluation enabled. """ - if ( - not is_postponed_evaluation_enabled(node) - or value.qname() not in SUBSCRIPTABLE_CLASSES_PEP585 - ): - return False + return ( + is_postponed_evaluation_enabled(node) + and value.qname() in SUBSCRIPTABLE_CLASSES_PEP585 + and is_node_in_type_annotation_context(node) + ) + - parent_node = node.parent +def is_node_in_type_annotation_context(node: astroid.node_classes.NodeNG) -> bool: + """Check if node is in type annotation context. + + Check for 'AnnAssign', function 'Arguments', + or part of function return type anntation. + """ + # pylint: disable=too-many-boolean-expressions + current_node, parent_node = node, node.parent while True: - # Check if any parent node matches condition - if isinstance( - parent_node, (astroid.AnnAssign, astroid.Arguments, astroid.FunctionDef) + if ( + isinstance(parent_node, astroid.AnnAssign) + and parent_node.annotation == current_node + or isinstance(parent_node, astroid.Arguments) + and current_node in parent_node.annotations + or isinstance(parent_node, astroid.FunctionDef) + and parent_node.returns == current_node ): - break - parent_node = parent_node.parent + return True + current_node, parent_node = parent_node, parent_node.parent if isinstance(parent_node, astroid.Module): return False - return True def is_subclass_of(child: astroid.ClassDef, parent: astroid.ClassDef) -> bool: diff --git a/tests/functional/p/postponed_evaluation_pep585.py b/tests/functional/p/postponed_evaluation_pep585.py index e007a4a5f..2a2f30ccf 100644 --- a/tests/functional/p/postponed_evaluation_pep585.py +++ b/tests/functional/p/postponed_evaluation_pep585.py @@ -3,7 +3,7 @@ This check requires Python 3.7 or 3.8! Testing with 3.8 only, to support TypedDict. """ -# pylint: disable=missing-docstring,unused-argument,unused-import,too-few-public-methods,invalid-name,inherit-non-class,unsupported-binary-operation,wrong-import-position,ungrouped-imports +# pylint: disable=missing-docstring,unused-argument,unused-import,too-few-public-methods,invalid-name,inherit-non-class,unsupported-binary-operation,wrong-import-position,ungrouped-imports,unused-variable from __future__ import annotations import collections import dataclasses @@ -115,3 +115,16 @@ var15: collections.Counter[int] var16: collections.abc.Iterable[int] var17: contextlib.AbstractContextManager[int] var18: re.Pattern[str] + + +def func3(): + AliasInvalid2 = list[int] # [unsubscriptable-object] + cast_variable2 = [1, 2, 3] + cast_variable2 = typing.cast(list[int], cast_variable2) # [unsubscriptable-object] + var19: list[int] + +def func4(arg=list[int]): # [unsubscriptable-object] + pass + +def func5(arg1: list[int], arg2=set[int]): # [unsubscriptable-object] + pass diff --git a/tests/functional/p/postponed_evaluation_pep585.txt b/tests/functional/p/postponed_evaluation_pep585.txt index 12c524491..2d595896c 100644 --- a/tests/functional/p/postponed_evaluation_pep585.txt +++ b/tests/functional/p/postponed_evaluation_pep585.txt @@ -14,3 +14,7 @@ unsubscriptable-object:100:15::Value 'list' is unsubscriptable unsubscriptable-object:101:9::Value 'list' is unsubscriptable unsubscriptable-object:101:14::Value 'list' is unsubscriptable unsubscriptable-object:111:7::Value 'OrderedDict' is unsubscriptable +unsubscriptable-object:121:20:func3:Value 'list' is unsubscriptable +unsubscriptable-object:123:33:func3:Value 'list' is unsubscriptable +unsubscriptable-object:126:14:func4:Value 'list' is unsubscriptable +unsubscriptable-object:129:32:func5:Value 'set' is unsubscriptable diff --git a/tests/functional/p/postponed_evaluation_pep585_error.py b/tests/functional/p/postponed_evaluation_pep585_error.py index bcc41def8..7eaf9b05c 100644 --- a/tests/functional/p/postponed_evaluation_pep585_error.py +++ b/tests/functional/p/postponed_evaluation_pep585_error.py @@ -3,7 +3,7 @@ This check requires Python 3.7 or Python 3.8! Testing with 3.8 only, to support TypedDict. """ -# pylint: disable=missing-docstring,unused-argument,unused-import,too-few-public-methods,invalid-name,inherit-non-class,unsupported-binary-operation +# pylint: disable=missing-docstring,unused-argument,unused-import,too-few-public-methods,invalid-name,inherit-non-class,unsupported-binary-operation,unused-variable import collections import dataclasses import typing @@ -97,3 +97,16 @@ Alias3 = Union[Union[list[int], int]] # [unsubscriptable-object] Alias5 = Dict[str, list[int]] # [unsubscriptable-object] Alias6 = int | list[int] # [unsubscriptable-object] Alias7 = list[list[int]] # [unsubscriptable-object,unsubscriptable-object] + + +def func3(): + AliasInvalid2 = list[int] # [unsubscriptable-object] + cast_variable2 = [1, 2, 3] + cast_variable2 = typing.cast(list[int], cast_variable2) # [unsubscriptable-object] + var12: list[int] # [unsubscriptable-object] + +def func4(var=list[int]): # [unsubscriptable-object] + pass + +def func5(arg1: list[int], arg2=set[int]): # [unsubscriptable-object,unsubscriptable-object] + pass diff --git a/tests/functional/p/postponed_evaluation_pep585_error.txt b/tests/functional/p/postponed_evaluation_pep585_error.txt index 31dd1f465..d3697dc67 100644 --- a/tests/functional/p/postponed_evaluation_pep585_error.txt +++ b/tests/functional/p/postponed_evaluation_pep585_error.txt @@ -37,3 +37,9 @@ unsubscriptable-object:97:19::Value 'list' is unsubscriptable unsubscriptable-object:98:15::Value 'list' is unsubscriptable unsubscriptable-object:99:9::Value 'list' is unsubscriptable unsubscriptable-object:99:14::Value 'list' is unsubscriptable +unsubscriptable-object:103:20:func3:Value 'list' is unsubscriptable +unsubscriptable-object:105:33:func3:Value 'list' is unsubscriptable +unsubscriptable-object:106:11:func3:Value 'list' is unsubscriptable +unsubscriptable-object:108:14:func4:Value 'list' is unsubscriptable +unsubscriptable-object:111:16:func5:Value 'list' is unsubscriptable +unsubscriptable-object:111:32:func5:Value 'set' is unsubscriptable diff --git a/tests/functional/p/postponed_evaluation_pep585_py39.py b/tests/functional/p/postponed_evaluation_pep585_py39.py index 387c2273c..75c109a0d 100644 --- a/tests/functional/p/postponed_evaluation_pep585_py39.py +++ b/tests/functional/p/postponed_evaluation_pep585_py39.py @@ -1,5 +1,5 @@ """Test PEP 585 works as expected, starting with Python 3.9""" -# pylint: disable=missing-docstring,unused-argument,unused-import,too-few-public-methods,invalid-name,inherit-non-class,unsupported-binary-operation,wrong-import-position,ungrouped-imports +# pylint: disable=missing-docstring,unused-argument,unused-import,too-few-public-methods,invalid-name,inherit-non-class,unsupported-binary-operation,wrong-import-position,ungrouped-imports,unused-variable import collections import dataclasses import typing @@ -110,3 +110,16 @@ var15: collections.Counter[int] var16: collections.abc.Iterable[int] var17: contextlib.AbstractContextManager[int] var18: re.Pattern[str] + + +def func3(): + AliasInvalid2 = list[int] + cast_variable2 = [1, 2, 3] + cast_variable2 = typing.cast(list[int], cast_variable2) + var19: list[int] + +def func4(var=list[int]): + pass + +def func5(arg1: list[int], arg2=set[int]): + pass |