Skip to content

Commit

Permalink
Take into account __class_getitem__ from PEP 560 which fixes some f…
Browse files Browse the repository at this point in the history
…alse

positives for `no-self-argument` and `unsubscriptable-object`.

https://www.python.org/dev/peps/pep-0560/

Close #2416
  • Loading branch information
pcorpet committed Jan 20, 2019
1 parent bc4b56e commit 999d50d
Show file tree
Hide file tree
Showing 11 changed files with 53 additions and 3 deletions.
4 changes: 3 additions & 1 deletion CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -262,4 +262,6 @@ contributors:

* Justin Li (justinnhli)

* Nicolas Dickreuter
* Nicolas Dickreuter

* Pascal Corpet
4 changes: 4 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ What's New in Pylint 2.3.0?

Release date: TBA

* Fixed false positives for ``no-self-argument`` and ``unsubscriptable-object`` when using ``__class_getitem__`` (new in Python 3.7)

Close #2416

* ``fixme`` gets triggered only on comments.

Close #2321
Expand Down
2 changes: 1 addition & 1 deletion pylint/checkers/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1407,7 +1407,7 @@ def _check_first_arg_for_type(self, node, metaclass=0):
# regular class
else:
# class method
if node.type == "classmethod":
if node.type == "classmethod" or node.name == "__class_getitem__":
self._check_first_arg_config(
first,
self.config.valid_classmethod_first_arg,
Expand Down
2 changes: 1 addition & 1 deletion pylint/checkers/typecheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ def _missing_member_hint(owner, attrname, distance_threshold, max_choices):
"Value '%s' is unsubscriptable",
"unsubscriptable-object",
"Emitted when a subscripted value doesn't support subscription "
"(i.e. doesn't define __getitem__ method).",
"(i.e. doesn't define __getitem__ method or __class_getitem__ for a class).",
),
"E1137": (
"%r does not support item assignment",
Expand Down
4 changes: 4 additions & 0 deletions pylint/checkers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
AITER_METHOD = "__aiter__"
NEXT_METHOD = "__next__"
GETITEM_METHOD = "__getitem__"
CLASS_GETITEM_METHOD = "__class_getitem__"
SETITEM_METHOD = "__setitem__"
DELITEM_METHOD = "__delitem__"
CONTAINS_METHOD = "__contains__"
Expand Down Expand Up @@ -1035,6 +1036,9 @@ def supports_membership_test(value: astroid.node_classes.NodeNG) -> bool:


def supports_getitem(value: astroid.node_classes.NodeNG) -> bool:
if isinstance(value, astroid.ClassDef):
if _supports_protocol_method(value, CLASS_GETITEM_METHOD):
return True
return _supports_protocol(value, _supports_getitem_protocol)


Expand Down
15 changes: 15 additions & 0 deletions pylint/test/functional/no_self_argument_py37.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""Test detection of self as argument of first method in Python 3.7 and above."""

# pylint: disable=missing-docstring,too-few-public-methods,useless-object-inheritance


class Toto(object):

def __class_getitem__(cls, params):
# This is actually a special method which is always a class method.
# See https://www.python.org/dev/peps/pep-0560/#class-getitem
pass

def __class_other__(cls, params): # [no-self-argument]
# This is not a special case and as such is an instance method.
pass
2 changes: 2 additions & 0 deletions pylint/test/functional/no_self_argument_py37.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[testoptions]
min_pyver=3.7
1 change: 1 addition & 0 deletions pylint/test/functional/no_self_argument_py37.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
no-self-argument:13:Toto.__class_other__:Method should have "self" as first argument
19 changes: 19 additions & 0 deletions pylint/test/functional/unsubscriptable_value_py37.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""
Checks that class used in a subscript supports subscription
(i.e. defines __class_getitem__ method).
"""
# pylint: disable=missing-docstring,pointless-statement,expression-not-assigned,wrong-import-position
# pylint: disable=too-few-public-methods,import-error,invalid-name,wrong-import-order, useless-object-inheritance

import typing

class Subscriptable(object):

def __class_getitem__(cls, params):
pass


Subscriptable[0]
Subscriptable()[0] # [unsubscriptable-object]

a: typing.List[int]
2 changes: 2 additions & 0 deletions pylint/test/functional/unsubscriptable_value_py37.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[testoptions]
min_pyver=3.7
1 change: 1 addition & 0 deletions pylint/test/functional/unsubscriptable_value_py37.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
unsubscriptable-object:17::Value 'Subscriptable()' is unsubscriptable

0 comments on commit 999d50d

Please sign in to comment.