summaryrefslogtreecommitdiff
path: root/pylint
diff options
context:
space:
mode:
authorDaniƫl van Noord <13665637+DanielNoord@users.noreply.github.com>2021-10-25 09:27:47 +0200
committerGitHub <noreply@github.com>2021-10-25 09:27:47 +0200
commit8cfce138e9519f63e969e89359e86e140b2f0f13 (patch)
treeeff12c3d285d53b7a6e4cf387f265ca029c72b51 /pylint
parented3449fee063d91f050c6b733030d3b3d7ad719f (diff)
downloadpylint-git-8cfce138e9519f63e969e89359e86e140b2f0f13.tar.gz
Add ``mixin-class-rgx`` option (#5203)
Co-authored-by: Alpha <alpha@pokesplash.net> Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
Diffstat (limited to 'pylint')
-rw-r--r--pylint/checkers/async.py10
-rw-r--r--pylint/checkers/classes.py5
-rw-r--r--pylint/checkers/typecheck.py29
-rw-r--r--pylint/utils/utils.py5
4 files changed, 38 insertions, 11 deletions
diff --git a/pylint/checkers/async.py b/pylint/checkers/async.py
index f9d1e15dd..9aaead7c4 100644
--- a/pylint/checkers/async.py
+++ b/pylint/checkers/async.py
@@ -44,6 +44,7 @@ class AsyncChecker(checkers.BaseChecker):
self._ignore_mixin_members = utils.get_global_option(
self, "ignore-mixin-members"
)
+ self._mixin_class_rgx = utils.get_global_option(self, "mixin-class-rgx")
self._async_generators = ["contextlib.asynccontextmanager"]
@checker_utils.check_messages("yield-inside-async-function")
@@ -81,10 +82,11 @@ class AsyncChecker(checkers.BaseChecker):
# just skip it.
if not checker_utils.has_known_bases(inferred):
continue
- # Just ignore mixin classes.
- if self._ignore_mixin_members:
- if inferred.name[-5:].lower() == "mixin":
- continue
+ # Ignore mixin classes if they match the rgx option.
+ if self._ignore_mixin_members and self._mixin_class_rgx.match(
+ inferred.name
+ ):
+ continue
else:
continue
self.add_message(
diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py
index 0b15861b0..2ab66fd0b 100644
--- a/pylint/checkers/classes.py
+++ b/pylint/checkers/classes.py
@@ -808,6 +808,9 @@ a metaclass class method.",
self._first_attrs = []
self._meth_could_be_func = None
+ def open(self) -> None:
+ self._mixin_class_rgx = get_global_option(self, "mixin-class-rgx")
+
@astroid.decorators.cachedproperty
def _dummy_rgx(self):
return get_global_option(self, "dummy-variables-rgx", default=None)
@@ -1029,7 +1032,7 @@ a metaclass class method.",
def _check_attribute_defined_outside_init(self, cnode: nodes.ClassDef) -> None:
# check access to existent members on non metaclass classes
- if self._ignore_mixin and cnode.name[-5:].lower() == "mixin":
+ if self._ignore_mixin and self._mixin_class_rgx.match(cnode.name):
# We are in a mixin class. No need to try to figure out if
# something is missing, since it is most likely that it will
# miss.
diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py
index 8fa80ee21..549cd7f4c 100644
--- a/pylint/checkers/typecheck.py
+++ b/pylint/checkers/typecheck.py
@@ -442,7 +442,14 @@ SEQUENCE_TYPES = {
}
-def _emit_no_member(node, owner, owner_name, ignored_mixins=True, ignored_none=True):
+def _emit_no_member(
+ node,
+ owner,
+ owner_name,
+ mixin_class_rgx: Pattern[str],
+ ignored_mixins=True,
+ ignored_none=True,
+):
"""Try to see if no-member should be emitted for the given owner.
The following cases are ignored:
@@ -462,7 +469,7 @@ def _emit_no_member(node, owner, owner_name, ignored_mixins=True, ignored_none=T
return False
if is_super(owner) or getattr(owner, "type", None) == "metaclass":
return False
- if owner_name and ignored_mixins and owner_name[-5:].lower() == "mixin":
+ if owner_name and ignored_mixins and mixin_class_rgx.match(owner_name):
return False
if isinstance(owner, nodes.FunctionDef) and (
owner.decorators or owner.is_abstract()
@@ -781,14 +788,24 @@ class TypeChecker(BaseChecker):
},
),
(
+ "mixin-class-rgx",
+ {
+ "default": ".*[Mm]ixin",
+ "type": "regexp",
+ "metavar": "<regexp>",
+ "help": "Regex pattern to define which classes are considered mixins "
+ "ignore-mixin-members is set to 'yes'",
+ },
+ ),
+ (
"ignore-mixin-members",
{
"default": True,
"type": "yn",
"metavar": "<y_or_n>",
- "help": 'Tells whether missing members accessed in mixin \
-class should be ignored. A mixin class is detected if its name ends with \
-"mixin" (case insensitive).',
+ "help": "Tells whether missing members accessed in mixin "
+ "class should be ignored. A class is considered mixin if its name matches "
+ "the mixin-class-rgx option.",
},
),
(
@@ -898,6 +915,7 @@ accessed. Python regular expressions are accepted.",
def open(self) -> None:
py_version = get_global_option(self, "py-version")
self._py310_plus = py_version >= (3, 10)
+ self._mixin_class_rgx = get_global_option(self, "mixin-class-rgx")
@astroid.decorators.cachedproperty
def _suggestion_mode(self):
@@ -1040,6 +1058,7 @@ accessed. Python regular expressions are accepted.",
node,
owner,
name,
+ self._mixin_class_rgx,
ignored_mixins=self.config.ignore_mixin_members,
ignored_none=self.config.ignore_none,
):
diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py
index 6c9f11298..8243bc322 100644
--- a/pylint/utils/utils.py
+++ b/pylint/utils/utils.py
@@ -54,7 +54,10 @@ GLOBAL_OPTION_BOOL = Literal[
GLOBAL_OPTION_INT = Literal["max-line-length", "docstring-min-length"]
GLOBAL_OPTION_LIST = Literal["ignored-modules"]
GLOBAL_OPTION_PATTERN = Literal[
- "no-docstring-rgx", "dummy-variables-rgx", "ignored-argument-names"
+ "no-docstring-rgx",
+ "dummy-variables-rgx",
+ "ignored-argument-names",
+ "mixin-class-rgx",
]
GLOBAL_OPTION_PATTERN_LIST = Literal["ignore-paths"]
GLOBAL_OPTION_TUPLE_INT = Literal["py-version"]