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.
authorClaudiu Popa <pcmanticore@gmail.com>
changeset5ad1348455fe
branchdefault
phasepublic
hiddenno
parent revision#18da1f9006b2 Disable persistence for test_self.
child revision#84cb406fd1a0 Don't emit super-on-old-class on classes with unknown bases.
files modified by this revision
ChangeLog
pylint/checkers/stdlib.py
pylint/test/unittest_checker_stdlib.py
# HG changeset patch
# User Claudiu Popa <pcmanticore@gmail.com>
# Date 1449173078 -7200
# Thu Dec 03 22:04:38 2015 +0200
# Node ID 5ad1348455fe4758b0dcab4051e5f1c33d54dd66
# Parent 18da1f9006b2fdae7063821a0d994678a19c8b2d
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.

diff --git a/ChangeLog b/ChangeLog
@@ -1,17 +1,26 @@
1  ChangeLog for Pylint
2  --------------------
3 
4  --
5 +     * Accept only functions and methods for the deprecated-method checker.
6 +
7 +      This prevents a crash which can occur when an object doesn't have
8 +      .qname() method after the inference.
9 +
10      * Added a new warning, 'unsupported-assignment-operation', which is
11        emitted when item assignment is tried on an object which doesn't
12        have this ability. Closes issue #591.
13 
14      * Added a new warning, 'unsupported-delete-operation', which is
15        emitted when item deletion is tried on an object which doesn't
16        have this ability. Closes issue #592.
17 
18 +
19 +2015-12-02 -- 1.5.1
20 +
21 +
22      * Fix a crash which occurred when old visit methods are encountered
23        in plugin modules. Closes issue #711.
24 
25      * Add wrong-import-position to check_messages's decorator arguments
26        for ImportChecker.leave_module
diff --git a/pylint/checkers/stdlib.py b/pylint/checkers/stdlib.py
@@ -209,33 +209,39 @@
27      @utils.check_messages('boolean-datetime')
28      def visit_boolop(self, node):
29          for value in node.values:
30              self._check_datetime(value)
31 
32 -    def _check_deprecated_method(self, node, infer):
33 +    def _check_deprecated_method(self, node, inferred):
34          py_vers = sys.version_info[0]
35 
36          if isinstance(node.func, astroid.Attribute):
37              func_name = node.func.attrname
38          elif isinstance(node.func, astroid.Name):
39              func_name = node.func.name
40          else:
41              # Not interested in other nodes.
42              return
43 
44 -        qname = infer.qname()
45 +        # Reject nodes which aren't of interest to us.
46 +        acceptable_nodes = (astroid.BoundMethod,
47 +                            astroid.UnboundMethod,
48 +                            astroid.FunctionDef)
49 +        if not isinstance(inferred, acceptable_nodes):
50 +            return
51 +
52 +        qname = inferred.qname()
53          if qname in self.deprecated[0]:
54              self.add_message('deprecated-method', node=node,
55                               args=(func_name, ))
56          else:
57              for since_vers, func_list in self.deprecated[py_vers].items():
58                  if since_vers <= sys.version_info and qname in func_list:
59                      self.add_message('deprecated-method', node=node,
60                                       args=(func_name, ))
61                      break
62 
63 -
64      def _check_redundant_assert(self, node, infer):
65          if (isinstance(infer, astroid.BoundMethod) and
66                  node.args and isinstance(node.args[0], astroid.Const) and
67                  infer.name in ['assertTrue', 'assertFalse']):
68              self.add_message('redundant-unittest-assert',
diff --git a/pylint/test/unittest_checker_stdlib.py b/pylint/test/unittest_checker_stdlib.py
@@ -0,0 +1,65 @@
69 +# Copyright (c) 2003-2015 LOGILAB S.A. (Paris, FRANCE).
70 +# http://www.logilab.fr/ -- mailto:contact@logilab.fr
71 +#
72 +# This program is free software; you can redistribute it and/or modify it under
73 +# the terms of the GNU General Public License as published by the Free Software
74 +# Foundation; either version 2 of the License, or (at your option) any later
75 +# version.
76 +#
77 +# This program is distributed in the hope that it will be useful, but WITHOUT
78 +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
79 +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
80 +#
81 +# You should have received a copy of the GNU General Public License along with
82 +# this program; if not, write to the Free Software Foundation, Inc.,
83 +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
84 +
85 +import contextlib
86 +import unittest
87 +
88 +import astroid
89 +from astroid import test_utils
90 +
91 +from pylint.checkers import stdlib
92 +from pylint.testutils import CheckerTestCase
93 +
94 +
95 +@contextlib.contextmanager
96 +def _add_transform(manager, node, transform, predicate=None):
97 +    manager.register_transform(node, transform, predicate)
98 +    try:
99 +        yield
100 +    finally:
101 +        manager.unregister_transform(node, transform, predicate)
102 +
103 +
104 +class StdlibCheckerTest(CheckerTestCase):
105 +    CHECKER_CLASS = stdlib.StdlibChecker
106 +
107 +    def test_deprecated_no_qname_on_unexpected_nodes(self):
108 +        # Test that we don't crash on nodes which don't have
109 +        # a qname method. While this test might seem weird since
110 +        # it uses a transform, it's actually testing a crash that
111 +        # happened in production, but there was no way to retrieve
112 +        # the code for which this occurred (how an AssignAttr
113 +        # got to be the result of a function inference
114 +        # beats me..)
115 +
116 +        def infer_func(node, context=None):
117 +            new_node = astroid.AssignAttr()
118 +            new_node.parent = node
119 +            yield new_node
120 +
121 +        manager = astroid.MANAGER
122 +        transform = astroid.inference_tip(infer_func)
123 +        with _add_transform(manager, astroid.Name, transform):
124 +            node = test_utils.extract_node('''
125 +            call_something()
126 +            ''')
127 +            with self.assertNoMessages():
128 +                self.checker.visit_call(node)
129 + 
130 +
131 +
132 +if __name__ == '__main__':
133 +    unittest.main()