def _get_first_import(node, context, name, base, level, alias): """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, astroid.Import): if any(fullname == iname[0] for iname in first.names): found = True break elif isinstance(first, astroid.ImportFrom): if level == first.level: for imported_name, imported_alias in first.names: if fullname == '%s.%s' % (first.modname, imported_name): found = True break if name != '*' and name == imported_name and not ( alias or imported_alias): found = True break if found: break if found and not are_exclusive(first, node): return first return None
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('function-redefined', node=node, args=(redeftype, defined_self.fromlineno))
def _get_first_import(node, context, name, base, level, alias): """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, astroid.Import): if any(fullname == iname[0] for iname in first.names): found = True break elif isinstance(first, astroid.ImportFrom): if level == first.level: for imported_name, imported_alias in first.names: if fullname == '%s.%s' % (first.modname, imported_name): found = True break if name != '*' and name == imported_name and not (alias or imported_alias): found = True break if found: 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 six.iteritems(accessed): # deactivate "except doesn't do anything", that's expected # pylint: disable=W0704 try: # is it a class attribute ? node.local_attr(attr) # yes, stop here continue except astroid.NotFoundError: pass # is it an instance attribute of a parent class ? try: next(node.instance_attr_ancestors(attr)) # yes, stop here continue except StopIteration: pass # is it an instance attribute ? try: defstmts = node.instance_attr(attr) except astroid.NotFoundError: pass else: # filter out augment assignment nodes defstmts = [stmt for stmt in defstmts if stmt not in nodes] if not defstmts: # only augment assignment for this node, no-member should be # triggered by the typecheck checker continue # filter defstmts to only pick the first one when there are # several assignments in the same scope scope = defstmts[0].scope() defstmts = [ stmt for i, stmt in enumerate(defstmts) if i == 0 or stmt.scope() is not scope ] # if there are still more than one, don't attempt to be smarter # than we can be 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('access-member-before-definition', node=_node, args=(attr, lno))
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 try: # is it a class attribute ? node.local_attr(attr) # yes, stop here continue except astroid.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 astroid.NotFoundError: pass else: # filter out augment assignment nodes defstmts = [stmt for stmt in defstmts if stmt not in nodes] if not defstmts: # only augment assignment for this node, no-member should be # triggered by the typecheck checker continue # filter defstmts to only pick the first one when there are # several assignments in the same scope scope = defstmts[0].scope() defstmts = [stmt for i, stmt in enumerate(defstmts) if i == 0 or stmt.scope() is not scope] # if there are still more than one, don't attempt to be smarter # than we can be 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("access-member-before-definition", node=_node, args=(attr, lno))
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 astroid.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 astroid.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 astroid 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): # Detect if we are in a local class scope, as an assignment. # For example, the following is fair game. # # class A: # b = 1 # c = lambda b=b: b * b # # class B: # tp = 1 # def func(self, arg: tp): # ... in_annotation = ( PY3K and isinstance(frame, astroid.Function) and node.statement() is frame and (node in frame.args.annotations or node is frame.args.varargannotation or node is frame.args.kwargannotation)) if in_annotation: frame_locals = frame.parent.scope().locals else: frame_locals = frame.locals if not ((isinstance(frame, astroid.Class) or in_annotation) and name in frame_locals): continue # the name has already been consumed, only check it's not a loop # variable used outside the loop if name in consumed: defnode = assign_parent(consumed[name][0]) self._check_late_binding_closure(node, defnode) 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: self._check_late_binding_closure(node, defnode) defstmt = defnode.statement() defframe = defstmt.frame() maybee0601 = True if not frame is defframe: maybee0601 = _detect_global_scope(node, frame, defframe) 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 else: # check if we have a nonlocal if name in defframe.locals: maybee0601 = not any(isinstance(child, astroid.Nonlocal) and name in child.names for child in defframe.get_children()) # Handle a couple of class scoping issues. annotation_return = False # The class reuses itself in the class scope. recursive_klass = (frame is defframe and defframe.parent_of(node) and isinstance(defframe, astroid.Class) and node.name == defframe.name) if (self._to_consume[-1][-1] == 'lambda' and isinstance(frame, astroid.Class) and name in frame.locals): maybee0601 = True elif (isinstance(defframe, astroid.Class) and isinstance(frame, astroid.Function)): # Special rule for function return annotations, # which uses the same name as the class where # the function lives. if (PY3K and node is frame.returns and defframe.parent_of(frame.returns)): maybee0601 = annotation_return = True if (maybee0601 and defframe.name in defframe.locals and defframe.locals[name][0].lineno < frame.lineno): # Detect class assignments with the same # name as the class. In this case, no warning # should be raised. maybee0601 = False elif recursive_klass: maybee0601 = True else: maybee0601 = maybee0601 and stmt.fromlineno <= defstmt.fromlineno if (maybee0601 and not is_defined_before(node) and not are_exclusive(stmt, defstmt, ('NameError', 'Exception', 'BaseException'))): if recursive_klass or (defstmt is stmt and isinstance(node, (astroid.DelName, astroid.AssName))): self.add_message('undefined-variable', args=name, node=node) elif annotation_return: self.add_message('undefined-variable', args=name, node=node) elif self._to_consume[-1][-1] != 'lambda': # E0601 may *not* occurs in lambda scope. self.add_message('used-before-assignment', args=name, node=node) elif self._to_consume[-1][-1] == 'lambda': # E0601 can occur in class-level scope in lambdas, as in # the following example: # class A: # x = lambda attr: f + attr # f = 42 if isinstance(frame, astroid.Class) and name in frame.locals: if isinstance(node.parent, astroid.Arguments): # Doing the following is fine: # class A: # x = 42 # y = lambda attr=x: attr if stmt.fromlineno <= defstmt.fromlineno: self.add_message('used-before-assignment', args=name, node=node) else: self.add_message('undefined-variable', args=name, node=node) if isinstance(node, astroid.AssName): # Aug AssName del consumed[name] else: del to_consume[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 astroid.Module.scope_attrs or is_builtin(name) or name in self.config.additional_builtins): self.add_message('undefined-variable', 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 astroid 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: defnode = assign_parent(consumed[name][0]) self._check_late_binding_closure(node, defnode, scope_type) 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: self._check_late_binding_closure(node, defnode, scope_type) 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 else: # check if we have a nonlocal if name in defframe.locals: maybee0601 = not any(isinstance(child, astroid.Nonlocal) and name in child.names for child in defframe.get_children()) 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, (astroid.DelName, astroid.AssName)): self.add_message('undefined-variable', args=name, node=node) elif self._to_consume[-1][-1] != 'lambda': # E0601 may *not* occurs in lambda scope self.add_message('used-before-assignment', args=name, node=node) if isinstance(node, astroid.AssName): # Aug AssName del consumed[name] else: del to_consume[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 astroid.Module.scope_attrs or is_builtin(name) or name in self.config.additional_builtins): self.add_message('undefined-variable', args=name, node=node)
def _is_fallback_import(node, imports): imports = [import_node for (import_node, _) in imports] return any( astroid.are_exclusive(import_node, node) for import_node in imports)
def _is_fallback_import(node, imports): imports = [import_node for (import_node, _) in imports] return any(astroid.are_exclusive(import_node, node) for import_node in imports)
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 astroid 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): # Detect if we are in a local class scope, as an assignment. # For example, the following is fair game. # class A: # b = 1 # c = lambda b=b: b * b class_assignment = (isinstance(frame, astroid.Class) and name in frame.locals) if not class_assignment: continue # the name has already been consumed, only check it's not a loop # variable used outside the loop if name in consumed: defnode = assign_parent(consumed[name][0]) self._check_late_binding_closure(node, defnode, scope_type) 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: self._check_late_binding_closure(node, defnode, scope_type) defstmt = defnode.statement() defframe = defstmt.frame() maybee0601 = True if not frame is defframe: maybee0601 = _detect_global_scope(node, frame, defframe) 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 else: # check if we have a nonlocal if name in defframe.locals: maybee0601 = not any( isinstance(child, astroid.Nonlocal) and name in child.names for child in defframe.get_children()) if (self._to_consume[-1][-1] == 'lambda' and isinstance(frame, astroid.Class) and name in frame.locals): maybee0601 = True else: maybee0601 = maybee0601 and stmt.fromlineno <= defstmt.fromlineno if (maybee0601 and not is_defined_before(node) and not are_exclusive( stmt, defstmt, ('NameError', 'Exception', 'BaseException'))): if defstmt is stmt and isinstance( node, (astroid.DelName, astroid.AssName)): self.add_message('undefined-variable', args=name, node=node) elif self._to_consume[-1][-1] != 'lambda': # E0601 may *not* occurs in lambda scope. self.add_message('used-before-assignment', args=name, node=node) elif self._to_consume[-1][-1] == 'lambda': # E0601 can occur in class-level scope in lambdas, as in # the following example: # class A: # x = lambda attr: f + attr # f = 42 if isinstance(frame, astroid.Class) and name in frame.locals: if isinstance(node.parent, astroid.Arguments): # Doing the following is fine: # class A: # x = 42 # y = lambda attr=x: attr if stmt.fromlineno <= defstmt.fromlineno: self.add_message('used-before-assignment', args=name, node=node) else: self.add_message('undefined-variable', args=name, node=node) if isinstance(node, astroid.AssName): # Aug AssName del consumed[name] else: del to_consume[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 astroid.Module.scope_attrs or is_builtin(name) or name in self.config.additional_builtins): self.add_message('undefined-variable', 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 astroid 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, (astroid.DelName, astroid.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, astroid.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 astroid.Module.scope_attrs or is_builtin(name) or name in self.config.additional_builtins): self.add_message('E0602', args=name, node=node)
def _check_redefinition(self, redeftype, node): """Check for redefinition of a function / method / class name.""" parent_frame = node.parent.frame(future=True) # Ignore function stubs created for type information redefinitions = [ i for i in parent_frame.locals[node.name] if not (isinstance(i.parent, nodes.AnnAssign) and i.parent.simple) ] defined_self = next( (local for local in redefinitions if not utils.is_overload_stub(local)), node, ) if defined_self is not node and not astroid.are_exclusive( node, defined_self): # Additional checks for methods which are not considered # redefined, since they are already part of the base API. if (isinstance(parent_frame, nodes.ClassDef) and node.name in REDEFINABLE_METHODS): return # Skip typing.overload() functions. if utils.is_overload_stub(node): return # Exempt functions redefined on a condition. if isinstance(node.parent, nodes.If): # Exempt "if not <func>" cases if (isinstance(node.parent.test, nodes.UnaryOp) and node.parent.test.op == "not" and isinstance(node.parent.test.operand, nodes.Name) and node.parent.test.operand.name == node.name): return # Exempt "if <func> is not None" cases # pylint: disable=too-many-boolean-expressions if (isinstance(node.parent.test, nodes.Compare) and isinstance(node.parent.test.left, nodes.Name) and node.parent.test.left.name == node.name and node.parent.test.ops[0][0] == "is" and isinstance(node.parent.test.ops[0][1], nodes.Const) and node.parent.test.ops[0][1].value is None): return # Check if we have forward references for this node. try: redefinition_index = redefinitions.index(node) except ValueError: pass else: for redefinition in redefinitions[:redefinition_index]: inferred = utils.safe_infer(redefinition) if (inferred and isinstance(inferred, astroid.Instance) and inferred.qname() == TYPING_FORWARD_REF_QNAME): return dummy_variables_rgx = lint_utils.get_global_option( self, "dummy-variables-rgx", default=None) if dummy_variables_rgx and dummy_variables_rgx.match(node.name): return self.add_message( "function-redefined", node=node, args=(redeftype, defined_self.fromlineno), )