Skip to content

Commit 46dedd0

Browse files
committed
Take into account __class_getitem__ from PEP 560 which fixes some false
positives for `no-self-argument` and `unsubscriptable-object`. https://www.python.org/dev/peps/pep-0560/ Close pylint-dev#2416
1 parent bc4b56e commit 46dedd0

11 files changed

+47
-3
lines changed

Diff for: CONTRIBUTORS.txt

+3-1
Original file line numberDiff line numberDiff line change
@@ -262,4 +262,6 @@ contributors:
262262

263263
* Justin Li (justinnhli)
264264

265-
* Nicolas Dickreuter
265+
* Nicolas Dickreuter
266+
267+
* Pascal Corpet

Diff for: ChangeLog

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ What's New in Pylint 2.3.0?
77

88
Release date: TBA
99

10+
* Fixed false positives for ``no-self-argument`` and ``unsubscriptable-object`` when using ``__class_getitem__`` (new in Python 3.7)
11+
12+
Close #2416
13+
1014
* ``fixme`` gets triggered only on comments.
1115

1216
Close #2321

Diff for: pylint/checkers/classes.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1407,7 +1407,7 @@ def _check_first_arg_for_type(self, node, metaclass=0):
14071407
# regular class
14081408
else:
14091409
# class method
1410-
if node.type == "classmethod":
1410+
if node.type == "classmethod" or node.name == "__class_getitem__":
14111411
self._check_first_arg_config(
14121412
first,
14131413
self.config.valid_classmethod_first_arg,

Diff for: pylint/checkers/typecheck.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ def _missing_member_hint(owner, attrname, distance_threshold, max_choices):
329329
"Value '%s' is unsubscriptable",
330330
"unsubscriptable-object",
331331
"Emitted when a subscripted value doesn't support subscription "
332-
"(i.e. doesn't define __getitem__ method).",
332+
"(i.e. doesn't define __getitem__ method or __class_getitem__ for a class).",
333333
),
334334
"E1137": (
335335
"%r does not support item assignment",

Diff for: pylint/checkers/utils.py

+3
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
AITER_METHOD = "__aiter__"
6868
NEXT_METHOD = "__next__"
6969
GETITEM_METHOD = "__getitem__"
70+
CLASS_GETITEM_METHOD = "__class_getitem__"
7071
SETITEM_METHOD = "__setitem__"
7172
DELITEM_METHOD = "__delitem__"
7273
CONTAINS_METHOD = "__contains__"
@@ -1035,6 +1036,8 @@ def supports_membership_test(value: astroid.node_classes.NodeNG) -> bool:
10351036

10361037

10371038
def supports_getitem(value: astroid.node_classes.NodeNG) -> bool:
1039+
if isinstance(value, astroid.ClassDef):
1040+
return _supports_protocol_method(value, CLASS_GETITEM_METHOD)
10381041
return _supports_protocol(value, _supports_getitem_protocol)
10391042

10401043

Diff for: pylint/test/functional/no_self_argument_py37.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""Test detection of self as argument of first method in Python 3.7 and above."""
2+
3+
# pylint: disable=missing-docstring,too-few-public-methods,useless-object-inheritance
4+
5+
6+
class Toto(object):
7+
8+
def __class_getitem__(cls, params):
9+
# This is actually a special method which is always a class method.
10+
# See https://www.python.org/dev/peps/pep-0560/#class-getitem
11+
pass
12+
13+
def __class_other__(cls, params): # [no-self-argument]
14+
# This is not a special case and as such is an instance method.
15+
pass

Diff for: pylint/test/functional/no_self_argument_py37.rc

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[testoptions]
2+
min_pyver=3.7

Diff for: pylint/test/functional/no_self_argument_py37.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
no-self-argument:13:Toto.__class_other__:Method should have "self" as first argument

Diff for: pylint/test/functional/unsubscriptable_value_py37.py

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""
2+
Checks that class used in a subscript supports subscription
3+
(i.e. defines __class_getitem__ method).
4+
"""
5+
# pylint: disable=missing-docstring,pointless-statement,expression-not-assigned,wrong-import-position
6+
# pylint: disable=too-few-public-methods,import-error,invalid-name,wrong-import-order, useless-object-inheritance
7+
class Subscriptable(object):
8+
9+
def __class_getitem__(cls, params):
10+
pass
11+
12+
13+
Subscriptable[0]
14+
Subscriptable()[0] # [unsubscriptable-object]

Diff for: pylint/test/functional/unsubscriptable_value_py37.rc

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[testoptions]
2+
min_pyver=3.7
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
unsubscriptable-object:14::Value 'Subscriptable()' is unsubscriptable

0 commit comments

Comments
 (0)