Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(69)

Side by Side Diff: third_party/pylint/checkers/classes.py

Issue 739393004: Revert "Revert "pylint: upgrade to 1.3.1"" (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools/
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « third_party/pylint/checkers/base.py ('k') | third_party/pylint/checkers/design_analysis.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2003-2011 LOGILAB S.A. (Paris, FRANCE). 1 # Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE).
2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr 2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr
3 # 3 #
4 # This program is free software; you can redistribute it and/or modify it under 4 # This program is free software; you can redistribute it and/or modify it under
5 # the terms of the GNU General Public License as published by the Free Software 5 # the terms of the GNU General Public License as published by the Free Software
6 # Foundation; either version 2 of the License, or (at your option) any later 6 # Foundation; either version 2 of the License, or (at your option) any later
7 # version. 7 # version.
8 # 8 #
9 # This program is distributed in the hope that it will be useful, but WITHOUT 9 # This program is distributed in the hope that it will be useful, but WITHOUT
10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 11 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 # 12 #
13 # You should have received a copy of the GNU General Public License along with 13 # You should have received a copy of the GNU General Public License along with
14 # this program; if not, write to the Free Software Foundation, Inc., 14 # this program; if not, write to the Free Software Foundation, Inc.,
15 # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 """classes checker for Python code 16 """classes checker for Python code
17 """ 17 """
18 from __future__ import generators 18 from __future__ import generators
19 19
20 from logilab import astng 20 import sys
21 from logilab.astng import YES, Instance, are_exclusive
22 21
23 from pylint.interfaces import IASTNGChecker 22 import astroid
23 from astroid import YES, Instance, are_exclusive, AssAttr, Class
24 from astroid.bases import Generator
25
26 from pylint.interfaces import IAstroidChecker
24 from pylint.checkers import BaseChecker 27 from pylint.checkers import BaseChecker
25 from pylint.checkers.utils import PYMETHODS, overrides_a_method, check_messages 28 from pylint.checkers.utils import (
29 PYMETHODS, overrides_a_method, check_messages, is_attr_private,
30 is_attr_protected, node_frame_class, safe_infer)
31
32 if sys.version_info >= (3, 0):
33 NEXT_METHOD = '__next__'
34 else:
35 NEXT_METHOD = 'next'
36 ITER_METHODS = ('__iter__', '__getitem__')
26 37
27 def class_is_abstract(node): 38 def class_is_abstract(node):
28 """return true if the given class node should be considered as an abstract 39 """return true if the given class node should be considered as an abstract
29 class 40 class
30 """ 41 """
31 for method in node.methods(): 42 for method in node.methods():
32 if method.parent.frame() is node: 43 if method.parent.frame() is node:
33 if method.is_abstract(pass_is_abstract=False): 44 if method.is_abstract(pass_is_abstract=False):
34 return True 45 return True
35 return False 46 return False
36 47
37 48
38 MSGS = { 49 MSGS = {
39 'F0202': ('Unable to check methods signature (%s / %s)', 50 'F0202': ('Unable to check methods signature (%s / %s)',
51 'method-check-failed',
40 'Used when PyLint has been unable to check methods signature \ 52 'Used when PyLint has been unable to check methods signature \
41 compatibility for an unexpected reason. Please report this kind \ 53 compatibility for an unexpected reason. Please report this kind \
42 if you don\'t make sense of it.'), 54 if you don\'t make sense of it.'),
43 55
44 'E0202': ('An attribute affected in %s line %s hide this method', 56 'E0202': ('An attribute defined in %s line %s hides this method',
57 'method-hidden',
45 'Used when a class defines a method which is hidden by an ' 58 'Used when a class defines a method which is hidden by an '
46 'instance attribute from an ancestor class or set by some ' 59 'instance attribute from an ancestor class or set by some '
47 'client code.'), 60 'client code.'),
48 'E0203': ('Access to member %r before its definition line %s', 61 'E0203': ('Access to member %r before its definition line %s',
62 'access-member-before-definition',
49 'Used when an instance member is accessed before it\'s actually\ 63 'Used when an instance member is accessed before it\'s actually\
50 assigned.'), 64 assigned.'),
51 'W0201': ('Attribute %r defined outside __init__', 65 'W0201': ('Attribute %r defined outside __init__',
66 'attribute-defined-outside-init',
52 'Used when an instance attribute is defined outside the __init__\ 67 'Used when an instance attribute is defined outside the __init__\
53 method.'), 68 method.'),
54 69
55 'W0212': ('Access to a protected member %s of a client class', # E0214 70 'W0212': ('Access to a protected member %s of a client class', # E0214
71 'protected-access',
56 'Used when a protected member (i.e. class member with a name \ 72 'Used when a protected member (i.e. class member with a name \
57 beginning with an underscore) is access outside the class or a \ 73 beginning with an underscore) is access outside the class or a \
58 descendant of the class where it\'s defined.'), 74 descendant of the class where it\'s defined.'),
59 75
60 'E0211': ('Method has no argument', 76 'E0211': ('Method has no argument',
77 'no-method-argument',
61 'Used when a method which should have the bound instance as \ 78 'Used when a method which should have the bound instance as \
62 first argument has no argument defined.'), 79 first argument has no argument defined.'),
63 'E0213': ('Method should have "self" as first argument', 80 'E0213': ('Method should have "self" as first argument',
81 'no-self-argument',
64 'Used when a method has an attribute different the "self" as\ 82 'Used when a method has an attribute different the "self" as\
65 first argument. This is considered as an error since this is\ 83 first argument. This is considered as an error since this is\
66 a so common convention that you shouldn\'t break it!'), 84 a so common convention that you shouldn\'t break it!'),
67 'C0202': ('Class method should have %s as first argument', # E0212 85 'C0202': ('Class method %s should have %s as first argument', # E0212
68 'Used when a class method has an attribute different than "cls"\ 86 'bad-classmethod-argument',
69 as first argument, to easily differentiate them from regular \ 87 'Used when a class method has a first argument named differently '
70 instance methods.'), 88 'than the value specified in valid-classmethod-first-arg option '
71 'C0203': ('Metaclass method should have "mcs" as first argument', # E0214 89 '(default to "cls"), recommended to easily differentiate them '
72 'Used when a metaclass method has an attribute different the \ 90 'from regular instance methods.'),
73 "mcs" as first argument.'), 91 'C0203': ('Metaclass method %s should have %s as first argument', # E0214
92 'bad-mcs-method-argument',
93 'Used when a metaclass method has a first agument named '
94 'differently than the value specified in valid-classmethod-first'
95 '-arg option (default to "cls"), recommended to easily '
96 'differentiate them from regular instance methods.'),
97 'C0204': ('Metaclass class method %s should have %s as first argument',
98 'bad-mcs-classmethod-argument',
99 'Used when a metaclass class method has a first argument named '
100 'differently than the value specified in valid-metaclass-'
101 'classmethod-first-arg option (default to "mcs"), recommended to '
102 'easily differentiate them from regular instance methods.'),
74 103
75 'W0211': ('Static method with %r as first argument', 104 'W0211': ('Static method with %r as first argument',
76 'Used when a static method has "self" or "cls" as first argument.' 105 'bad-staticmethod-argument',
77 ), 106 'Used when a static method has "self" or a value specified in '
107 'valid-classmethod-first-arg option or '
108 'valid-metaclass-classmethod-first-arg option as first argument.'
109 ),
78 'R0201': ('Method could be a function', 110 'R0201': ('Method could be a function',
111 'no-self-use',
79 'Used when a method doesn\'t use its bound instance, and so could\ 112 'Used when a method doesn\'t use its bound instance, and so could\
80 be written as a function.' 113 be written as a function.'
81 ), 114 ),
82 115
83 'E0221': ('Interface resolved to %s is not a class', 116 'E0221': ('Interface resolved to %s is not a class',
117 'interface-is-not-class',
84 'Used when a class claims to implement an interface which is not \ 118 'Used when a class claims to implement an interface which is not \
85 a class.'), 119 a class.'),
86 'E0222': ('Missing method %r from %s interface', 120 'E0222': ('Missing method %r from %s interface',
121 'missing-interface-method',
87 'Used when a method declared in an interface is missing from a \ 122 'Used when a method declared in an interface is missing from a \
88 class implementing this interface'), 123 class implementing this interface'),
89 'W0221': ('Arguments number differs from %s method', 124 'W0221': ('Arguments number differs from %s method',
125 'arguments-differ',
90 'Used when a method has a different number of arguments than in \ 126 'Used when a method has a different number of arguments than in \
91 the implemented interface or in an overridden method.'), 127 the implemented interface or in an overridden method.'),
92 'W0222': ('Signature differs from %s method', 128 'W0222': ('Signature differs from %s method',
129 'signature-differs',
93 'Used when a method signature is different than in the \ 130 'Used when a method signature is different than in the \
94 implemented interface or in an overridden method.'), 131 implemented interface or in an overridden method.'),
95 'W0223': ('Method %r is abstract in class %r but is not overridden', 132 'W0223': ('Method %r is abstract in class %r but is not overridden',
133 'abstract-method',
96 'Used when an abstract method (i.e. raise NotImplementedError) is \ 134 'Used when an abstract method (i.e. raise NotImplementedError) is \
97 not overridden in concrete class.' 135 not overridden in concrete class.'
98 ), 136 ),
99 'F0220': ('failed to resolve interfaces implemented by %s (%s)', # W0224 137 'F0220': ('failed to resolve interfaces implemented by %s (%s)', # W0224
138 'unresolved-interface',
100 'Used when a PyLint as failed to find interfaces implemented by \ 139 'Used when a PyLint as failed to find interfaces implemented by \
101 a class'), 140 a class'),
102 141
103 142
104 'W0231': ('__init__ method from base class %r is not called', 143 'W0231': ('__init__ method from base class %r is not called',
144 'super-init-not-called',
105 'Used when an ancestor class method has an __init__ method \ 145 'Used when an ancestor class method has an __init__ method \
106 which is not called by a derived class.'), 146 which is not called by a derived class.'),
107 'W0232': ('Class has no __init__ method', 147 'W0232': ('Class has no __init__ method',
148 'no-init',
108 'Used when a class has no __init__ method, neither its parent \ 149 'Used when a class has no __init__ method, neither its parent \
109 classes.'), 150 classes.'),
110 'W0233': ('__init__ method from a non direct base class %r is called', 151 'W0233': ('__init__ method from a non direct base class %r is called',
152 'non-parent-init-called',
111 'Used when an __init__ method is called on a class which is not \ 153 'Used when an __init__ method is called on a class which is not \
112 in the direct ancestors for the analysed class.'), 154 in the direct ancestors for the analysed class.'),
155 'W0234': ('__iter__ returns non-iterator',
156 'non-iterator-returned',
157 'Used when an __iter__ method returns something which is not an \
158 iterable (i.e. has no `%s` method)' % NEXT_METHOD),
159 'E0235': ('__exit__ must accept 3 arguments: type, value, traceback',
160 'bad-context-manager',
161 'Used when the __exit__ special method, belonging to a \
162 context manager, does not accept 3 arguments \
163 (type, value, traceback).'),
164 'E0236': ('Invalid object %r in __slots__, must contain '
165 'only non empty strings',
166 'invalid-slots-object',
167 'Used when an invalid (non-string) object occurs in __slots__.'),
168 'E0237': ('Assigning to attribute %r not defined in class slots',
169 'assigning-non-slot',
170 'Used when assigning to an attribute not defined '
171 'in the class slots.'),
172 'E0238': ('Invalid __slots__ object',
173 'invalid-slots',
174 'Used when an invalid __slots__ is found in class. '
175 'Only a string, an iterable or a sequence is permitted.')
176
113 177
114 } 178 }
115 179
116 180
117 class ClassChecker(BaseChecker): 181 class ClassChecker(BaseChecker):
118 """checks for : 182 """checks for :
119 * methods without self as first argument 183 * methods without self as first argument
120 * overridden methods signature 184 * overridden methods signature
121 * access only to existent members via self 185 * access only to existent members via self
122 * attributes not defined in the __init__ method 186 * attributes not defined in the __init__ method
123 * supported interfaces implementation 187 * supported interfaces implementation
124 * unreachable code 188 * unreachable code
125 """ 189 """
126 190
127 __implements__ = (IASTNGChecker,) 191 __implements__ = (IAstroidChecker,)
128 192
129 # configuration section name 193 # configuration section name
130 name = 'classes' 194 name = 'classes'
131 # messages 195 # messages
132 msgs = MSGS 196 msgs = MSGS
133 priority = -2 197 priority = -2
134 # configuration options 198 # configuration options
135 options = (('ignore-iface-methods', 199 options = (('ignore-iface-methods',
136 {'default' : (#zope interface 200 {'default' : (#zope interface
137 'isImplementedBy', 'deferred', 'extends', 'names', 201 'isImplementedBy', 'deferred', 'extends', 'names',
138 'namesAndDescriptions', 'queryDescriptionFor', 'getBases', 202 'namesAndDescriptions', 'queryDescriptionFor', 'getBases',
139 'getDescriptionFor', 'getDoc', 'getName', 'getTaggedValue', 203 'getDescriptionFor', 'getDoc', 'getName', 'getTaggedValue',
140 'getTaggedValueTags', 'isEqualOrExtendedBy', 'setTaggedValue', 204 'getTaggedValueTags', 'isEqualOrExtendedBy', 'setTaggedValue ',
141 'isImplementedByInstancesOf', 205 'isImplementedByInstancesOf',
142 # twisted 206 # twisted
143 'adaptWith', 207 'adaptWith',
144 # logilab.common interface 208 # logilab.common interface
145 'is_implemented_by'), 209 'is_implemented_by'),
146 'type' : 'csv', 210 'type' : 'csv',
147 'metavar' : '<method names>', 211 'metavar' : '<method names>',
148 'help' : 'List of interface methods to ignore, \ 212 'help' : 'List of interface methods to ignore, \
149 separated by a comma. This is used for instance to not check methods defines \ 213 separated by a comma. This is used for instance to not check methods defines \
150 in Zope\'s Interface base class.'} 214 in Zope\'s Interface base class.'}
151 ), 215 ),
152
153 ('defining-attr-methods', 216 ('defining-attr-methods',
154 {'default' : ('__init__', '__new__', 'setUp'), 217 {'default' : ('__init__', '__new__', 'setUp'),
155 'type' : 'csv', 218 'type' : 'csv',
156 'metavar' : '<method names>', 219 'metavar' : '<method names>',
157 'help' : 'List of method names used to declare (i.e. assign) \ 220 'help' : 'List of method names used to declare (i.e. assign) \
158 instance attributes.'} 221 instance attributes.'}
159 ), 222 ),
160 ('valid-classmethod-first-arg', 223 ('valid-classmethod-first-arg',
161 {'default' : ('cls',), 224 {'default' : ('cls',),
162 'type' : 'csv', 225 'type' : 'csv',
163 'metavar' : '<argument names>', 226 'metavar' : '<argument names>',
164 'help' : 'List of valid names for the first argument in \ 227 'help' : 'List of valid names for the first argument in \
165 a class method.'} 228 a class method.'}
166 ), 229 ),
167 230 ('valid-metaclass-classmethod-first-arg',
168 ) 231 {'default' : ('mcs',),
232 'type' : 'csv',
233 'metavar' : '<argument names>',
234 'help' : 'List of valid names for the first argument in \
235 a metaclass class method.'}
236 ),
237 )
169 238
170 def __init__(self, linter=None): 239 def __init__(self, linter=None):
171 BaseChecker.__init__(self, linter) 240 BaseChecker.__init__(self, linter)
172 self._accessed = [] 241 self._accessed = []
173 self._first_attrs = [] 242 self._first_attrs = []
174 self._meth_could_be_func = None 243 self._meth_could_be_func = None
175 244
176 def visit_class(self, node): 245 def visit_class(self, node):
177 """init visit variable _accessed and check interfaces 246 """init visit variable _accessed and check interfaces
178 """ 247 """
179 self._accessed.append({}) 248 self._accessed.append({})
180 self._check_bases_classes(node) 249 self._check_bases_classes(node)
181 self._check_interfaces(node) 250 self._check_interfaces(node)
182 # if not an interface, exception, metaclass 251 # if not an interface, exception, metaclass
183 if node.type == 'class': 252 if node.type == 'class':
184 try: 253 try:
185 node.local_attr('__init__') 254 node.local_attr('__init__')
186 except astng.NotFoundError: 255 except astroid.NotFoundError:
187 self.add_message('W0232', args=node, node=node) 256 self.add_message('no-init', args=node, node=node)
257 self._check_slots(node)
188 258
189 @check_messages('E0203', 'W0201') 259 @check_messages('access-member-before-definition', 'attribute-defined-outsid e-init')
190 def leave_class(self, cnode): 260 def leave_class(self, cnode):
191 """close a class node: 261 """close a class node:
192 check that instance attributes are defined in __init__ and check 262 check that instance attributes are defined in __init__ and check
193 access to existent members 263 access to existent members
194 """ 264 """
195 # check access to existent members on non metaclass classes 265 # check access to existent members on non metaclass classes
196 accessed = self._accessed.pop() 266 accessed = self._accessed.pop()
197 if cnode.type != 'metaclass': 267 if cnode.type != 'metaclass':
198 self._check_accessed_members(cnode, accessed) 268 self._check_accessed_members(cnode, accessed)
199 # checks attributes are defined in an allowed method such as __init__ 269 # checks attributes are defined in an allowed method such as __init__
200 if 'W0201' not in self.active_msgs: 270 if not self.linter.is_message_enabled('attribute-defined-outside-init'):
201 return 271 return
202 defining_methods = self.config.defining_attr_methods 272 defining_methods = self.config.defining_attr_methods
203 for attr, nodes in cnode.instance_attrs.items(): 273 current_module = cnode.root()
274 for attr, nodes in cnode.instance_attrs.iteritems():
275 # skip nodes which are not in the current module and it may screw up
276 # the output, while it's not worth it
204 nodes = [n for n in nodes if not 277 nodes = [n for n in nodes if not
205 isinstance(n.statement(), (astng.Delete, astng.AugAssign))] 278 isinstance(n.statement(), (astroid.Delete, astroid.AugAssig n))
279 and n.root() is current_module]
206 if not nodes: 280 if not nodes:
207 continue # error detected by typechecking 281 continue # error detected by typechecking
208 attr_defined = False
209 # check if any method attr is defined in is a defining method 282 # check if any method attr is defined in is a defining method
210 for node in nodes: 283 if any(node.frame().name in defining_methods
211 if node.frame().name in defining_methods: 284 for node in nodes):
212 attr_defined = True 285 continue
213 if not attr_defined: 286
214 # check attribute is defined in a parent's __init__ 287 # check attribute is defined in a parent's __init__
215 for parent in cnode.instance_attr_ancestors(attr): 288 for parent in cnode.instance_attr_ancestors(attr):
216 attr_defined = False 289 attr_defined = False
217 # check if any parent method attr is defined in is a definin g method 290 # check if any parent method attr is defined in is a defining me thod
218 for node in parent.instance_attrs[attr]: 291 for node in parent.instance_attrs[attr]:
219 if node.frame().name in defining_methods: 292 if node.frame().name in defining_methods:
220 attr_defined = True 293 attr_defined = True
221 if attr_defined: 294 if attr_defined:
222 # we're done :) 295 # we're done :)
223 break 296 break
224 else: 297 else:
225 # check attribute is defined as a class attribute 298 # check attribute is defined as a class attribute
226 try: 299 try:
227 cnode.local_attr(attr) 300 cnode.local_attr(attr)
228 except astng.NotFoundError: 301 except astroid.NotFoundError:
229 self.add_message('W0201', args=attr, node=node) 302 for node in nodes:
303 if node.frame().name not in defining_methods:
304 self.add_message('attribute-defined-outside-init',
305 args=attr, node=node)
230 306
231 def visit_function(self, node): 307 def visit_function(self, node):
232 """check method arguments, overriding""" 308 """check method arguments, overriding"""
233 # ignore actual functions 309 # ignore actual functions
234 if not node.is_method(): 310 if not node.is_method():
235 return 311 return
236 klass = node.parent.frame() 312 klass = node.parent.frame()
237 self._meth_could_be_func = True 313 self._meth_could_be_func = True
238 # check first argument is self if this is actually a method 314 # check first argument is self if this is actually a method
239 self._check_first_arg_for_type(node, klass.type == 'metaclass') 315 self._check_first_arg_for_type(node, klass.type == 'metaclass')
240 if node.name == '__init__': 316 if node.name == '__init__':
241 self._check_init(node) 317 self._check_init(node)
242 return 318 return
243 # check signature if the method overloads inherited method 319 # check signature if the method overloads inherited method
244 for overridden in klass.local_attr_ancestors(node.name): 320 for overridden in klass.local_attr_ancestors(node.name):
245 # get astng for the searched method 321 # get astroid for the searched method
246 try: 322 try:
247 meth_node = overridden[node.name] 323 meth_node = overridden[node.name]
248 except KeyError: 324 except KeyError:
249 # we have found the method but it's not in the local 325 # we have found the method but it's not in the local
250 # dictionary. 326 # dictionary.
251 # This may happen with astng build from living objects 327 # This may happen with astroid build from living objects
252 continue 328 continue
253 if not isinstance(meth_node, astng.Function): 329 if not isinstance(meth_node, astroid.Function):
254 continue 330 continue
255 self._check_signature(node, meth_node, 'overridden') 331 self._check_signature(node, meth_node, 'overridden')
256 break 332 break
257 # check if the method overload an attribute 333 if node.decorators:
334 for decorator in node.decorators.nodes:
335 if isinstance(decorator, astroid.Getattr) and \
336 decorator.attrname in ('getter', 'setter', 'deleter'):
337 # attribute affectation will call this method, not hiding it
338 return
339 if isinstance(decorator, astroid.Name) and decorator.name == 'pr operty':
340 # attribute affectation will either call a setter or raise
341 # an attribute error, anyway not hiding the function
342 return
343 # check if the method is hidden by an attribute
258 try: 344 try:
259 overridden = klass.instance_attr(node.name)[0] # XXX 345 overridden = klass.instance_attr(node.name)[0] # XXX
260 args = (overridden.root().name, overridden.fromlineno) 346 overridden_frame = overridden.frame()
261 self.add_message('E0202', args=args, node=node) 347 if (isinstance(overridden_frame, astroid.Function)
262 except astng.NotFoundError: 348 and overridden_frame.type == 'method'):
349 overridden_frame = overridden_frame.parent.frame()
350 if (isinstance(overridden_frame, Class)
351 and klass._is_subtype_of(overridden_frame.qname())):
352 args = (overridden.root().name, overridden.fromlineno)
353 self.add_message('method-hidden', args=args, node=node)
354 except astroid.NotFoundError:
263 pass 355 pass
264 356
357 # check non-iterators in __iter__
358 if node.name == '__iter__':
359 self._check_iter(node)
360 elif node.name == '__exit__':
361 self._check_exit(node)
362
363 def _check_slots(self, node):
364 if '__slots__' not in node.locals:
365 return
366 for slots in node.igetattr('__slots__'):
367 # check if __slots__ is a valid type
368 for meth in ITER_METHODS:
369 try:
370 slots.getattr(meth)
371 break
372 except astroid.NotFoundError:
373 continue
374 else:
375 self.add_message('invalid-slots', node=node)
376 continue
377
378 if isinstance(slots, astroid.Const):
379 # a string, ignore the following checks
380 continue
381 if not hasattr(slots, 'itered'):
382 # we can't obtain the values, maybe a .deque?
383 continue
384
385 if isinstance(slots, astroid.Dict):
386 values = [item[0] for item in slots.items]
387 else:
388 values = slots.itered()
389 if values is YES:
390 return
391
392 for elt in values:
393 try:
394 self._check_slots_elt(elt)
395 except astroid.InferenceError:
396 continue
397
398 def _check_slots_elt(self, elt):
399 for infered in elt.infer():
400 if infered is YES:
401 continue
402 if (not isinstance(infered, astroid.Const) or
403 not isinstance(infered.value, str)):
404 self.add_message('invalid-slots-object',
405 args=infered.as_string(),
406 node=elt)
407 continue
408 if not infered.value:
409 self.add_message('invalid-slots-object',
410 args=infered.as_string(),
411 node=elt)
412
413 def _check_iter(self, node):
414 try:
415 infered = node.infer_call_result(node)
416 except astroid.InferenceError:
417 return
418
419 for infered_node in infered:
420 if (infered_node is YES
421 or isinstance(infered_node, Generator)):
422 continue
423 if isinstance(infered_node, astroid.Instance):
424 try:
425 infered_node.local_attr(NEXT_METHOD)
426 except astroid.NotFoundError:
427 self.add_message('non-iterator-returned',
428 node=node)
429 break
430
431 def _check_exit(self, node):
432 positional = sum(1 for arg in node.args.args if arg.name != 'self')
433 if positional < 3 and not node.args.vararg:
434 self.add_message('bad-context-manager',
435 node=node)
436 elif positional > 3:
437 self.add_message('bad-context-manager',
438 node=node)
439
265 def leave_function(self, node): 440 def leave_function(self, node):
266 """on method node, check if this method couldn't be a function 441 """on method node, check if this method couldn't be a function
267 442
268 ignore class, static and abstract methods, initializer, 443 ignore class, static and abstract methods, initializer,
269 methods overridden from a parent class and any 444 methods overridden from a parent class and any
270 kind of method defined in an interface for this warning 445 kind of method defined in an interface for this warning
271 """ 446 """
272 if node.is_method(): 447 if node.is_method():
273 if node.args.args is not None: 448 if node.args.args is not None:
274 self._first_attrs.pop() 449 self._first_attrs.pop()
275 if 'R0201' not in self.active_msgs: 450 if not self.linter.is_message_enabled('no-self-use'):
276 return 451 return
277 class_node = node.parent.frame() 452 class_node = node.parent.frame()
278 if (self._meth_could_be_func and node.type == 'method' 453 if (self._meth_could_be_func and node.type == 'method'
279 and not node.name in PYMETHODS 454 and not node.name in PYMETHODS
280 and not (node.is_abstract() or 455 and not (node.is_abstract() or
281 overrides_a_method(class_node, node.name)) 456 overrides_a_method(class_node, node.name))
282 and class_node.type != 'interface'): 457 and class_node.type != 'interface'):
283 self.add_message('R0201', node=node) 458 self.add_message('no-self-use', node=node)
284 459
285 def visit_getattr(self, node): 460 def visit_getattr(self, node):
286 """check if the getattr is an access to a class member 461 """check if the getattr is an access to a class member
287 if so, register it. Also check for access to protected 462 if so, register it. Also check for access to protected
288 class member from outside its class (but ignore __special__ 463 class member from outside its class (but ignore __special__
289 methods) 464 methods)
290 """ 465 """
291 attrname = node.attrname 466 attrname = node.attrname
292 if self._first_attrs and isinstance(node.expr, astng.Name) and \ 467 # Check self
293 node.expr.name == self._first_attrs[-1]: 468 if self.is_first_attr(node):
294 self._accessed[-1].setdefault(attrname, []).append(node) 469 self._accessed[-1].setdefault(attrname, []).append(node)
295 return 470 return
296 if 'W0212' not in self.active_msgs: 471 if not self.linter.is_message_enabled('protected-access'):
297 return 472 return
298 if attrname[0] == '_' and not attrname == '_' and not ( 473
299 attrname.startswith('__') and attrname.endswith('__')): 474 self._check_protected_attribute_access(node)
300 # XXX move this in a reusable function 475
301 klass = node.frame() 476 def visit_assattr(self, node):
302 while klass is not None and not isinstance(klass, astng.Class): 477 if isinstance(node.ass_type(), astroid.AugAssign) and self.is_first_attr (node):
303 if klass.parent is None: 478 self._accessed[-1].setdefault(node.attrname, []).append(node)
304 klass = None 479 self._check_in_slots(node)
305 else: 480
306 klass = klass.parent.frame() 481 def _check_in_slots(self, node):
482 """ Check that the given assattr node
483 is defined in the class slots.
484 """
485 infered = safe_infer(node.expr)
486 if infered and isinstance(infered, Instance):
487 klass = infered._proxied
488 if '__slots__' not in klass.locals or not klass.newstyle:
489 return
490
491 slots = klass.slots()
492 # If any ancestor doesn't use slots, the slots
493 # defined for this class are superfluous.
494 if any('__slots__' not in ancestor.locals and
495 ancestor.name != 'object'
496 for ancestor in klass.ancestors()):
497 return
498
499 if not any(slot.value == node.attrname for slot in slots):
500 # If we have a '__dict__' in slots, then
501 # assigning any name is valid.
502 if not any(slot.value == '__dict__' for slot in slots):
503 self.add_message('assigning-non-slot',
504 args=(node.attrname, ), node=node)
505
506 @check_messages('protected-access')
507 def visit_assign(self, assign_node):
508 node = assign_node.targets[0]
509 if not isinstance(node, AssAttr):
510 return
511
512 if self.is_first_attr(node):
513 return
514
515 self._check_protected_attribute_access(node)
516
517 def _check_protected_attribute_access(self, node):
518 '''Given an attribute access node (set or get), check if attribute
519 access is legitimate. Call _check_first_attr with node before calling
520 this method. Valid cases are:
521 * self._attr in a method or cls._attr in a classmethod. Checked by
522 _check_first_attr.
523 * Klass._attr inside "Klass" class.
524 * Klass2._attr inside "Klass" class when Klass2 is a base class of
525 Klass.
526 '''
527 attrname = node.attrname
528
529 if is_attr_protected(attrname):
530
531 klass = node_frame_class(node)
532
307 # XXX infer to be more safe and less dirty ?? 533 # XXX infer to be more safe and less dirty ??
308 # in classes, check we are not getting a parent method 534 # in classes, check we are not getting a parent method
309 # through the class object or through super 535 # through the class object or through super
310 callee = node.expr.as_string() 536 callee = node.expr.as_string()
311 if klass is None or not (callee == klass.name or
312 callee in klass.basenames
313 or (isinstance(node.expr, astng.CallFunc)
314 and isinstance(node.expr.func, astng.Name)
315 and node.expr.func.name == 'super')):
316 self.add_message('W0212', node=node, args=attrname)
317 537
538 # We are not in a class, no remaining valid case
539 if klass is None:
540 self.add_message('protected-access', node=node, args=attrname)
541 return
542
543 # If the expression begins with a call to super, that's ok.
544 if isinstance(node.expr, astroid.CallFunc) and \
545 isinstance(node.expr.func, astroid.Name) and \
546 node.expr.func.name == 'super':
547 return
548
549 # We are in a class, one remaining valid cases, Klass._attr inside
550 # Klass
551 if not (callee == klass.name or callee in klass.basenames):
552 self.add_message('protected-access', node=node, args=attrname)
318 553
319 def visit_name(self, node): 554 def visit_name(self, node):
320 """check if the name handle an access to a class member 555 """check if the name handle an access to a class member
321 if so, register it 556 if so, register it
322 """ 557 """
323 if self._first_attrs and (node.name == self._first_attrs[-1] or 558 if self._first_attrs and (node.name == self._first_attrs[-1] or
324 not self._first_attrs[-1]): 559 not self._first_attrs[-1]):
325 self._meth_could_be_func = False 560 self._meth_could_be_func = False
326 561
327 def _check_accessed_members(self, node, accessed): 562 def _check_accessed_members(self, node, accessed):
328 """check that accessed members are defined""" 563 """check that accessed members are defined"""
329 # XXX refactor, probably much simpler now that E0201 is in type checker 564 # XXX refactor, probably much simpler now that E0201 is in type checker
330 for attr, nodes in accessed.items(): 565 for attr, nodes in accessed.iteritems():
331 # deactivate "except doesn't do anything", that's expected 566 # deactivate "except doesn't do anything", that's expected
332 # pylint: disable=W0704 567 # pylint: disable=W0704
333 # is it a class attribute ?
334 try: 568 try:
569 # is it a class attribute ?
335 node.local_attr(attr) 570 node.local_attr(attr)
336 # yes, stop here 571 # yes, stop here
337 continue 572 continue
338 except astng.NotFoundError: 573 except astroid.NotFoundError:
339 pass 574 pass
340 # is it an instance attribute of a parent class ? 575 # is it an instance attribute of a parent class ?
341 try: 576 try:
342 node.instance_attr_ancestors(attr).next() 577 node.instance_attr_ancestors(attr).next()
343 # yes, stop here 578 # yes, stop here
344 continue 579 continue
345 except StopIteration: 580 except StopIteration:
346 pass 581 pass
347 # is it an instance attribute ? 582 # is it an instance attribute ?
348 try: 583 try:
349 defstmts = node.instance_attr(attr) 584 defstmts = node.instance_attr(attr)
350 except astng.NotFoundError: 585 except astroid.NotFoundError:
351 pass 586 pass
352 else: 587 else:
588 # filter out augment assignment nodes
589 defstmts = [stmt for stmt in defstmts if stmt not in nodes]
590 if not defstmts:
591 # only augment assignment for this node, no-member should be
592 # triggered by the typecheck checker
593 continue
594 # filter defstmts to only pick the first one when there are
595 # several assignments in the same scope
596 scope = defstmts[0].scope()
597 defstmts = [stmt for i, stmt in enumerate(defstmts)
598 if i == 0 or stmt.scope() is not scope]
599 # if there are still more than one, don't attempt to be smarter
600 # than we can be
353 if len(defstmts) == 1: 601 if len(defstmts) == 1:
354 defstmt = defstmts[0] 602 defstmt = defstmts[0]
355 # check that if the node is accessed in the same method as 603 # check that if the node is accessed in the same method as
356 # it's defined, it's accessed after the initial assignment 604 # it's defined, it's accessed after the initial assignment
357 frame = defstmt.frame() 605 frame = defstmt.frame()
358 lno = defstmt.fromlineno 606 lno = defstmt.fromlineno
359 for _node in nodes: 607 for _node in nodes:
360 if _node.frame() is frame and _node.fromlineno < lno \ 608 if _node.frame() is frame and _node.fromlineno < lno \
361 and not are_exclusive(_node.statement(), defstmt, ('A ttributeError', 'Exception', 'BaseException')): 609 and not are_exclusive(_node.statement(), defstmt, ('A ttributeError', 'Exception', 'BaseException')):
362 self.add_message('E0203', node=_node, 610 self.add_message('access-member-before-definition',
363 args=(attr, lno)) 611 node=_node, args=(attr, lno))
364 612
365 def _check_first_arg_for_type(self, node, metaclass=0): 613 def _check_first_arg_for_type(self, node, metaclass=0):
366 """check the name of first argument, expect: 614 """check the name of first argument, expect:
367 615
368 * 'self' for a regular method 616 * 'self' for a regular method
369 * 'cls' for a class method 617 * 'cls' for a class method or a metaclass regular method (actually
370 * 'mcs' for a metaclass 618 valid-classmethod-first-arg value)
619 * 'mcs' for a metaclass class method (actually
620 valid-metaclass-classmethod-first-arg)
371 * not one of the above for a static method 621 * not one of the above for a static method
372 """ 622 """
373 # don't care about functions with unknown argument (builtins) 623 # don't care about functions with unknown argument (builtins)
374 if node.args.args is None: 624 if node.args.args is None:
375 return 625 return
376 first_arg = node.args.args and node.argnames()[0] 626 first_arg = node.args.args and node.argnames()[0]
377 self._first_attrs.append(first_arg) 627 self._first_attrs.append(first_arg)
378 first = self._first_attrs[-1] 628 first = self._first_attrs[-1]
379 # static method 629 # static method
380 if node.type == 'staticmethod': 630 if node.type == 'staticmethod':
381 if first_arg in ('self', 'cls', 'mcs'): 631 if (first_arg == 'self' or
382 self.add_message('W0211', args=first, node=node) 632 first_arg in self.config.valid_classmethod_first_arg or
633 first_arg in self.config.valid_metaclass_classmethod_first_a rg):
634 self.add_message('bad-staticmethod-argument', args=first, node=n ode)
635 return
383 self._first_attrs[-1] = None 636 self._first_attrs[-1] = None
384 # class / regular method with no args 637 # class / regular method with no args
385 elif not node.args.args: 638 elif not node.args.args:
386 self.add_message('E0211', node=node) 639 self.add_message('no-method-argument', node=node)
387 # metaclass method 640 # metaclass
388 elif metaclass: 641 elif metaclass:
389 if first != 'mcs': 642 # metaclass __new__ or classmethod
390 self.add_message('C0203', node=node) 643 if node.type == 'classmethod':
391 # class method 644 self._check_first_arg_config(
392 elif node.type == 'classmethod': 645 first,
393 if first not in self.config.valid_classmethod_first_arg: 646 self.config.valid_metaclass_classmethod_first_arg, node,
394 if len(self.config.valid_classmethod_first_arg) == 1: 647 'bad-mcs-classmethod-argument', node.name)
395 valid = repr(self.config.valid_classmethod_first_arg[0]) 648 # metaclass regular method
396 else: 649 else:
397 valid = ', '.join( 650 self._check_first_arg_config(
398 repr(v) 651 first,
399 for v in self.config.valid_classmethod_first_arg[:-1]) 652 self.config.valid_classmethod_first_arg, node, 'bad-mcs-meth od-argument',
400 valid = '%s or %r' % ( 653 node.name)
401 valid, self.config.valid_classmethod_first_arg[-1]) 654 # regular class
402 self.add_message('C0202', args=valid, node=node) 655 else:
403 # regular method without self as argument 656 # class method
404 elif first != 'self': 657 if node.type == 'classmethod':
405 self.add_message('E0213', node=node) 658 self._check_first_arg_config(
659 first,
660 self.config.valid_classmethod_first_arg, node, 'bad-classmet hod-argument',
661 node.name)
662 # regular method without self as argument
663 elif first != 'self':
664 self.add_message('no-self-argument', node=node)
665
666 def _check_first_arg_config(self, first, config, node, message,
667 method_name):
668 if first not in config:
669 if len(config) == 1:
670 valid = repr(config[0])
671 else:
672 valid = ', '.join(repr(v) for v in config[:-1])
673 valid = '%s or %r' % (valid, config[-1])
674 self.add_message(message, args=(method_name, valid), node=node)
406 675
407 def _check_bases_classes(self, node): 676 def _check_bases_classes(self, node):
408 """check that the given class node implements abstract methods from 677 """check that the given class node implements abstract methods from
409 base classes 678 base classes
410 """ 679 """
411 # check if this class abstract 680 # check if this class abstract
412 if class_is_abstract(node): 681 if class_is_abstract(node):
413 return 682 return
414 for method in node.methods(): 683 for method in node.methods():
415 owner = method.parent.frame() 684 owner = method.parent.frame()
416 if owner is node: 685 if owner is node:
417 continue 686 continue
418 # owner is not this class, it must be a parent class 687 # owner is not this class, it must be a parent class
419 # check that the ancestor's method is not abstract 688 # check that the ancestor's method is not abstract
689 if method.name in node.locals:
690 # it is redefined as an attribute or with a descriptor
691 continue
420 if method.is_abstract(pass_is_abstract=False): 692 if method.is_abstract(pass_is_abstract=False):
421 self.add_message('W0223', node=node, 693 self.add_message('abstract-method', node=node,
422 args=(method.name, owner.name)) 694 args=(method.name, owner.name))
423 695
424 def _check_interfaces(self, node): 696 def _check_interfaces(self, node):
425 """check that the given class node really implements declared 697 """check that the given class node really implements declared
426 interfaces 698 interfaces
427 """ 699 """
428 e0221_hack = [False] 700 e0221_hack = [False]
429 def iface_handler(obj): 701 def iface_handler(obj):
430 """filter interface objects, it should be classes""" 702 """filter interface objects, it should be classes"""
431 if not isinstance(obj, astng.Class): 703 if not isinstance(obj, astroid.Class):
432 e0221_hack[0] = True 704 e0221_hack[0] = True
433 self.add_message('E0221', node=node, 705 self.add_message('interface-is-not-class', node=node,
434 args=(obj.as_string(),)) 706 args=(obj.as_string(),))
435 return False 707 return False
436 return True 708 return True
437 ignore_iface_methods = self.config.ignore_iface_methods 709 ignore_iface_methods = self.config.ignore_iface_methods
438 try: 710 try:
439 for iface in node.interfaces(handler_func=iface_handler): 711 for iface in node.interfaces(handler_func=iface_handler):
440 for imethod in iface.methods(): 712 for imethod in iface.methods():
441 name = imethod.name 713 name = imethod.name
442 if name.startswith('_') or name in ignore_iface_methods: 714 if name.startswith('_') or name in ignore_iface_methods:
443 # don't check method beginning with an underscore, 715 # don't check method beginning with an underscore,
444 # usually belonging to the interface implementation 716 # usually belonging to the interface implementation
445 continue 717 continue
446 # get class method astng 718 # get class method astroid
447 try: 719 try:
448 method = node_method(node, name) 720 method = node_method(node, name)
449 except astng.NotFoundError: 721 except astroid.NotFoundError:
450 self.add_message('E0222', args=(name, iface.name), 722 self.add_message('missing-interface-method', args=(name, iface.name),
451 node=node) 723 node=node)
452 continue 724 continue
453 # ignore inherited methods 725 # ignore inherited methods
454 if method.parent.frame() is not node: 726 if method.parent.frame() is not node:
455 continue 727 continue
456 # check signature 728 # check signature
457 self._check_signature(method, imethod, 729 self._check_signature(method, imethod,
458 '%s interface' % iface.name) 730 '%s interface' % iface.name)
459 except astng.InferenceError: 731 except astroid.InferenceError:
460 if e0221_hack[0]: 732 if e0221_hack[0]:
461 return 733 return
462 implements = Instance(node).getattr('__implements__')[0] 734 implements = Instance(node).getattr('__implements__')[0]
463 assignment = implements.parent 735 assignment = implements.parent
464 assert isinstance(assignment, astng.Assign) 736 assert isinstance(assignment, astroid.Assign)
465 # assignment.expr can be a Name or a Tuple or whatever. 737 # assignment.expr can be a Name or a Tuple or whatever.
466 # Use as_string() for the message 738 # Use as_string() for the message
467 # FIXME: in case of multiple interfaces, find which one could not 739 # FIXME: in case of multiple interfaces, find which one could not
468 # be resolved 740 # be resolved
469 self.add_message('F0220', node=implements, 741 self.add_message('unresolved-interface', node=implements,
470 args=(node.name, assignment.value.as_string())) 742 args=(node.name, assignment.value.as_string()))
471 743
472 def _check_init(self, node): 744 def _check_init(self, node):
473 """check that the __init__ method call super or ancestors'__init__ 745 """check that the __init__ method call super or ancestors'__init__
474 method 746 method
475 """ 747 """
476 if not set(('W0231', 'W0233')) & self.active_msgs: 748 if (not self.linter.is_message_enabled('super-init-not-called') and
749 not self.linter.is_message_enabled('non-parent-init-called')):
477 return 750 return
478 klass_node = node.parent.frame() 751 klass_node = node.parent.frame()
479 to_call = _ancestors_to_call(klass_node) 752 to_call = _ancestors_to_call(klass_node)
480 not_called_yet = dict(to_call) 753 not_called_yet = dict(to_call)
481 for stmt in node.nodes_of_class(astng.CallFunc): 754 for stmt in node.nodes_of_class(astroid.CallFunc):
482 expr = stmt.func 755 expr = stmt.func
483 if not isinstance(expr, astng.Getattr) \ 756 if not isinstance(expr, astroid.Getattr) \
484 or expr.attrname != '__init__': 757 or expr.attrname != '__init__':
485 continue 758 continue
486 # skip the test if using super 759 # skip the test if using super
487 if isinstance(expr.expr, astng.CallFunc) and \ 760 if isinstance(expr.expr, astroid.CallFunc) and \
488 isinstance(expr.expr.func, astng.Name) and \ 761 isinstance(expr.expr.func, astroid.Name) and \
489 expr.expr.func.name == 'super': 762 expr.expr.func.name == 'super':
490 return 763 return
491 try: 764 try:
492 klass = expr.expr.infer().next() 765 klass = expr.expr.infer().next()
493 if klass is YES: 766 if klass is YES:
494 continue 767 continue
495 try: 768 try:
496 del not_called_yet[klass] 769 del not_called_yet[klass]
497 except KeyError: 770 except KeyError:
498 if klass not in to_call: 771 if klass not in to_call:
499 self.add_message('W0233', node=expr, args=klass.name) 772 self.add_message('non-parent-init-called', node=expr, ar gs=klass.name)
500 except astng.InferenceError: 773 except astroid.InferenceError:
501 continue 774 continue
502 for klass in not_called_yet.keys(): 775 for klass, method in not_called_yet.iteritems():
503 if klass.name == 'object': 776 if klass.name == 'object' or method.parent.name == 'object':
504 continue 777 continue
505 self.add_message('W0231', args=klass.name, node=node) 778 self.add_message('super-init-not-called', args=klass.name, node=node )
506 779
507 def _check_signature(self, method1, refmethod, class_type): 780 def _check_signature(self, method1, refmethod, class_type):
508 """check that the signature of the two given methods match 781 """check that the signature of the two given methods match
509 782
510 class_type is in 'class', 'interface' 783 class_type is in 'class', 'interface'
511 """ 784 """
512 if not (isinstance(method1, astng.Function) 785 if not (isinstance(method1, astroid.Function)
513 and isinstance(refmethod, astng.Function)): 786 and isinstance(refmethod, astroid.Function)):
514 self.add_message('F0202', args=(method1, refmethod), node=method1) 787 self.add_message('method-check-failed', args=(method1, refmethod), n ode=method1)
515 return 788 return
516 # don't care about functions with unknown argument (builtins) 789 # don't care about functions with unknown argument (builtins)
517 if method1.args.args is None or refmethod.args.args is None: 790 if method1.args.args is None or refmethod.args.args is None:
518 return 791 return
519 # if we use *args, **kwargs, skip the below checks 792 # if we use *args, **kwargs, skip the below checks
520 if method1.args.vararg or method1.args.kwarg: 793 if method1.args.vararg or method1.args.kwarg:
521 return 794 return
795 if is_attr_private(method1.name):
796 return
522 if len(method1.args.args) != len(refmethod.args.args): 797 if len(method1.args.args) != len(refmethod.args.args):
523 self.add_message('W0221', args=class_type, node=method1) 798 self.add_message('arguments-differ', args=class_type, node=method1)
524 elif len(method1.args.defaults) < len(refmethod.args.defaults): 799 elif len(method1.args.defaults) < len(refmethod.args.defaults):
525 self.add_message('W0222', args=class_type, node=method1) 800 self.add_message('signature-differs', args=class_type, node=method1)
526 801
802 def is_first_attr(self, node):
803 """Check that attribute lookup name use first attribute variable name
804 (self for method, cls for classmethod and mcs for metaclass).
805 """
806 return self._first_attrs and isinstance(node.expr, astroid.Name) and \
807 node.expr.name == self._first_attrs[-1]
527 808
528 def _ancestors_to_call(klass_node, method='__init__'): 809 def _ancestors_to_call(klass_node, method='__init__'):
529 """return a dictionary where keys are the list of base classes providing 810 """return a dictionary where keys are the list of base classes providing
530 the queried method, and so that should/may be called from the method node 811 the queried method, and so that should/may be called from the method node
531 """ 812 """
532 to_call = {} 813 to_call = {}
533 for base_node in klass_node.ancestors(recurs=False): 814 for base_node in klass_node.ancestors(recurs=False):
534 try: 815 try:
535 base_node.local_attr(method) 816 to_call[base_node] = base_node.igetattr(method).next()
536 to_call[base_node] = 1 817 except astroid.InferenceError:
537 except astng.NotFoundError:
538 continue 818 continue
539 return to_call 819 return to_call
540 820
541 821
542 def node_method(node, method_name): 822 def node_method(node, method_name):
543 """get astng for <method_name> on the given class node, ensuring it 823 """get astroid for <method_name> on the given class node, ensuring it
544 is a Function node 824 is a Function node
545 """ 825 """
546 for n in node.local_attr(method_name): 826 for n in node.local_attr(method_name):
547 if isinstance(n, astng.Function): 827 if isinstance(n, astroid.Function):
548 return n 828 return n
549 raise astng.NotFoundError(method_name) 829 raise astroid.NotFoundError(method_name)
550 830
551 def register(linter): 831 def register(linter):
552 """required method to auto register this checker """ 832 """required method to auto register this checker """
553 linter.register_checker(ClassChecker(linter)) 833 linter.register_checker(ClassChecker(linter))
OLDNEW
« no previous file with comments | « third_party/pylint/checkers/base.py ('k') | third_party/pylint/checkers/design_analysis.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698