~ubuntu-branches/ubuntu/vivid/pylint/vivid-proposed

« back to all changes in this revision

Viewing changes to checkers/variables.py

  • Committer: Bazaar Package Importer
  • Author(s): Sandro Tosi
  • Date: 2011-03-29 19:27:47 UTC
  • mfrom: (1.3.1 upstream) (12.1.3 experimental)
  • Revision ID: james.westby@ubuntu.com-20110329192747-fwcizgk9vg1wi0tv
Tags: 0.23.0-1
* New upstream release
  - provides manpages for epylint, pylint-gui, symilar; Closes: #575679
* debian/control
  - mention pyreverse, symilar, epylint, pylint-gui additional commands in
    long description
  - update versioned Depends and b-d-i on logilab-{common, astng}
  - added python-unittest2 to b-d-i, needed to run tests
* debian/pylint.manpages
  - install all the available manpages

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
"""variables checkers for Python code
17
17
"""
18
18
 
 
19
import sys
19
20
from copy import copy
20
21
 
21
 
from logilab.common.compat import enumerate
22
22
from logilab import astng
23
 
from logilab.astng import are_exclusive, builtin_lookup
 
23
from logilab.astng import are_exclusive, builtin_lookup, ASTNGBuildingException
24
24
 
25
25
from pylint.interfaces import IASTNGChecker
26
26
from pylint.checkers import BaseChecker
27
 
from pylint.checkers.utils import PYMETHODS, is_ancestor_name, is_builtin, \
28
 
     is_defined_before, is_error, is_func_default, is_func_decorator, assign_parent
29
 
 
 
27
from pylint.checkers.utils import (PYMETHODS, is_ancestor_name, is_builtin,
 
28
     is_defined_before, is_error, is_func_default, is_func_decorator,
 
29
     assign_parent, check_messages)
30
30
 
31
31
def overridden_method(klass, name):
32
32
    """get overridden method if any"""
