From db7009ac5f518d9ee40a15a29d4a18220e987ff4 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Sat, 22 Nov 2025 12:45:33 -0500 Subject: [PATCH] [invalid-name] Fix FP for exclusive assignment of objects (#10746) (cherry picked from commit d308554ad37a6880283b41d66ee2715a1690c516) --- custom_dict.txt | 2 ++ doc/whatsnew/fragments/10745.false_positive | 4 ++++ pylint/checkers/base/name_checker/checker.py | 13 ++++++------- .../invalid_name/invalid_name_module_level.py | 6 ++++++ 4 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 doc/whatsnew/fragments/10745.false_positive diff --git a/custom_dict.txt b/custom_dict.txt index 77df598cf8..9428ac6d79 100644 --- a/custom_dict.txt +++ b/custom_dict.txt @@ -24,6 +24,7 @@ asynccontextmanager attr attrib attrname +attrs backport BaseChecker basename @@ -144,6 +145,7 @@ gv hashable hmac html +iattrs idgeneratormixin ifexpr igetattr diff --git a/doc/whatsnew/fragments/10745.false_positive b/doc/whatsnew/fragments/10745.false_positive new file mode 100644 index 0000000000..3915e3b405 --- /dev/null +++ b/doc/whatsnew/fragments/10745.false_positive @@ -0,0 +1,4 @@ +Fix a false positive for ``invalid-name`` on an UPPER_CASED name inside an +``if`` branch that assigns an object. + +Closes #10745 diff --git a/pylint/checkers/base/name_checker/checker.py b/pylint/checkers/base/name_checker/checker.py index 4530e78822..9b3ed0ed1f 100644 --- a/pylint/checkers/base/name_checker/checker.py +++ b/pylint/checkers/base/name_checker/checker.py @@ -518,13 +518,12 @@ def visit_assignname( # pylint: disable=too-many-branches,too-many-statements and self._name_regexps["const"].match(node.name) is not None ): return - if ( - util.Uninferable not in iattrs - and len(iattrs) > 1 - and all( - astroid.are_exclusive(*combo) - for combo in itertools.combinations(iattrs, 2) - ) + # Do the exclusive assignment analysis on attrs, not iattrs. + # iattrs locations could be anywhere (inference result). + attrs = tuple(node.frame().getattr(node.name)) + if len(attrs) > 1 and all( + astroid.are_exclusive(*combo) + for combo in itertools.combinations(attrs, 2) ): node_type = "const" if not self._meets_exception_for_non_consts( 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 75620aea57..61f8651c4d 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 @@ -44,6 +44,12 @@ def A(): # [invalid-name] other_const = [3] +if CONST: + ANOTHER_CONST = A() +else: + ANOTHER_CONST = 5 + + from importlib.metadata import PackageNotFoundError from importlib.metadata import version