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