summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPierre Sassoulas <pierre.sassoulas@gmail.com>2021-02-15 22:18:54 +0100
committerGitHub <noreply@github.com>2021-02-15 22:18:54 +0100
commite48946f0b4bd385fcd93b6ef920d092f0d8d7424 (patch)
treed3cb5ed7b674b1db111062c178181dd8b7f33f92
parent493885f17375bf322a1b8d3ee7ba5a8dd4124599 (diff)
downloadpylint-git-e48946f0b4bd385fcd93b6ef920d092f0d8d7424.tar.gz
Graingert add generated members match against the qualified name (#4092)
* Fix #2498: add generated-members match against the qualified name Add test for fully qualified generated-members Co-authored-by: Gauthier Sebaux <gauthier.sebaux@wavestone.com> Co-authored-by: Thomas Grainger <tagrain@gmail.com>
-rw-r--r--CONTRIBUTORS.txt2
-rw-r--r--ChangeLog4
-rw-r--r--pylint/checkers/typecheck.py32
-rw-r--r--tests/functional/g/generated_members.py2
-rw-r--r--tests/functional/g/generated_members.rc8
-rw-r--r--tests/functional/g/generated_members.txt1
6 files changed, 37 insertions, 12 deletions
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 81b1008bb..cdf7db72f 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -436,6 +436,8 @@ contributors:
* Frank Harrison (doublethefish): contributor
+* Gauthier Sebaux: contributor
+
* Logan Miller (komodo472): contributor
* Matthew Suozzo: contributor
diff --git a/ChangeLog b/ChangeLog
index e3183ec5d..a12f631f6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -138,6 +138,10 @@ Release date: TBA
Close #3314
+* `generated-members` now matches the qualified name of members
+
+ Close #2498
+
What's New in Pylint 2.6.0?
===========================
diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py
index 055292a60..e135f6e94 100644
--- a/pylint/checkers/typecheck.py
+++ b/pylint/checkers/typecheck.py
@@ -60,6 +60,7 @@ import types
from collections import deque
from collections.abc import Sequence
from functools import singledispatch
+from typing import Pattern, Tuple
import astroid
import astroid.arguments
@@ -855,17 +856,20 @@ accessed. Python regular expressions are accepted.",
def _suggestion_mode(self):
return get_global_option(self, "suggestion-mode", default=True)
- def open(self):
- # do this in open since config not fully initialized in __init__
+ @decorators.cachedproperty
+ def _compiled_generated_members(self) -> Tuple[Pattern, ...]:
+ # do this lazily since config not fully initialized in __init__
# generated_members may contain regular expressions
# (surrounded by quote `"` and followed by a comma `,`)
# REQUEST,aq_parent,"[a-zA-Z]+_set{1,2}"' =>
# ('REQUEST', 'aq_parent', '[a-zA-Z]+_set{1,2}')
- if isinstance(self.config.generated_members, str):
- gen = shlex.shlex(self.config.generated_members)
+ generated_members = self.config.generated_members
+ if isinstance(generated_members, str):
+ gen = shlex.shlex(generated_members)
gen.whitespace += ","
gen.wordchars += r"[]-+\.*?()|"
- self.config.generated_members = tuple(tok.strip('"') for tok in gen)
+ generated_members = tuple(tok.strip('"') for tok in gen)
+ return tuple(re.compile(exp) for exp in generated_members)
@check_messages("keyword-arg-before-vararg")
def visit_functiondef(self, node):
@@ -919,12 +923,12 @@ accessed. Python regular expressions are accepted.",
function/method, super call and metaclasses are ignored
"""
- for pattern in self.config.generated_members:
- # attribute is marked as generated, stop here
- if re.match(pattern, node.attrname):
- return
- if re.match(pattern, node.as_string()):
- return
+ if any(
+ pattern.match(name)
+ for name in (node.attrname, node.as_string())
+ for pattern in self._compiled_generated_members
+ ):
+ return
try:
inferred = list(node.expr.infer())
@@ -955,6 +959,12 @@ accessed. Python regular expressions are accepted.",
):
continue
+ qualname = "{}.{}".format(owner.pytype(), node.attrname)
+ if any(
+ pattern.match(qualname) for pattern in self._compiled_generated_members
+ ):
+ return
+
try:
if not [
n
diff --git a/tests/functional/g/generated_members.py b/tests/functional/g/generated_members.py
index 802ebaf8f..b79c008dd 100644
--- a/tests/functional/g/generated_members.py
+++ b/tests/functional/g/generated_members.py
@@ -9,6 +9,8 @@ class Klass(object):
print(Klass().DoesNotExist)
print(Klass().aBC_set1)
+print(Klass().ham.does.not_.exist)
+print(Klass().spam.does.not_.exist) # [no-member]
node_classes.Tuple.does.not_.exist
checkers.base.doesnotexist()
diff --git a/tests/functional/g/generated_members.rc b/tests/functional/g/generated_members.rc
index 177eca955..8b61b4996 100644
--- a/tests/functional/g/generated_members.rc
+++ b/tests/functional/g/generated_members.rc
@@ -2,4 +2,10 @@
disable=too-few-public-methods,print-statement
[typecheck]
-generated-members=DoesNotExist,"[a-zA-Z]+_set{1,2}",node_classes.Tuple.*,checkers.*?base,(session|SESSION).rollback
+generated-members=
+ \Afunctional\.g\.generated_members\.Klass\.ham\Z,
+ DoesNotExist,
+ "[a-zA-Z]+_set{1,2}",
+ node_classes.Tuple.*,
+ checkers.*?base,
+ (session|SESSION).rollback
diff --git a/tests/functional/g/generated_members.txt b/tests/functional/g/generated_members.txt
new file mode 100644
index 000000000..28e793c32
--- /dev/null
+++ b/tests/functional/g/generated_members.txt
@@ -0,0 +1 @@
+no-member:13:6::Instance of 'Klass' has no 'spam' member:INFERENCE