From e54a5ec5bf41840de96df649e62e9b2fac316fa7 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Thu, 3 Dec 2015 22:04:38 +0200 Subject: Accept only functions and methods for the deprecated-method checker. This prevents a crash which can occur when an object doesn't have .qname() method after the inference. --- ChangeLog | 9 +++++ pylint/checkers/stdlib.py | 12 +++++-- pylint/test/unittest_checker_stdlib.py | 65 ++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 pylint/test/unittest_checker_stdlib.py diff --git a/ChangeLog b/ChangeLog index 5e7b4b3..0e6433e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,11 @@ ChangeLog for Pylint -------------------- -- + * Accept only functions and methods for the deprecated-method checker. + + This prevents a crash which can occur when an object doesn't have + .qname() method after the inference. + * Added a new warning, 'unsupported-assignment-operation', which is emitted when item assignment is tried on an object which doesn't have this ability. Closes issue #591. @@ -10,6 +15,10 @@ ChangeLog for Pylint emitted when item deletion is tried on an object which doesn't have this ability. Closes issue #592. + +2015-12-02 -- 1.5.1 + + * Fix a crash which occurred when old visit methods are encountered in plugin modules. Closes issue #711. diff --git a/pylint/checkers/stdlib.py b/pylint/checkers/stdlib.py index afb4541..0ec3d6c 100644 --- a/pylint/checkers/stdlib.py +++ b/pylint/checkers/stdlib.py @@ -211,7 +211,7 @@ class StdlibChecker(BaseChecker): for value in node.values: self._check_datetime(value) - def _check_deprecated_method(self, node, infer): + def _check_deprecated_method(self, node, inferred): py_vers = sys.version_info[0] if isinstance(node.func, astroid.Attribute): @@ -222,7 +222,14 @@ class StdlibChecker(BaseChecker): # Not interested in other nodes. return - qname = infer.qname() + # Reject nodes which aren't of interest to us. + acceptable_nodes = (astroid.BoundMethod, + astroid.UnboundMethod, + astroid.FunctionDef) + if not isinstance(inferred, acceptable_nodes): + return + + qname = inferred.qname() if qname in self.deprecated[0]: self.add_message('deprecated-method', node=node, args=(func_name, )) @@ -233,7 +240,6 @@ class StdlibChecker(BaseChecker): args=(func_name, )) break - def _check_redundant_assert(self, node, infer): if (isinstance(infer, astroid.BoundMethod) and node.args and isinstance(node.args[0], astroid.Const) and diff --git a/pylint/test/unittest_checker_stdlib.py b/pylint/test/unittest_checker_stdlib.py new file mode 100644 index 0000000..82c1f17 --- /dev/null +++ b/pylint/test/unittest_checker_stdlib.py @@ -0,0 +1,65 @@ +# Copyright (c) 2003-2015 LOGILAB S.A. (Paris, FRANCE). +# http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +import contextlib +import unittest + +import astroid +from astroid import test_utils + +from pylint.checkers import stdlib +from pylint.testutils import CheckerTestCase + + +@contextlib.contextmanager +def _add_transform(manager, node, transform, predicate=None): + manager.register_transform(node, transform, predicate) + try: + yield + finally: + manager.unregister_transform(node, transform, predicate) + + +class StdlibCheckerTest(CheckerTestCase): + CHECKER_CLASS = stdlib.StdlibChecker + + def test_deprecated_no_qname_on_unexpected_nodes(self): + # Test that we don't crash on nodes which don't have + # a qname method. While this test might seem weird since + # it uses a transform, it's actually testing a crash that + # happened in production, but there was no way to retrieve + # the code for which this occurred (how an AssignAttr + # got to be the result of a function inference + # beats me..) + + def infer_func(node, context=None): + new_node = astroid.AssignAttr() + new_node.parent = node + yield new_node + + manager = astroid.MANAGER + transform = astroid.inference_tip(infer_func) + with _add_transform(manager, astroid.Name, transform): + node = test_utils.extract_node(''' + call_something() + ''') + with self.assertNoMessages(): + self.checker.visit_call(node) + + + +if __name__ == '__main__': + unittest.main() -- cgit v1.2.1