diff --git a/doc/whatsnew/fragments/10696.false_positive b/doc/whatsnew/fragments/10696.false_positive new file mode 100644 index 0000000000..9a341efe24 --- /dev/null +++ b/doc/whatsnew/fragments/10696.false_positive @@ -0,0 +1,3 @@ +Fixed false positive for ``invalid-name`` with ``typing.Annotated``. + +Closes #10696 diff --git a/pylint/checkers/base/name_checker/checker.py b/pylint/checkers/base/name_checker/checker.py index 3cb1c336da..d282ab5eda 100644 --- a/pylint/checkers/base/name_checker/checker.py +++ b/pylint/checkers/base/name_checker/checker.py @@ -488,17 +488,7 @@ def visit_assignname( # pylint: disable=too-many-branches,too-many-statements return # Check classes (TypeVar's are classes so they need to be excluded first) - elif isinstance(inferred_assign_type, nodes.ClassDef) or ( - isinstance(inferred_assign_type, bases.Instance) - and {"EnumMeta", "TypedDict"}.intersection( - { - ancestor.name - for ancestor in cast( - InferenceResult, inferred_assign_type - ).mro() - } - ) - ): + elif self._should_check_class_regex(inferred_assign_type): self._check_name("class", node.name, node) # Don't emit if the name redefines an import in an ImportError except handler @@ -576,6 +566,28 @@ def _meets_exception_for_non_consts( regexp = self._name_regexps["variable"] return regexp.match(name) is not None + def _should_check_class_regex( + self, inferred_assign_type: InferenceResult | None + ) -> bool: + if isinstance(inferred_assign_type, nodes.ClassDef): + return True + if isinstance(inferred_assign_type, bases.Instance) and { + "EnumMeta", + "TypedDict", + }.intersection( + { + ancestor.name + for ancestor in cast(InferenceResult, inferred_assign_type).mro() + } + ): + return True + if ( + isinstance(inferred_assign_type, nodes.FunctionDef) + and inferred_assign_type.qname() == "typing.Annotated" + ): + return True + return False + def _recursive_check_names(self, args: list[nodes.AssignName]) -> None: """Check names in a possibly recursive list .""" for arg in args: diff --git a/tests/functional/i/invalid/invalid_name/invalid_name_module_level.py b/tests/functional/i/invalid/invalid_name/invalid_name_module_level.py index 9eae16b7c1..99323d1119 100644 --- a/tests/functional/i/invalid/invalid_name/invalid_name_module_level.py +++ b/tests/functional/i/invalid/invalid_name/invalid_name_module_level.py @@ -51,3 +51,7 @@ def A(): # [invalid-name] VERSION = version("ty") # uninferable except PackageNotFoundError: VERSION = "0.0.0" + + +from typing import Annotated +IntWithAnnotation = Annotated[int, "anything"]