diff options
author | Pascal Corpet <pcorpet@users.noreply.github.com> | 2019-02-04 14:56:09 +0100 |
---|---|---|
committer | Claudiu Popa <pcmanticore@gmail.com> | 2019-02-04 14:56:09 +0100 |
commit | 03b4d035203c750908e197debdbb3f8161d51666 (patch) | |
tree | fa76e6994b2c001d369d03ccaa6e441d0737b402 | |
parent | c2af5c760307edaf50cc519d189b620ba41f12db (diff) | |
download | pylint-git-03b4d035203c750908e197debdbb3f8161d51666.tar.gz |
Take into account `__class_getitem__`
Take into account `__class_getitem__` from PEP 560 and fixes some false
positives for `no-self-argument` and `unsubscriptable-object`
PEP: https://www.python.org/dev/peps/pep-0560/
Close #2416
-rw-r--r-- | CONTRIBUTORS.txt | 4 | ||||
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | pylint/checkers/classes.py | 2 | ||||
-rw-r--r-- | pylint/checkers/typecheck.py | 2 | ||||
-rw-r--r-- | pylint/checkers/utils.py | 4 | ||||
-rw-r--r-- | pylint/test/functional/no_self_argument_py37.py | 15 | ||||
-rw-r--r-- | pylint/test/functional/no_self_argument_py37.rc | 2 | ||||
-rw-r--r-- | pylint/test/functional/no_self_argument_py37.txt | 1 | ||||
-rw-r--r-- | pylint/test/functional/unsubscriptable_value_py37.py | 19 | ||||
-rw-r--r-- | pylint/test/functional/unsubscriptable_value_py37.rc | 2 | ||||
-rw-r--r-- | pylint/test/functional/unsubscriptable_value_py37.txt | 1 |
11 files changed, 53 insertions, 3 deletions
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 200e9651e..a479e9461 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -262,4 +262,6 @@ contributors: * Justin Li (justinnhli) -* Nicolas Dickreuter
\ No newline at end of file +* Nicolas Dickreuter + +* Pascal Corpet @@ -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 + * Support ``Ellipsis`` as a synonym for ``pass`` statements. Close #2718 diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index b0a85906b..2fb152bd1 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -1407,7 +1407,7 @@ a metaclass class method.", # 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, diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index 976e7fa89..321636e2f 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -329,7 +329,7 @@ MSGS = { "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", diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 36aa8791f..d6083d4ef 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -67,6 +67,7 @@ ITER_METHOD = "__iter__" AITER_METHOD = "__aiter__" NEXT_METHOD = "__next__" GETITEM_METHOD = "__getitem__" +CLASS_GETITEM_METHOD = "__class_getitem__" SETITEM_METHOD = "__setitem__" DELITEM_METHOD = "__delitem__" CONTAINS_METHOD = "__contains__" @@ -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) diff --git a/pylint/test/functional/no_self_argument_py37.py b/pylint/test/functional/no_self_argument_py37.py new file mode 100644 index 000000000..8e6d6fc34 --- /dev/null +++ b/pylint/test/functional/no_self_argument_py37.py @@ -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 diff --git a/pylint/test/functional/no_self_argument_py37.rc b/pylint/test/functional/no_self_argument_py37.rc new file mode 100644 index 000000000..a17bb22da --- /dev/null +++ b/pylint/test/functional/no_self_argument_py37.rc @@ -0,0 +1,2 @@ +[testoptions] +min_pyver=3.7 diff --git a/pylint/test/functional/no_self_argument_py37.txt b/pylint/test/functional/no_self_argument_py37.txt new file mode 100644 index 000000000..812b8aa0c --- /dev/null +++ b/pylint/test/functional/no_self_argument_py37.txt @@ -0,0 +1 @@ +no-self-argument:13:Toto.__class_other__:Method should have "self" as first argument diff --git a/pylint/test/functional/unsubscriptable_value_py37.py b/pylint/test/functional/unsubscriptable_value_py37.py new file mode 100644 index 000000000..c7b06cfb2 --- /dev/null +++ b/pylint/test/functional/unsubscriptable_value_py37.py @@ -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] diff --git a/pylint/test/functional/unsubscriptable_value_py37.rc b/pylint/test/functional/unsubscriptable_value_py37.rc new file mode 100644 index 000000000..a17bb22da --- /dev/null +++ b/pylint/test/functional/unsubscriptable_value_py37.rc @@ -0,0 +1,2 @@ +[testoptions] +min_pyver=3.7 diff --git a/pylint/test/functional/unsubscriptable_value_py37.txt b/pylint/test/functional/unsubscriptable_value_py37.txt new file mode 100644 index 000000000..b2ff0fff5 --- /dev/null +++ b/pylint/test/functional/unsubscriptable_value_py37.txt @@ -0,0 +1 @@ +unsubscriptable-object:17::Value 'Subscriptable()' is unsubscriptable |