Skip to content

Commit 284a794

Browse files
fix: avoid false positive when module-level names match class-level names
- Add scope comparision to avoid module-level constants to be incorrectly classified as variables when a class-level attribute with the same name exists Closes #10719
1 parent 4f0716a commit 284a794

File tree

5 files changed

+47
-13
lines changed

5 files changed

+47
-13
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixed false positive for ``invalid-name`` where module-level constants were incorrectly classified as variables when a class-level attribute with the same name exists.
2+
3+
Closes #10719

pylint/checkers/utils.py

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1847,24 +1847,40 @@ def is_reassigned_before_current(node: nodes.NodeNG, varname: str) -> bool:
18471847
"""Check if the given variable name is reassigned in the same scope before the
18481848
current node.
18491849
"""
1850-
return any(
1851-
a.name == varname and a.lineno < node.lineno
1852-
for a in node.scope().nodes_of_class(
1853-
(nodes.AssignName, nodes.ClassDef, nodes.FunctionDef)
1854-
)
1855-
)
1850+
node_scope = node.scope()
1851+
node_lineno = node.lineno
1852+
if node_lineno is None:
1853+
return False
1854+
for a in node_scope.nodes_of_class(
1855+
(nodes.AssignName, nodes.ClassDef, nodes.FunctionDef)
1856+
):
1857+
if a.name == varname and a.lineno is not None and a.lineno < node_lineno:
1858+
if isinstance(a, (nodes.ClassDef, nodes.FunctionDef)):
1859+
if a.parent and a.parent.scope() == node_scope:
1860+
return True
1861+
elif a.scope() == node_scope:
1862+
return True
1863+
return False
18561864

18571865

18581866
def is_reassigned_after_current(node: nodes.NodeNG, varname: str) -> bool:
18591867
"""Check if the given variable name is reassigned in the same scope after the
18601868
current node.
18611869
"""
1862-
return any(
1863-
a.name == varname and a.lineno > node.lineno
1864-
for a in node.scope().nodes_of_class(
1865-
(nodes.AssignName, nodes.ClassDef, nodes.FunctionDef)
1866-
)
1867-
)
1870+
node_scope = node.scope()
1871+
node_lineno = node.lineno
1872+
if node_lineno is None:
1873+
return False
1874+
for a in node_scope.nodes_of_class(
1875+
(nodes.AssignName, nodes.ClassDef, nodes.FunctionDef)
1876+
):
1877+
if a.name == varname and a.lineno is not None and a.lineno > node_lineno:
1878+
if isinstance(a, (nodes.ClassDef, nodes.FunctionDef)):
1879+
if a.parent and a.parent.scope() == node_scope:
1880+
return True
1881+
elif a.scope() == node_scope:
1882+
return True
1883+
return False
18681884

18691885

18701886
def is_deleted_after_current(node: nodes.NodeNG, varname: str) -> bool:
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""Test module-level constants with class attribute same name
2+
Regression test for #10719.
3+
"""
4+
# pylint: disable=missing-docstring, too-few-public-methods, redefined-builtin
5+
6+
7+
class Theme:
8+
INPUT = ">>> "
9+
10+
11+
INPUT = Theme()
12+
input = Theme()
13+
OUTPUT = Theme()
14+
output = Theme()

tests/functional/n/no/no_dummy_redefined.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Make sure warnings about redefinitions do not trigger for dummy variables."""
2+
# pylint: disable=invalid-name
23

34

45
_, INTERESTING = 'a=b'.split('=')
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
redefined-outer-name:11:4:11:9:clobbering:Redefining name 'value' from outer scope (line 6):UNDEFINED
1+
redefined-outer-name:12:4:12:9:clobbering:Redefining name 'value' from outer scope (line 7):UNDEFINED

0 commit comments

Comments
 (0)