Skip to content

Commit

Permalink
Graingert add generated members match against the qualified name (#4092)
Browse files Browse the repository at this point in the history
* Fix #2498: add generated-members match against the qualified name

Add test for fully qualified generated-members

Co-authored-by: Gauthier Sebaux <[email protected]>
Co-authored-by: Thomas Grainger <[email protected]>
  • Loading branch information
3 people authored Feb 15, 2021
1 parent 493885f commit e48946f
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 12 deletions.
2 changes: 2 additions & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,8 @@ contributors:

* Frank Harrison (doublethefish): contributor

* Gauthier Sebaux: contributor

* Logan Miller (komodo472): contributor

* Matthew Suozzo: contributor
Expand Down
4 changes: 4 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -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?
===========================

Expand Down
32 changes: 21 additions & 11 deletions pylint/checkers/typecheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
from collections import deque
from collections.abc import Sequence
from functools import singledispatch
from typing import Pattern, Tuple

import astroid
import astroid.arguments
Expand Down Expand Up @@ -855,17 +856,20 @@ class should be ignored. A mixin class is detected if its name ends with \
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):
Expand Down Expand Up @@ -919,12 +923,12 @@ def visit_attribute(self, node):
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())
Expand Down Expand Up @@ -955,6 +959,12 @@ def visit_attribute(self, node):
):
continue

qualname = "{}.{}".format(owner.pytype(), node.attrname)
if any(
pattern.match(qualname) for pattern in self._compiled_generated_members
):
return

try:
if not [
n
Expand Down
2 changes: 2 additions & 0 deletions tests/functional/g/generated_members.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
8 changes: 7 additions & 1 deletion tests/functional/g/generated_members.rc
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions tests/functional/g/generated_members.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
no-member:13:6::Instance of 'Klass' has no 'spam' member:INFERENCE

0 comments on commit e48946f

Please sign in to comment.