def test_try_except(self): astng = builder.string_build(''' try: def exclusive_func2(): "docstring" except TypeError: def exclusive_func2(): "docstring" except: def exclusive_func2(): "docstring" else: def exclusive_func2(): "this one redefine the one defined line 42" ''') f1 = astng.locals['exclusive_func2'][0] f2 = astng.locals['exclusive_func2'][1] f3 = astng.locals['exclusive_func2'][2] f4 = astng.locals['exclusive_func2'][3] self.assertEquals(are_exclusive(f1, f2), True) self.assertEquals(are_exclusive(f1, f3), True) self.assertEquals(are_exclusive(f1, f4), False) self.assertEquals(are_exclusive(f2, f4), True) self.assertEquals(are_exclusive(f3, f4), True) self.assertEquals(are_exclusive(f3, f2), True) self.assertEquals(are_exclusive(f2, f1), True) self.assertEquals(are_exclusive(f4, f1), False) self.assertEquals(are_exclusive(f4, f2), True)
def test_not_exclusive(self): astng = builder.string_build(""" x = 10 for x in range(5): print x if x > 0: print '#' * x """, __name__, __file__) xass1 = astng.locals['x'][0] assert xass1.lineno == 2 xnames = [n for n in astng.nodes_of_class(nodes.Name) if n.name == 'x'] assert len(xnames) == 3 assert xnames[1].lineno == 6 self.assertEquals(are_exclusive(xass1, xnames[1]), False) self.assertEquals(are_exclusive(xass1, xnames[2]), False)
def _check_reimport(self, node, name, basename=None, level=0): """check if the import is necessary (i.e. not already done) """ frame = node.frame() first = get_first_import(frame, name, basename, level) if isinstance(first, (astng.Import, astng.From)) and first is not node \ and not are_exclusive(first, node): self.add_message('W0404', node=node, args=(name, first.fromlineno)) else: root = node.root() if root is frame: return first = get_first_import(root, name, basename) if not isinstance(first, (astng.Import, astng.From)): return if first is not node and not are_exclusive(first, node): self.add_message('W0404', node=node, args=(name, first.fromlineno))
def test_if(self): astng = builder.string_build(''' if 1: a = 1 a = 2 elif 2: a = 12 a = 13 else: a = 3 a = 4 ''') a1 = astng.locals['a'][0] a2 = astng.locals['a'][1] a3 = astng.locals['a'][2] a4 = astng.locals['a'][3] a5 = astng.locals['a'][4] a6 = astng.locals['a'][5] self.assertEquals(are_exclusive(a1, a2), False) self.assertEquals(are_exclusive(a1, a3), True) self.assertEquals(are_exclusive(a1, a5), True) self.assertEquals(are_exclusive(a3, a5), True) self.assertEquals(are_exclusive(a3, a4), False) self.assertEquals(are_exclusive(a5, a6), False)
def _check_accessed_members(self, node, accessed): """check that accessed members are defined""" # XXX refactor, probably much simpler now that E0201 is in type checker for attr, nodes in accessed.items(): # deactivate "except doesn't do anything", that's expected # pylint: disable-msg=W0704 # is it a class attribute ? try: node.local_attr(attr) # yes, stop here continue except astng.NotFoundError: pass # is it an instance attribute of a parent class ? try: node.instance_attr_ancestors(attr).next() # yes, stop here continue except StopIteration: pass # is it an instance attribute ? try: defstmts = node.instance_attr(attr) except astng.NotFoundError: pass else: if len(defstmts) == 1: defstmt = defstmts[0] # check that if the node is accessed in the same method as # it's defined, it's accessed after the initial assigment frame = defstmt.frame() lno = defstmt.fromlineno for _node in nodes: if _node.frame() is frame and _node.fromlineno < lno \ and not are_exclusive(_node.statement(), defstmt, ('AttributeError', 'Exception', 'BaseException')): self.add_message('E0203', node=_node, args=(attr, lno))
def _check_redefinition(self, redef_type, node): """check for redefinition of a function / method / class name""" defined_self = node.parent.frame()[node.name] if defined_self is not node and not are_exclusive(node, defined_self): self.add_message('E0102', node=node, args=(redef_type, defined_self.fromlineno))
def _filter_stmts(self, stmts, frame, offset): """filter statements to remove ignorable statements. If self is not a frame itself and the name is found in the inner frame locals, statements will be filtered to remove ignorable statements according to self's location """ # if offset == -1, my actual frame is not the inner frame but its parent # # class A(B): pass # # we need this to resolve B correctly if offset == -1: myframe = self.frame().parent.frame() else: myframe = self.frame() if not myframe is frame or self is frame: return stmts mystmt = self.statement() # line filtering if we are in the same frame # # take care node may be missing lineno information (this is the case for # nodes inserted for living objects) if myframe is frame and mystmt.fromlineno is not None: assert mystmt.fromlineno is not None, mystmt mylineno = mystmt.fromlineno + offset else: # disabling lineno filtering mylineno = 0 _stmts = [] _stmt_parents = [] for node in stmts: stmt = node.statement() # line filtering is on and we have reached our location, break if mylineno > 0 and stmt.fromlineno > mylineno: break if isinstance(node, nodes.Class) and self in node.bases: break assert hasattr(node, 'ass_type'), (node, node.scope(), node.scope().locals) ass_type = node.ass_type() if ass_type is mystmt and not isinstance(ass_type, (nodes.Class, nodes.Function, nodes.Import, nodes.From, nodes.Lambda)): if not isinstance(ass_type, nodes.Comprehension): break if isinstance(self, (nodes.Const, nodes.Name)): _stmts = [self] break elif ass_type.statement() is mystmt: # original node's statement is the assignment, only keeps # current node (gen exp, list comp) _stmts = [node] break optional_assign = isinstance(ass_type, nodes.LOOP_SCOPES) if optional_assign and ass_type.parent_of(self): # we are inside a loop, loop var assigment is hidding previous # assigment _stmts = [node] _stmt_parents = [stmt.parent] continue # XXX comment various branches below!!! try: pindex = _stmt_parents.index(stmt.parent) except ValueError: pass else: # we got a parent index, this means the currently visited node # is at the same block level as a previously visited node if _stmts[pindex].ass_type().parent_of(ass_type): # both statements are not at the same block level continue # if currently visited node is following previously considered # assignement and both are not exclusive, we can drop the # previous one. For instance in the following code :: # # if a: # x = 1 # else: # x = 2 # print x # # we can't remove neither x = 1 nor x = 2 when looking for 'x' # of 'print x'; while in the following :: # # x = 1 # x = 2 # print x # # we can remove x = 1 when we see x = 2 # # moreover, on loop assignment types, assignment won't # necessarily be done if the loop has no iteration, so we don't # want to clear previous assigments if any (hence the test on # optional_assign) if not (optional_assign or are_exclusive(_stmts[pindex], node)): del _stmt_parents[pindex] del _stmts[pindex] if isinstance(node, nodes.AssName): if not optional_assign and stmt.parent is mystmt.parent: _stmts = [] _stmt_parents = [] elif isinstance(node, nodes.DelName): _stmts = [] _stmt_parents = [] continue if not are_exclusive(self, node): _stmts.append(node) _stmt_parents.append(stmt.parent) return _stmts
def visit_name(self, node): """check that a name is defined if the current scope and doesn't redefine a built-in """ name = node.name stmt = node.statement() # probably "is_statement == True" missing somewhere in astng assert stmt.fromlineno, (stmt, node, node.fromlineno) frame = stmt.scope() # if the name node is used as a function default argument's value or as # a decorator, then start from the parent frame of the function instead # of the function frame - and thus open an inner class scope if (is_func_default(node) or is_func_decorator(node) or is_ancestor_name(frame, node)): start_index = len(self._to_consume) - 2 else: start_index = len(self._to_consume) - 1 # iterates through parent scopes, from the inner to the outer for i in range(start_index, -1, -1): to_consume, consumed, scope_type = self._to_consume[i] # if the current scope is a class scope but it's not the inner # scope, ignore it. This prevents to access this scope instead of # the globals one in function members when there are some common # names if scope_type == 'class' and i != start_index: continue # the name has already been consumed, only check it's not a loop # variable used outside the loop if consumed.has_key(name): self._loopvar_name(node, name) break # mark the name as consumed if it's defined in this scope # (i.e. no KeyError is raised by "to_consume[name]") try: consumed[name] = to_consume[name] except KeyError: continue else: # checks for use before assigment defnode = assign_parent(to_consume[name][0]) if defnode is not None: defstmt = defnode.statement() defframe = defstmt.frame() maybee0601 = True if not frame is defframe: maybee0601 = False elif defframe.parent is None: # we are at the module level, check the name is not # defined in builtins if name in defframe.scope_attrs or builtin_lookup(name)[1]: maybee0601 = False else: # we are in a local scope, check the name is not # defined in global or builtin scope if defframe.root().lookup(name)[1]: maybee0601 = False if (maybee0601 and stmt.fromlineno <= defstmt.fromlineno and not is_defined_before(node) and not are_exclusive(stmt, defstmt, ('NameError', 'Exception', 'BaseException'))): if defstmt is stmt and isinstance(node, (astng.DelName, astng.AssName)): self.add_message('E0602', args=name, node=node) else: self.add_message('E0601', args=name, node=node) if not isinstance(node, astng.AssName): # Aug AssName del to_consume[name] else: del consumed[name] # check it's not a loop variable used outside the loop self._loopvar_name(node, name) break else: # we have not found the name, if it isn't a builtin, that's an # undefined name ! if not (name in astng.Module.scope_attrs or is_builtin(name) or name in self.config.additional_builtins): self.add_message('E0602', args=name, node=node)