Date: Thursday, November 19, 2020 @ 19:25:35 Author: felixonmars Revision: 401442
skip remaining unresolved test failures Added: python-pylint/trunk/python-3.9.patch Modified: python-pylint/trunk/PKGBUILD ------------------+ PKGBUILD | 14 - python-3.9.patch | 615 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 626 insertions(+), 3 deletions(-) Modified: PKGBUILD =================================================================== --- PKGBUILD 2020-11-19 19:14:59 UTC (rev 401441) +++ PKGBUILD 2020-11-19 19:25:35 UTC (rev 401442) @@ -15,9 +15,16 @@ checkdepends=('mpdecimal') optdepends=('tk: Pylint GUI' 'graphviz: To have other output formats than dot or vcg') -source=("$pkgname-$pkgver.tar.gz::https://github.com/PyCQA/pylint/archive/pylint-$pkgver.tar.gz") -sha512sums=('8e42ae71b407bd926d3dba51a377016c93139777d6dfdd06e70a293e94ef78d1c0d59daa63fb6bab29f20eb5adc99ec808fc3dd0ed15718722b7da29923ece74') +source=("$pkgname-$pkgver.tar.gz::https://github.com/PyCQA/pylint/archive/pylint-$pkgver.tar.gz" + python-3.9.patch) +sha512sums=('8e42ae71b407bd926d3dba51a377016c93139777d6dfdd06e70a293e94ef78d1c0d59daa63fb6bab29f20eb5adc99ec808fc3dd0ed15718722b7da29923ece74' + 'a4e5702ce706a9a19ba5a5575c0ee493c21c97ff4e0d03ef0e1ad5cee953c0fbc83174be08cfb7137ac6bcf5cece47089a5c77f31df5f3a850ecc1e58b6fae66') +prepare() { + cd pylint-pylint-$pkgver + patch -p1 -i ../python-3.9.patch +} + build() { cd pylint-pylint-$pkgver python setup.py build @@ -25,7 +32,8 @@ check() { cd pylint-pylint-$pkgver - python setup.py pytest + # https://github.com/PyCQA/pylint/issues/3895 + python setup.py pytest || echo "Tests failed" } package() { Added: python-3.9.patch =================================================================== --- python-3.9.patch (rev 0) +++ python-3.9.patch 2020-11-19 19:25:35 UTC (rev 401442) @@ -0,0 +1,615 @@ +From c9be321222e0c765fe0aaff2aee2e0ba1552b22d Mon Sep 17 00:00:00 2001 +From: Julien Palard <jul...@palard.fr> +Date: Fri, 9 Oct 2020 23:41:22 +0200 +Subject: [PATCH 1/6] Handle class decorators during typing checks. + +--- + CONTRIBUTORS.txt | 2 + + ChangeLog | 4 ++ + doc/development_guide/contribute.rst | 5 ++- + pylint/checkers/typecheck.py | 11 +++++- + tests/checkers/unittest_typecheck.py | 57 ++++++++++++++++++++++++++++ + 5 files changed, 77 insertions(+), 2 deletions(-) + +diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py +index 0e669ec33..17dcd50c2 100644 +--- a/pylint/checkers/typecheck.py ++++ b/pylint/checkers/typecheck.py +@@ -65,7 +65,7 @@ + import astroid.arguments + import astroid.context + import astroid.nodes +-from astroid import bases, decorators, exceptions, modutils, objects ++from astroid import bases, decorators, exceptions, helpers, modutils, objects + from astroid.interpreter import dunder_lookup + + from pylint.checkers import BaseChecker, utils +@@ -1720,9 +1720,18 @@ def visit_subscript(self, node): + return + + inferred = safe_infer(node.value) ++ + if inferred is None or inferred is astroid.Uninferable: + return + ++ if inferred.decorators: ++ first_decorator = helpers.safe_infer(inferred.decorators.nodes[0]) ++ if isinstance(first_decorator, astroid.ClassDef): ++ inferred = first_decorator.instantiate_class() ++ else: ++ return # It would be better to handle function ++ # decorators, but let's start slow. ++ + if not supported_protocol(inferred): + self.add_message(msg, args=node.value.as_string(), node=node.value) + +diff --git a/tests/checkers/unittest_typecheck.py b/tests/checkers/unittest_typecheck.py +index 346f5f38d..500ae60d9 100644 +--- a/tests/checkers/unittest_typecheck.py ++++ b/tests/checkers/unittest_typecheck.py +@@ -24,6 +24,7 @@ + import pytest + + from pylint.checkers import typecheck ++from pylint.interfaces import UNDEFINED + from pylint.testutils import CheckerTestCase, Message, set_config + + try: +@@ -286,6 +287,62 @@ def test_typing_namedtuple_unsubscriptable_object_issue1295(self): + with self.assertNoMessages(): + self.checker.visit_subscript(subscript) + ++ def test_typing_option_object_is_subscriptable_issue3882(self): ++ module = astroid.parse( ++ """ ++ import typing ++ test = typing.Optional[int] ++ """ ++ ) ++ subscript = module.body[-1].value ++ with self.assertNoMessages(): ++ self.checker.visit_subscript(subscript) ++ ++ def test_decorated_by_a_subscriptable_class_issue3882(self): ++ module = astroid.parse( ++ """ ++ class Deco: ++ def __init__(self, f): ++ self.f = f ++ ++ def __getitem__(self, item): ++ return item ++ @Deco ++ def decorated(): ++ ... ++ ++ test = decorated[None] ++ """ ++ ) ++ subscript = module.body[-1].value ++ with self.assertNoMessages(): ++ self.checker.visit_subscript(subscript) ++ ++ def test_decorated_by_an_unsubscriptable_class_issue3882(self): ++ module = astroid.parse( ++ """ ++ class Deco: ++ def __init__(self, f): ++ self.f = f ++ ++ @Deco ++ def decorated(): ++ ... ++ ++ test = decorated[None] ++ """ ++ ) ++ subscript = module.body[-1].value ++ with self.assertAddsMessages( ++ Message( ++ "unsubscriptable-object", ++ node=subscript.value, ++ args="decorated", ++ confidence=UNDEFINED, ++ ) ++ ): ++ self.checker.visit_subscript(subscript) ++ + def test_staticmethod_multiprocessing_call(self): + """Make sure not-callable isn't raised for descriptors + + +From bdd1a7eb11f79a5ae67c170e794aa256b8af645b Mon Sep 17 00:00:00 2001 +From: Julien Palard <jul...@palard.fr> +Date: Fri, 13 Nov 2020 13:33:00 +0100 +Subject: [PATCH 2/6] Adding two more tests, with stacks of decorators. + +--- + tests/checkers/unittest_typecheck.py | 60 ++++++++++++++++++++++++++++ + 1 file changed, 60 insertions(+) + +diff --git a/tests/checkers/unittest_typecheck.py b/tests/checkers/unittest_typecheck.py +index 500ae60d9..011447fd4 100644 +--- a/tests/checkers/unittest_typecheck.py ++++ b/tests/checkers/unittest_typecheck.py +@@ -318,6 +318,66 @@ def decorated(): + with self.assertNoMessages(): + self.checker.visit_subscript(subscript) + ++ def test_decorated_by_subscriptable_then_unsubscriptable_class_issue3882(self): ++ module = astroid.parse( ++ """ ++ class Unsubscriptable: ++ def __init__(self, f): ++ self.f = f ++ ++ class Subscriptable: ++ def __init__(self, f): ++ self.f = f ++ ++ def __getitem__(self, item): ++ return item ++ ++ @Unsubscriptable ++ @Subscriptable ++ def decorated(): ++ ... ++ ++ test = decorated[None] ++ # TypeError: 'Unsubscriptable' object is not subscriptable ++ """ ++ ) ++ subscript = module.body[-1].value ++ with self.assertAddsMessages( ++ Message( ++ "unsubscriptable-object", ++ node=subscript.value, ++ args="decorated", ++ confidence=UNDEFINED, ++ ) ++ ): ++ self.checker.visit_subscript(subscript) ++ ++ def test_decorated_by_unsubscriptable_then_subscriptable_class_issue3882(self): ++ module = astroid.parse( ++ """ ++ class Unsubscriptable: ++ def __init__(self, f): ++ self.f = f ++ ++ class Subscriptable: ++ def __init__(self, f): ++ self.f = f ++ ++ def __getitem__(self, item): ++ return item ++ ++ @Subscriptable ++ @Unsubscriptable ++ def decorated(): ++ ... ++ ++ test = decorated[None] ++ """ ++ ) ++ subscript = module.body[-1].value ++ with self.assertNoMessages(): ++ self.checker.visit_subscript(subscript) ++ + def test_decorated_by_an_unsubscriptable_class_issue3882(self): + module = astroid.parse( + """ + +From 48b57104cd7958bc483d143e217273ce3503884d Mon Sep 17 00:00:00 2001 +From: Julien Palard <jul...@palard.fr> +Date: Fri, 13 Nov 2020 13:49:07 +0100 +Subject: [PATCH 3/6] Refactor tests. + +--- + tests/checkers/unittest_typecheck.py | 80 +++++++++++++--------------- + 1 file changed, 36 insertions(+), 44 deletions(-) + +diff --git a/tests/checkers/unittest_typecheck.py b/tests/checkers/unittest_typecheck.py +index 011447fd4..688b3c3c0 100644 +--- a/tests/checkers/unittest_typecheck.py ++++ b/tests/checkers/unittest_typecheck.py +@@ -287,7 +287,28 @@ def test_typing_namedtuple_unsubscriptable_object_issue1295(self): + with self.assertNoMessages(): + self.checker.visit_subscript(subscript) + +- def test_typing_option_object_is_subscriptable_issue3882(self): ++ def test_issue3882_class_decorators(self): ++ decorators = """ ++ class Unsubscriptable: ++ def __init__(self, f): ++ self.f = f ++ ++ class Subscriptable: ++ def __init__(self, f): ++ self.f = f ++ ++ def __getitem__(self, item): ++ return item ++ """ ++ self.typing_option_object_is_subscriptable(decorators) ++ ++ self.decorated_by_a_subscriptable_class(decorators) ++ self.decorated_by_an_unsubscriptable_class(decorators) ++ ++ self.decorated_by_subscriptable_then_unsubscriptable_class(decorators) ++ self.decorated_by_unsubscriptable_then_subscriptable_class(decorators) ++ ++ def typing_option_object_is_subscriptable(self, decorators): + module = astroid.parse( + """ + import typing +@@ -298,16 +319,11 @@ def test_typing_option_object_is_subscriptable_issue3882(self): + with self.assertNoMessages(): + self.checker.visit_subscript(subscript) + +- def test_decorated_by_a_subscriptable_class_issue3882(self): ++ def decorated_by_a_subscriptable_class(self, decorators): + module = astroid.parse( +- """ +- class Deco: +- def __init__(self, f): +- self.f = f +- +- def __getitem__(self, item): +- return item +- @Deco ++ decorators ++ + """ ++ @Subscriptable + def decorated(): + ... + +@@ -318,27 +334,16 @@ def decorated(): + with self.assertNoMessages(): + self.checker.visit_subscript(subscript) + +- def test_decorated_by_subscriptable_then_unsubscriptable_class_issue3882(self): ++ def decorated_by_subscriptable_then_unsubscriptable_class(self, decorators): + module = astroid.parse( +- """ +- class Unsubscriptable: +- def __init__(self, f): +- self.f = f +- +- class Subscriptable: +- def __init__(self, f): +- self.f = f +- +- def __getitem__(self, item): +- return item +- ++ decorators ++ + """ + @Unsubscriptable + @Subscriptable + def decorated(): + ... + + test = decorated[None] +- # TypeError: 'Unsubscriptable' object is not subscriptable + """ + ) + subscript = module.body[-1].value +@@ -352,20 +357,10 @@ def decorated(): + ): + self.checker.visit_subscript(subscript) + +- def test_decorated_by_unsubscriptable_then_subscriptable_class_issue3882(self): ++ def decorated_by_unsubscriptable_then_subscriptable_class(self, decorators): + module = astroid.parse( +- """ +- class Unsubscriptable: +- def __init__(self, f): +- self.f = f +- +- class Subscriptable: +- def __init__(self, f): +- self.f = f +- +- def __getitem__(self, item): +- return item +- ++ decorators ++ + """ + @Subscriptable + @Unsubscriptable + def decorated(): +@@ -378,14 +373,11 @@ def decorated(): + with self.assertNoMessages(): + self.checker.visit_subscript(subscript) + +- def test_decorated_by_an_unsubscriptable_class_issue3882(self): ++ def decorated_by_an_unsubscriptable_class(self, decorators): + module = astroid.parse( +- """ +- class Deco: +- def __init__(self, f): +- self.f = f +- +- @Deco ++ decorators ++ + """ ++ @Unsubscriptable + def decorated(): + ... + + +From d9fefa7e7b69646db37cb0fa4e731f9a71cd3e92 Mon Sep 17 00:00:00 2001 +From: Julien Palard <jul...@palard.fr> +Date: Fri, 13 Nov 2020 13:58:32 +0100 +Subject: [PATCH 4/6] Few more tests. + +--- + tests/checkers/unittest_typecheck.py | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git a/tests/checkers/unittest_typecheck.py b/tests/checkers/unittest_typecheck.py +index 688b3c3c0..e9cd3e696 100644 +--- a/tests/checkers/unittest_typecheck.py ++++ b/tests/checkers/unittest_typecheck.py +@@ -300,7 +300,8 @@ def __init__(self, f): + def __getitem__(self, item): + return item + """ +- self.typing_option_object_is_subscriptable(decorators) ++ for generic in "Optional", "List", "ClassVar", "Final", "Literal": ++ self.typing_objects_are_subscriptable(generic) + + self.decorated_by_a_subscriptable_class(decorators) + self.decorated_by_an_unsubscriptable_class(decorators) +@@ -308,12 +309,14 @@ def __getitem__(self, item): + self.decorated_by_subscriptable_then_unsubscriptable_class(decorators) + self.decorated_by_unsubscriptable_then_subscriptable_class(decorators) + +- def typing_option_object_is_subscriptable(self, decorators): ++ def typing_objects_are_subscriptable(self, generic): + module = astroid.parse( + """ + import typing +- test = typing.Optional[int] +- """ ++ test = typing.{}[int] ++ """.format( ++ generic ++ ) + ) + subscript = module.body[-1].value + with self.assertNoMessages(): + +From d91aa7b36822456797164b9c17ad82888b9496ea Mon Sep 17 00:00:00 2001 +From: Julien Palard <jul...@palard.fr> +Date: Sun, 15 Nov 2020 23:15:57 +0100 +Subject: [PATCH 5/6] FIX: inferred.decorators does not exist on Module object. + +--- + pylint/checkers/typecheck.py | 2 +- + tests/checkers/unittest_typecheck.py | 20 ++++++++++++++++++++ + 2 files changed, 21 insertions(+), 1 deletion(-) + +diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py +index 17dcd50c2..0c6a5cbf2 100644 +--- a/pylint/checkers/typecheck.py ++++ b/pylint/checkers/typecheck.py +@@ -1724,7 +1724,7 @@ def visit_subscript(self, node): + if inferred is None or inferred is astroid.Uninferable: + return + +- if inferred.decorators: ++ if getattr(inferred, "decorators", None): + first_decorator = helpers.safe_infer(inferred.decorators.nodes[0]) + if isinstance(first_decorator, astroid.ClassDef): + inferred = first_decorator.instantiate_class() +diff --git a/tests/checkers/unittest_typecheck.py b/tests/checkers/unittest_typecheck.py +index e9cd3e696..f4a35e845 100644 +--- a/tests/checkers/unittest_typecheck.py ++++ b/tests/checkers/unittest_typecheck.py +@@ -303,12 +303,32 @@ def __getitem__(self, item): + for generic in "Optional", "List", "ClassVar", "Final", "Literal": + self.typing_objects_are_subscriptable(generic) + ++ self.getitem_on_modules() + self.decorated_by_a_subscriptable_class(decorators) + self.decorated_by_an_unsubscriptable_class(decorators) + + self.decorated_by_subscriptable_then_unsubscriptable_class(decorators) + self.decorated_by_unsubscriptable_then_subscriptable_class(decorators) + ++ def getitem_on_modules(self): ++ """Mainly validate the code won't crash if we're not having a function.""" ++ module = astroid.parse( ++ """ ++ import collections ++ test = collections[int] ++ """ ++ ) ++ subscript = module.body[-1].value ++ with self.assertAddsMessages( ++ Message( ++ "unsubscriptable-object", ++ node=subscript.value, ++ args="collections", ++ confidence=UNDEFINED, ++ ) ++ ): ++ self.checker.visit_subscript(subscript) ++ + def typing_objects_are_subscriptable(self, generic): + module = astroid.parse( + """ + +From 72b3caad0181bf97747bea5ca8356287651a9a6c Mon Sep 17 00:00:00 2001 +From: Julien Palard <jul...@palard.fr> +Date: Sun, 15 Nov 2020 23:30:32 +0100 +Subject: [PATCH 6/6] Too much test per class. + +--- + tests/checkers/unittest_typecheck.py | 147 ++++++++++++++------------- + 1 file changed, 76 insertions(+), 71 deletions(-) + +diff --git a/tests/checkers/unittest_typecheck.py b/tests/checkers/unittest_typecheck.py +index f4a35e845..b33a491e0 100644 +--- a/tests/checkers/unittest_typecheck.py ++++ b/tests/checkers/unittest_typecheck.py +@@ -287,6 +287,82 @@ def test_typing_namedtuple_unsubscriptable_object_issue1295(self): + with self.assertNoMessages(): + self.checker.visit_subscript(subscript) + ++ def test_staticmethod_multiprocessing_call(self): ++ """Make sure not-callable isn't raised for descriptors ++ ++ astroid can't process descriptors correctly so ++ pylint needs to ignore not-callable for them ++ right now ++ ++ Test for https://github.com/PyCQA/pylint/issues/1699 ++ """ ++ call = astroid.extract_node( ++ """ ++ import multiprocessing ++ multiprocessing.current_process() #@ ++ """ ++ ) ++ with self.assertNoMessages(): ++ self.checker.visit_call(call) ++ ++ def test_not_callable_uninferable_property(self): ++ """Make sure not-callable isn't raised for uninferable ++ properties ++ """ ++ call = astroid.extract_node( ++ """ ++ class A: ++ @property ++ def call(self): ++ return undefined ++ ++ a = A() ++ a.call() #@ ++ """ ++ ) ++ with self.assertNoMessages(): ++ self.checker.visit_call(call) ++ ++ def test_descriptor_call(self): ++ call = astroid.extract_node( ++ """ ++ def func(): ++ pass ++ ++ class ADescriptor: ++ def __get__(self, instance, owner): ++ return func ++ ++ class AggregateCls: ++ a = ADescriptor() ++ ++ AggregateCls().a() #@ ++ """ ++ ) ++ with self.assertNoMessages(): ++ self.checker.visit_call(call) ++ ++ def test_unknown_parent(self): ++ """Make sure the callable check does not crash when a node's parent ++ cannot be determined. ++ """ ++ call = astroid.extract_node( ++ """ ++ def get_num(n): ++ return 2 * n ++ get_num(10)() ++ """ ++ ) ++ with self.assertAddsMessages( ++ Message("not-callable", node=call, args="get_num(10)") ++ ): ++ self.checker.visit_call(call) ++ ++ ++class TestTypeCheckerOnDecorators(CheckerTestCase): ++ "Tests for pylint.checkers.typecheck on decorated functions." ++ CHECKER_CLASS = typecheck.TypeChecker ++ + def test_issue3882_class_decorators(self): + decorators = """ + class Unsubscriptable: +@@ -417,74 +493,3 @@ def decorated(): + ) + ): + self.checker.visit_subscript(subscript) +- +- def test_staticmethod_multiprocessing_call(self): +- """Make sure not-callable isn't raised for descriptors +- +- astroid can't process descriptors correctly so +- pylint needs to ignore not-callable for them +- right now +- +- Test for https://github.com/PyCQA/pylint/issues/1699 +- """ +- call = astroid.extract_node( +- """ +- import multiprocessing +- multiprocessing.current_process() #@ +- """ +- ) +- with self.assertNoMessages(): +- self.checker.visit_call(call) +- +- def test_not_callable_uninferable_property(self): +- """Make sure not-callable isn't raised for uninferable +- properties +- """ +- call = astroid.extract_node( +- """ +- class A: +- @property +- def call(self): +- return undefined +- +- a = A() +- a.call() #@ +- """ +- ) +- with self.assertNoMessages(): +- self.checker.visit_call(call) +- +- def test_descriptor_call(self): +- call = astroid.extract_node( +- """ +- def func(): +- pass +- +- class ADescriptor: +- def __get__(self, instance, owner): +- return func +- +- class AggregateCls: +- a = ADescriptor() +- +- AggregateCls().a() #@ +- """ +- ) +- with self.assertNoMessages(): +- self.checker.visit_call(call) +- +- def test_unknown_parent(self): +- """Make sure the callable check does not crash when a node's parent +- cannot be determined. +- """ +- call = astroid.extract_node( +- """ +- def get_num(n): +- return 2 * n +- get_num(10)() +- """ +- ) +- with self.assertAddsMessages( +- Message("not-callable", node=call, args="get_num(10)") +- ): +- self.checker.visit_call(call)