def _check_redefinition(self, redeftype, 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=(redeftype, defined_self.fromlineno))
def get_first_import(node, context, name, base, level): """return the node where [base.]<name> is imported or None if not found """ fullname = '%s.%s' % (base, name) if base else name first = None found = False for first in context.body: if first is node: continue if first.scope() is node.scope( ) and first.fromlineno > node.fromlineno: continue if isinstance(first, astng.Import): if any(fullname == iname[0] for iname in first.names): found = True break elif isinstance(first, astng.From): if level == first.level and any(fullname == '%s.%s' % (first.modname, iname[0]) for iname in first.names): found = True break if found and not are_exclusive(first, node): return first
def _check_reimport(self, node, name, basename=None, level=0): """check if the import is necessary (i.e. not already done)""" # XXX rewrite 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 get_first_import(node, context, name, base, level): """return the node where [base.]<name> is imported or None if not found """ first = None for first in context.values(): if isinstance(first, astng.Import): if name in [iname[0] for iname in first.names]: break elif isinstance(first, astng.From): if base == first.modname and level == first.level and \ name in [iname[0] for iname in first.names]: break if first is not node and not are_exclusive(first, node): return first
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.iteritems(): # deactivate "except doesn't do anything", that's expected # pylint: disable=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 assignment 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 get_first_import(node, context, name, base, level): """return the node where [base.]<name> is imported or None if not found """ fullname = '%s.%s' % (base, name) if base else name first = None found = False for first in context.body: if first is node: continue if first.scope() is node.scope() and first.lineno > node.lineno: continue if isinstance(first, astng.Import): if any(fullname == iname[0] for iname in first.names): found = True break elif isinstance(first, astng.From): if level == first.level and any( fullname == '%s.%s' % (first.modname, iname[0]) for iname in first.names): found = True break if found and not are_exclusive(first, node): return first
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=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 assignment 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 visit_name(self, node): """check that a name is defined if the current scope and doesn't redefine a built-in """ stmt = node.statement() if stmt.fromlineno is None: # name node from a astng built from live code, skip assert not stmt.root().file.endswith('.py') return name = node.name 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 base_scope_type = self._to_consume[start_index][-1] 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. The only exception is when the starting scope is a # genexpr and its direct outer scope is a class if scope_type == 'class' and i != start_index and not ( base_scope_type == 'genexpr' and i == start_index - 1): # XXX find a way to handle class scope in a smoother way 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 assignment 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) elif self._to_consume[-1][-1] != 'lambda': # E0601 may *not* occurs in lambda scope 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)
def visit_name(self, node): """check that a name is defined if the current scope and doesn't redefine a built-in """ stmt = node.statement() if stmt.fromlineno is None: # name node from a astng built from live code, skip assert not stmt.root().file.endswith('.py') return name = node.name 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 base_scope_type = self._to_consume[start_index][-1] 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. The only exception is when the starting scope is a # comprehension and its direct outer scope is a class if scope_type == 'class' and i != start_index and not ( base_scope_type == 'comprehension' and i == start_index-1): # XXX find a way to handle class scope in a smoother way continue # the name has already been consumed, only check it's not a loop # variable used outside the loop if name in consumed: 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 # checks for use before assignment 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) elif self._to_consume[-1][-1] != 'lambda': # E0601 may *not* occurs in lambda scope 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)