111
111
               ("dummy-variables-rgx",
112
112
                {'default': ('_|dummy'),
113
113
                 'type' :'regexp', 'metavar' : '<regexp>',
114
 
                 'help' : 'A regular expression matching names used \
115
 
                 for dummy variables (i.e. not used).'}),
 
114
                 'help' : 'A regular expression matching the beginning of \
 
115
                  the name of dummy variables (i.e. not used).'}),
116
116
               ("additional-builtins",
117
117
                {'default': (), 'type' : 'csv',
118
118
                 'metavar' : '<comma separated list>',
137
137
                # do not print Redefining builtin for additional builtins
138
138
                self.add_message('W0622', args=name, node=stmts[0])
139
139
 
 
140
    @check_messages('W0611', 'W0614')
140
141
    def leave_module(self, node):
141
142
        """leave module: check globals
142
143
        """
182
183
    def visit_genexpr(self, node):
183
184
        """visit genexpr: update consumption analysis variable
184
185
        """
185
 
        self._to_consume.append((copy(node.locals), {}, 'genexpr'))
 
186
        self._to_consume.append((copy(node.locals), {}, 'comprehension'))
186
187
 
187
188
    def leave_genexpr(self, _):
188
189
        """leave genexpr: update consumption analysis variable
190
191
        # do not check for not used locals here
191
192
        self._to_consume.pop()
192
193
 
 
194
    def visit_dictcomp(self, node):
 
195
        """visit dictcomp: update consumption analysis variable
 
196
        """
 
197
        self._to_consume.append((copy(node.locals), {}, 'comprehension'))
 
198
 
 
199
    def leave_dictcomp(self, _):
 
200
        """leave dictcomp: update consumption analysis variable
 
201
        """
 
202
        # do not check for not used locals here
 
203
        self._to_consume.pop()
 
204
 
 
205
    def visit_setcomp(self, node):
 
206
        """visit setcomp: update consumption analysis variable
 
207
        """
 
208
        self._to_consume.append((copy(node.locals), {}, 'comprehension'))
 
209
 
 
210
    def leave_setcomp(self, _):
 
211
        """leave setcomp: update consumption analysis variable
 
212
        """
 
213
        # do not check for not used locals here
 
214
        self._to_consume.pop()
 
215
 
193
216
    def visit_function(self, node):
194
217
        """visit function: update consumption analysis variable and check locals
195
218
        """
 
219
        self._to_consume.append((copy(node.locals), {}, 'function'))
 
220
        self._vars.append({})
 
221
        if not set(('W0621', 'W0622')) & self.active_msgs:
 
222
            return
196
223
        globs = node.root().globals
197
224
        for name, stmt in node.items():
198
 
            if globs.has_key(name) and not isinstance(stmt, astng.Global):
 
225
            if name in globs and not isinstance(stmt, astng.Global):
199
226
                line = globs[name][0].lineno
200
227
                self.add_message('W0621', args=(name, line), node=stmt)
201
228
            elif is_builtin(name):
202
229
                # do not print Redefining builtin for additional builtins
203
230
                self.add_message('W0622', args=name, node=stmt)
204
 
        self._to_consume.append((copy(node.locals), {}, 'function'))
205
 
        self._vars.append({})
206
231
 
207
232
    def leave_function(self, node):
208
233
        """leave function: check function's locals are consumed"""
209
234
        not_consumed = self._to_consume.pop()[0]
210
235
        self._vars.pop(0)
 
236
        if not set(('W0612', 'W0613')) & self.active_msgs:
 
237
            return
211
238
        # don't check arguments of function which are only raising an exception
212
239
        if is_error(node):
213
240
            return
217
244
        if is_method and (klass.type == 'interface' or node.is_abstract()):
218
245
            return
219
246
        authorized_rgx = self.config.dummy_variables_rgx
220
 
        overridden = marker = []
 
247
        called_overridden = False
221
248
        argnames = node.argnames()
222
249
        for name, stmts in not_consumed.iteritems():
223
250
            # ignore some special names specified by user configuration
235
262
                    if node.type != 'staticmethod' and name == argnames[0]:
236
263
                        continue
237
264
                    # don't warn for argument of an overridden method
238
 
                    if overridden is marker:
 
265
                    if not called_overridden:
239
266
                        overridden = overridden_method(klass, node.name)
 
267
                        called_overridden = True
240
268
                    if overridden is not None and name in overridden.argnames():
241
269
                        continue
242
270
                    if node.name in PYMETHODS and node.name not in ('__init__', '__new__'):
248
276
            else:
249
277
                self.add_message('W0612', args=name, node=stmt)
250
278
 
 
279
    @check_messages('W0601', 'W0602', 'W0603', 'W0604', 'W0622')
251
280
    def visit_global(self, node):
252
281
        """check names imported exists in the global scope"""
253
282
        frame = node.frame()
298
327
        #astmts = [stmt for stmt in node.lookup(name)[1]
299
328
        #          if hasattr(stmt, 'ass_type')] and
300
329
        #          not stmt.statement().parent_of(node)]
 
330
        if 'W0631' not in self.active_msgs:
 
331
            return
301
332
        astmts = [stmt for stmt in node.lookup(name)[1]
302
333
                  if hasattr(stmt, 'ass_type')]
303
334
        # filter variables according their respective scope
350
381
            # scope, ignore it. This prevents to access this scope instead of
351
382
            # the globals one in function members when there are some common
352
383
            # names. The only exception is when the starting scope is a
353
 
            # genexpr and its direct outer scope is a class
 
384
            # comprehension and its direct outer scope is a class
354
385
            if scope_type == 'class' and i != start_index and not (
355
 
                base_scope_type == 'genexpr' and i == start_index-1):
 
386
                base_scope_type == 'comprehension' and i == start_index-1):
356
387
                # XXX find a way to handle class scope in a smoother way
357
388
                continue
358
389
            # the name has already been consumed, only check it's not a loop
359
390
            # variable used outside the loop
360
 
            if consumed.has_key(name):
 
391
            if name in consumed:
361
392
                self._loopvar_name(node, name)
362
393
                break
363
394
            # mark the name as consumed if it's defined in this scope
366
397
                consumed[name] = to_consume[name]
367
398
            except KeyError:
368
399
                continue
 
400
            # checks for use before assignment
 
401
            defnode = assign_parent(to_consume[name][0])
 
402
            if defnode is not None:
 
403
                defstmt = defnode.statement()
 
404
                defframe = defstmt.frame()
 
405
                maybee0601 = True
 
406
                if not frame is defframe:
 
407
                    maybee0601 = False
 
408
                elif defframe.parent is None:
 
409
                    # we are at the module level, check the name is not
 
410
                    # defined in builtins
 
411
                    if name in defframe.scope_attrs or builtin_lookup(name)[1]:
 
412
                        maybee0601 = False
 
413
                else:
 
414
                    # we are in a local scope, check the name is not
 
415
                    # defined in global or builtin scope
 
416
                    if defframe.root().lookup(name)[1]:
 
417
                        maybee0601 = False
 
418
                if (maybee0601
 
419
                    and stmt.fromlineno <= defstmt.fromlineno
 
420
                    and not is_defined_before(node)
 
421
                    and not are_exclusive(stmt, defstmt, ('NameError', 'Exception', 'BaseException'))):
 
422
                    if defstmt is stmt and isinstance(node, (astng.DelName,
 
423
                                                             astng.AssName)):
 
424
                        self.add_message('E0602', args=name, node=node)
 
425
                    elif self._to_consume[-1][-1] != 'lambda':
 
426
                        # E0601 may *not* occurs in lambda scope
 
427
                        self.add_message('E0601', args=name, node=node)
 
428
            if not isinstance(node, astng.AssName): # Aug AssName
 
429
                del to_consume[name]
369
430
            else:
370
 
                # checks for use before assignment
371
 
                defnode = assign_parent(to_consume[name][0])
372
 
                if defnode is not None:
373
 
                    defstmt = defnode.statement()
374
 
                    defframe = defstmt.frame()
375
 
                    maybee0601 = True
376
 
                    if not frame is defframe:
377
 
                        maybee0601 = False
378
 
                    elif defframe.parent is None:
379
 
                        # we are at the module level, check the name is not
380
 
                        # defined in builtins
381
 
                        if name in defframe.scope_attrs or builtin_lookup(name)[1]:
382
 
                            maybee0601 = False
383
 
                    else:
384
 
                        # we are in a local scope, check the name is not
385
 
                        # defined in global or builtin scope
386
 
                        if defframe.root().lookup(name)[1]:
387
 
                            maybee0601 = False
388
 
                    if (maybee0601
389
 
                        and stmt.fromlineno <= defstmt.fromlineno
390
 
                        and not is_defined_before(node)
391
 
                        and not are_exclusive(stmt, defstmt, ('NameError', 'Exception', 'BaseException'))):
392
 
                        if defstmt is stmt and isinstance(node, (astng.DelName,
393
 
                                                                 astng.AssName)):
394
 
                            self.add_message('E0602', args=name, node=node)
395
 
                        elif self._to_consume[-1][-1] != 'lambda':
396
 
                            # E0601 may *not* occurs in lambda scope
397
 
                            self.add_message('E0601', args=name, node=node)
398
 
                if not isinstance(node, astng.AssName): # Aug AssName
399
 
                    del to_consume[name]
400
 
                else:
401
 
                    del consumed[name]
402
 
                # check it's not a loop variable used outside the loop
403
 
                self._loopvar_name(node, name)
404
 
                break
 
431
                del consumed[name]
 
432
            # check it's not a loop variable used outside the loop
 
433
            self._loopvar_name(node, name)
 
434
            break
405
435
        else:
406
436
            # we have not found the name, if it isn't a builtin, that's an
407
437
            # undefined name !
409
439
                    or name in self.config.additional_builtins):
410
440
                self.add_message('E0602', args=name, node=node)
411
441
 
 
442
    @check_messages('E0611')
412
443
    def visit_import(self, node):
413
444
        """check modules attribute accesses"""
414
445
        for name, _ in node.names:
419
450
                continue
420
451
            self._check_module_attrs(node, module, parts[1:])
421
452
 
 
453
    @check_messages('E0611')
422
454
    def visit_from(self, node):
423
455
        """check modules attribute accesses"""
424
456
        name_parts = node.modname.split('.')
425
457
        try:
426
458
            module = node.root().import_module(name_parts[0])
427
 
        except KeyboardInterrupt:
428
 
            raise
429
 
        except:
 
459
        except ASTNGBuildingException:
 
460
            return
 
461
        except Exception, exc:
 
462
            print 'Unhandled exception in VariablesChecker:', exc
430
463
            return
431
464
        module = self._check_module_attrs(node, module, name_parts[1:])
432
465
        if not module:
436
469
                continue
437
470
            self._check_module_attrs(node, module, name.split('.'))
438
471
 
439
 
##     def leave_getattr(self, node):
440
 
##         """check modules attribute accesses
441
 
 
442
 
##         this function is a "leave_" because when parsing 'a.b.c'
443
 
##         we want to check the innermost expression first.
444
 
##         """
445
 
##         if isinstance(node.expr, astng.Name):
446
 
##             try:
447
 
##                 module = node.expr.infer().next()
448
 
##             except astng.InferenceError:
449
 
##                 return
450
 
##             if not isinstance(module, astng.Module):
451
 
##                 # Not a module, don't check
452
 
##                 return
453
 
##         elif self._checking_mod_attr is not None:
454
 
##             module = self._checking_mod_attr
455
 
##         else:
456
 
##             return
457
 
##         self._checking_mod_attr = self._check_module_attrs(node, module,
458
 
##                                                            [node.attrname])
459
 
 
460
 
##     def leave_default(self, node):
461
 
##         """by default, reset the _checking_mod_attr attribute"""
462
 
##         self._checking_mod_attr = None
463
 
 
464
472
    def _check_module_attrs(self, node, module, module_names):
465
473
        """check that module_names (list of string) are accessible through the
466
474
        given module
491
499
        return None
492
500
 
493
501
 
 
502
class VariablesChecker3k(VariablesChecker):
 
503
    '''Modified variables checker for 3k'''
 
504
    # listcomp have now also their scope
 
505
 
 
506
    def visit_listcomp(self, node):
 
507
        """visit dictcomp: update consumption analysis variable
 
508
        """
 
509
        self._to_consume.append((copy(node.locals), {}, 'comprehension'))
 
510
 
 
511
    def leave_listcomp(self, _):
 
512
        """leave dictcomp: update consumption analysis variable
 
513
        """
 
514
        # do not check for not used locals here
 
515
        self._to_consume.pop()
 
516
 
 
517
if sys.version_info >= (3, 0):
 
518
    VariablesChecker = VariablesChecker3k
494
519
 
495
520
 
496
521
def register(linter):