def _check_catching_non_exception(self, handler, exc, part): if isinstance(exc, astroid.Tuple): # Check if it is a tuple of exceptions. inferred = [utils.safe_infer(elt) for elt in exc.elts] if any(node is astroid.Uninferable for node in inferred): # Don't emit if we don't know every component. return if all( node and (utils.inherit_from_std_ex(node) or not utils.has_known_bases(node)) for node in inferred ): return if not isinstance(exc, astroid.ClassDef): # Don't emit the warning if the inferred stmt # is None, but the exception handler is something else, # maybe it was redefined. if isinstance(exc, astroid.Const) and exc.value is None: if ( isinstance(handler.type, astroid.Const) and handler.type.value is None ) or handler.type.parent_of(exc): # If the exception handler catches None or # the exception component, which is None, is # defined by the entire exception handler, then # emit a warning. self.add_message( "catching-non-exception", node=handler.type, args=(part.as_string(),), ) else: self.add_message( "catching-non-exception", node=handler.type, args=(part.as_string(),), ) return if ( not utils.inherit_from_std_ex(exc) and exc.name not in self._builtin_exceptions ): if utils.has_known_bases(exc): self.add_message( "catching-non-exception", node=handler.type, args=(exc.name,) )
def _check_catching_non_exception(self, handler, exc, part): if isinstance(exc, astroid.Tuple): # Check if it is a tuple of exceptions. inferred = [utils.safe_infer(elt) for elt in exc.elts] if any(node is astroid.Uninferable for node in inferred): # Don't emit if we don't know every component. return if all( node and (utils.inherit_from_std_ex(node) or not utils.has_known_bases(node)) for node in inferred ): return if not isinstance(exc, astroid.ClassDef): # Don't emit the warning if the infered stmt # is None, but the exception handler is something else, # maybe it was redefined. if isinstance(exc, astroid.Const) and exc.value is None: if ( isinstance(handler.type, astroid.Const) and handler.type.value is None ) or handler.type.parent_of(exc): # If the exception handler catches None or # the exception component, which is None, is # defined by the entire exception handler, then # emit a warning. self.add_message( "catching-non-exception", node=handler.type, args=(part.as_string(),), ) else: self.add_message( "catching-non-exception", node=handler.type, args=(part.as_string(),), ) return if ( not utils.inherit_from_std_ex(exc) and exc.name not in self._builtin_exceptions ): if utils.has_known_bases(exc): self.add_message( "catching-non-exception", node=handler.type, args=(exc.name,) )
def visit_asyncwith(self, node): for ctx_mgr, _ in node.items: infered = checker_utils.safe_infer(ctx_mgr) if infered is None or infered is astroid.YES: continue if isinstance(infered, astroid.Instance): try: infered.getattr('__aenter__') infered.getattr('__aexit__') except exceptions.NotFoundError: if isinstance(infered, astroid.Instance): # If we do not know the bases of this class, # just skip it. if not checker_utils.has_known_bases(infered): continue # Just ignore mixin classes. if self._ignore_mixin_members: if infered.name[-5:].lower() == 'mixin': continue else: continue self.add_message('not-async-context-manager', node=node, args=(infered.name, ))
def visit_asyncwith(self, node): for ctx_mgr, _ in node.items: inferred = checker_utils.safe_infer(ctx_mgr) if inferred is None or inferred is astroid.Uninferable: continue if isinstance(inferred, bases.AsyncGenerator): # Check if we are dealing with a function decorated # with contextlib.asynccontextmanager. if decorated_with(inferred.parent, self._async_generators): continue else: try: inferred.getattr("__aenter__") inferred.getattr("__aexit__") except exceptions.NotFoundError: if isinstance(inferred, astroid.Instance): # If we do not know the bases of this class, # just skip it. if not checker_utils.has_known_bases(inferred): continue # Just ignore mixin classes. if self._ignore_mixin_members: if inferred.name[-5:].lower() == "mixin": continue else: continue self.add_message("not-async-context-manager", node=node, args=(inferred.name, ))
def visit_functiondef(self, node: nodes.FunctionDef) -> None: if self.linter.namespace.no_docstring_rgx.match(node.name) is None: ftype = "method" if node.is_method() else "function" if (is_property_setter(node) or is_property_deleter(node) or is_overload_stub(node)): return if isinstance(node.parent.frame(future=True), nodes.ClassDef): overridden = False confidence = (interfaces.INFERENCE if utils.has_known_bases( node.parent.frame( future=True)) else interfaces.INFERENCE_FAILURE) # check if node is from a method overridden by its ancestor for ancestor in node.parent.frame(future=True).ancestors(): if ancestor.qname() == "builtins.object": continue if node.name in ancestor and isinstance( ancestor[node.name], nodes.FunctionDef): overridden = True break self._check_docstring( ftype, node, report_missing=not overridden, confidence=confidence # type: ignore[arg-type] ) elif isinstance(node.parent.frame(future=True), nodes.Module): self._check_docstring(ftype, node) # type: ignore[arg-type] else: return
def visit_classdef(self, node: nodes.ClassDef) -> None: # TODO (Typing): This function can be called from visit_instance # with the return type of instance._proxied (not typed right now) if not utils.inherit_from_std_ex(node) and utils.has_known_bases(node): if node.newstyle: self._checker.add_message("raising-non-exception", node=self._node)
def visit_classdef(self, cls): if (not utils.inherit_from_std_ex(cls) and utils.has_known_bases(cls)): if cls.newstyle: self._checker.add_message('raising-non-exception', node=self._node) else: self._checker.add_message('nonstandard-exception', node=self._node)
def visit_asyncwith(self, node): for ctx_mgr, _ in node.items: inferred = checker_utils.safe_infer(ctx_mgr) if inferred is None or inferred is astroid.Uninferable: continue if isinstance(inferred, bases.AsyncGenerator): # Check if we are dealing with a function decorated # with contextlib.asynccontextmanager. if decorated_with(inferred.parent, self._async_generators): continue else: try: inferred.getattr("__aenter__") inferred.getattr("__aexit__") except exceptions.NotFoundError: if isinstance(inferred, astroid.Instance): # If we do not know the bases of this class, # just skip it. if not checker_utils.has_known_bases(inferred): continue # Just ignore mixin classes. if self._ignore_mixin_members: if inferred.name[-5:].lower() == "mixin": continue else: continue self.add_message( "not-async-context-manager", node=node, args=(inferred.name,) )
def visit_callfunc(self, node): """check property usage""" parent = node.parent.frame() if isinstance(parent, astroid.Class) and not parent.newstyle and isinstance(node.func, astroid.Name): confidence = INFERENCE if has_known_bases(parent) else INFERENCE_FAILURE name = node.func.name if name == "property": self.add_message("property-on-old-class", node=node, confidence=confidence)
def _check_binop_errors(self, node): for error in node.type_errors(): # Let the error customize its output. if any(isinstance(obj, astroid.ClassDef) and not has_known_bases(obj) for obj in (error.left_type, error.right_type)): continue self.add_message('unsupported-binary-operation', args=str(error), node=node)
def visit_function(self, node): """check use of super""" # ignore actual functions or method within a new style class if not node.is_method(): return klass = node.parent.frame() for stmt in node.nodes_of_class(astroid.CallFunc): if node_frame_class(stmt) != node_frame_class(node): # Don't look down in other scopes. continue expr = stmt.func if not isinstance(expr, astroid.Getattr): continue call = expr.expr # skip the test if using super if isinstance(call, astroid.CallFunc) and \ isinstance(call.func, astroid.Name) and \ call.func.name == 'super': confidence = (INFERENCE if has_known_bases(klass) else INFERENCE_FAILURE) if not klass.newstyle: # super should not be used on an old style class self.add_message('super-on-old-class', node=node, confidence=confidence) else: # super first arg should be the class if not call.args and sys.version_info[0] == 3: # unless Python 3 continue try: supcls = (call.args and next(call.args[0].infer()) or None) except astroid.InferenceError: continue if supcls is None: self.add_message('missing-super-argument', node=call, confidence=confidence) continue if klass is not supcls: name = None # if supcls is not YES, then supcls was infered # and use its name. Otherwise, try to look # for call.args[0].name if supcls is not astroid.YES: name = supcls.name else: if hasattr(call.args[0], 'name'): name = call.args[0].name if name is not None: self.add_message('bad-super-call', node=call, args=(name, ), confidence=confidence)
def _check_raise_value(self, node, expr): """check for bad values, string exception and class inheritance """ value_found = True if isinstance(expr, astroid.Const): value = expr.value if not isinstance(value, str): # raising-string will be emitted from python3 porting checker. self.add_message('raising-bad-type', node=node, args=value.__class__.__name__) elif ((isinstance(expr, astroid.Name) and expr.name in ('None', 'True', 'False')) or isinstance(expr, (astroid.List, astroid.Dict, astroid.Tuple, astroid.Module, astroid.FunctionDef))): emit = True if not PY3K and isinstance(expr, astroid.Tuple) and expr.elts: # On Python 2, using the following is not an error: # raise (ZeroDivisionError, None) # raise (ZeroDivisionError, ) # What's left to do is to check that the first # argument is indeed an exception. # Verifying the other arguments is not # the scope of this check. first = expr.elts[0] inferred = safe_infer(first) if isinstance(inferred, astroid.Instance): # pylint: disable=protected-access inferred = inferred._proxied if (inferred is astroid.YES or isinstance(inferred, astroid.ClassDef) and inherit_from_std_ex(inferred)): emit = False if emit: self.add_message('raising-bad-type', node=node, args=expr.name) elif ((isinstance(expr, astroid.Name) and expr.name == 'NotImplemented') or (isinstance(expr, astroid.Call) and isinstance(expr.func, astroid.Name) and expr.func.name == 'NotImplemented')): self.add_message('notimplemented-raised', node=node) elif isinstance(expr, (astroid.Instance, astroid.ClassDef)): if isinstance(expr, astroid.Instance): # pylint: disable=protected-access expr = expr._proxied if (isinstance(expr, astroid.ClassDef) and not inherit_from_std_ex(expr) and has_known_bases(expr)): if expr.newstyle: self.add_message('raising-non-exception', node=node) else: self.add_message('nonstandard-exception', node=node) else: value_found = False else: value_found = False return value_found
def _check_raise_value(self, node, expr): """check for bad values, string exception and class inheritance """ value_found = True if isinstance(expr, astroid.Const): value = expr.value if not isinstance(value, str): # raising-string will be emitted from python3 porting checker. self.add_message('raising-bad-type', node=node, args=value.__class__.__name__) elif ((isinstance(expr, astroid.Name) and expr.name in ('None', 'True', 'False')) or isinstance(expr, (astroid.List, astroid.Dict, astroid.Tuple, astroid.Module, astroid.FunctionDef))): emit = True if not PY3K and isinstance(expr, astroid.Tuple) and expr.elts: # On Python 2, using the following is not an error: # raise (ZeroDivisionError, None) # raise (ZeroDivisionError, ) # What's left to do is to check that the first # argument is indeed an exception. # Verifying the other arguments is not # the scope of this check. first = expr.elts[0] inferred = safe_infer(first) if isinstance(inferred, astroid.Instance): # pylint: disable=protected-access inferred = inferred._proxied if (inferred is astroid.YES or isinstance(inferred, astroid.ClassDef) and inherit_from_std_ex(inferred)): emit = False if emit: self.add_message('raising-bad-type', node=node, args=expr.name) elif ( (isinstance(expr, astroid.Name) and expr.name == 'NotImplemented') or (isinstance(expr, astroid.Call) and isinstance(expr.func, astroid.Name) and expr.func.name == 'NotImplemented')): self.add_message('notimplemented-raised', node=node) elif isinstance(expr, (astroid.Instance, astroid.ClassDef)): if isinstance(expr, astroid.Instance): # pylint: disable=protected-access expr = expr._proxied if (isinstance(expr, astroid.ClassDef) and not inherit_from_std_ex(expr) and has_known_bases(expr)): if expr.newstyle: self.add_message('raising-non-exception', node=node) else: self.add_message('nonstandard-exception', node=node) else: value_found = False else: value_found = False return value_found
def visit_functiondef(self, node): """check use of super""" # ignore actual functions or method within a new style class if not node.is_method(): return klass = node.parent.frame() for stmt in node.nodes_of_class(astroid.Call): if node_frame_class(stmt) != node_frame_class(node): # Don't look down in other scopes. continue expr = stmt.func if not isinstance(expr, astroid.Attribute): continue call = expr.expr # skip the test if using super if not (isinstance(call, astroid.Call) and isinstance(call.func, astroid.Name) and call.func.name == 'super'): continue confidence = (INFERENCE if has_known_bases(klass) else INFERENCE_FAILURE) if not klass.newstyle: # super should not be used on an old style class self.add_message('super-on-old-class', node=node, confidence=confidence) else: # super first arg should be the class if not call.args and sys.version_info[0] == 3: # unless Python 3 continue try: supcls = (call.args and next(call.args[0].infer()) or None) except astroid.InferenceError: continue if supcls is None: self.add_message('missing-super-argument', node=call, confidence=confidence) continue if klass is not supcls: name = None # if supcls is not YES, then supcls was infered # and use its name. Otherwise, try to look # for call.args[0].name if supcls is not astroid.YES: name = supcls.name else: if hasattr(call.args[0], 'name'): name = call.args[0].name if name is not None: self.add_message('bad-super-call', node=call, args=(name, ), confidence=confidence)
def visit_call(self, node): """check property usage""" parent = node.parent.frame() if (isinstance(parent, astroid.ClassDef) and not parent.newstyle and isinstance(node.func, astroid.Name)): confidence = (INFERENCE if has_known_bases(parent) else INFERENCE_FAILURE) name = node.func.name if name == 'property': self.add_message('property-on-old-class', node=node, confidence=confidence)
def visit_with(self, node): for ctx_mgr, _ in node.items: context = astroid.context.InferenceContext() infered = safe_infer(ctx_mgr, context=context) if infered is None or infered is astroid.YES: continue if isinstance(infered, bases.Generator): # Check if we are dealing with a function decorated # with contextlib.contextmanager. if decorated_with(infered.parent, self.config.contextmanager_decorators): continue # If the parent of the generator is not the context manager itself, # that means that it could have been returned from another # function which was the real context manager. # The following approach is more of a hack rather than a real # solution: walk all the inferred statements for the # given *ctx_mgr* and if you find one function scope # which is decorated, consider it to be the real # manager and give up, otherwise emit not-context-manager. # See the test file for not_context_manager for a couple # of self explaining tests. for path in six.moves.filter(None, _unflatten(context.path)): scope = path.scope() if not isinstance(scope, astroid.FunctionDef): continue if decorated_with(scope, self.config.contextmanager_decorators): break else: self.add_message('not-context-manager', node=node, args=(infered.name, )) else: try: infered.getattr('__enter__') infered.getattr('__exit__') except exceptions.NotFoundError: if isinstance(infered, astroid.Instance): # If we do not know the bases of this class, # just skip it. if not has_known_bases(infered): continue # Just ignore mixin classes. if self.config.ignore_mixin_members: if infered.name[-5:].lower() == 'mixin': continue self.add_message('not-context-manager', node=node, args=(infered.name, ))
def visit_classdef(self, node): """init visit variable _accessed """ self._accessed.append(defaultdict(list)) self._check_bases_classes(node) # if not an exception or a metaclass if node.type == 'class' and has_known_bases(node): try: node.local_attr('__init__') except astroid.NotFoundError: self.add_message('no-init', args=node, node=node) self._check_slots(node) self._check_proper_bases(node) self._check_consistent_mro(node)
def visit_class(self, node): """ Check __slots__ in old style classes and old style class definition. """ if "__slots__" in node and not node.newstyle: confidence = INFERENCE if has_known_bases(node) else INFERENCE_FAILURE self.add_message("slots-on-old-class", node=node, confidence=confidence) # The node type could be class, exception, metaclass, or # interface. Presumably, the non-class-type nodes would always # have an explicit base class anyway. if not node.bases and node.type == "class" and not node.metaclass(): # We use confidence HIGH here because this message should only ever # be emitted for classes at the root of the inheritance hierarchyself. self.add_message("old-style-class", node=node, confidence=HIGH)
def visit_classdef(self, node): """ Check __slots__ in old style classes and old style class definition. """ if '__slots__' in node and not node.newstyle: confidence = (INFERENCE if has_known_bases(node) else INFERENCE_FAILURE) self.add_message('slots-on-old-class', node=node, confidence=confidence) # The node type could be class, exception, metaclass, or # interface. Presumably, the non-class-type nodes would always # have an explicit base class anyway. if not node.bases and node.type == 'class' and not node.metaclass(): # We use confidence HIGH here because this message should only ever # be emitted for classes at the root of the inheritance hierarchyself. self.add_message('old-style-class', node=node, confidence=HIGH)
def _check_raise_value(self, node, expr): """check for bad values, string exception and class inheritance """ value_found = True if isinstance(expr, astroid.Const): value = expr.value if isinstance(value, str): self.add_message('raising-string', node=node) else: self.add_message('raising-bad-type', node=node, args=value.__class__.__name__) elif (isinstance(expr, astroid.Name) and \ expr.name in ('None', 'True', 'False')) or \ isinstance(expr, (astroid.List, astroid.Dict, astroid.Tuple, astroid.Module, astroid.Function)): self.add_message('raising-bad-type', node=node, args=expr.name) elif ( (isinstance(expr, astroid.Name) and expr.name == 'NotImplemented') or (isinstance(expr, astroid.CallFunc) and isinstance(expr.func, astroid.Name) and expr.func.name == 'NotImplemented')): self.add_message('notimplemented-raised', node=node) elif isinstance(expr, astroid.BinOp) and expr.op == '%': self.add_message('raising-string', node=node) elif isinstance(expr, (Instance, astroid.Class)): if isinstance(expr, Instance): expr = expr._proxied if (isinstance(expr, astroid.Class) and not inherit_from_std_ex(expr) and expr.root().name != BUILTINS_NAME): if expr.newstyle: self.add_message('raising-non-exception', node=node) else: self.add_message( 'nonstandard-exception', node=node, confidence=INFERENCE if has_known_bases(expr) else INFERENCE_FAILURE) else: value_found = False else: value_found = False return value_found
def _emit_no_member(owner, owner_name, attrname, ignored_modules, ignored_mixins, ignored_classes): """Try to see if no-member should be emitted for the given owner. The following cases are ignored: * the owner is a function and it has decorators. * the owner is an instance and it has __getattr__, __getattribute__ implemented * the module is explicitly ignored from no-member checks * the owner is a class and the name can be found in its metaclass. """ if owner_name in ignored_classes: return False # skip None anyway if isinstance(owner, astroid.Const) and owner.value is None: return False # TODO(cpopa): This should be removed when we'll understand "super" if is_super(owner) or getattr(owner, 'type', None) == 'metaclass': return False if ignored_mixins and owner_name[-5:].lower() == 'mixin': return False if isinstance(owner, astroid.Function) and owner.decorators: return False if isinstance(owner, Instance): if owner.has_dynamic_getattr() or not has_known_bases(owner): return False # explicit skipping of module member access if owner.root().name in ignored_modules: return False if isinstance(owner, astroid.Class): # Look up in the metaclass only if the owner is itself # a class. # TODO: getattr doesn't return by default members # from the metaclass, because handling various cases # of methods accessible from the metaclass itself # and/or subclasses only is too complicated for little to # no benefit. metaclass = owner.metaclass() or owner.implicit_metaclass() try: if metaclass and metaclass.getattr(attrname): return False except NotFoundError: pass return True
def visit_functiondef(self, node): if self.config.no_docstring_rgx.match(node.name) is None: ftype = node.is_method() and 'method' or 'function' if isinstance(node.parent.frame(), astroid.ClassDef): overridden = False confidence = (INFERENCE if has_known_bases(node.parent.frame()) else INFERENCE_FAILURE) # check if node is from a method overridden by its ancestor for ancestor in node.parent.frame().ancestors(): if node.name in ancestor and \ isinstance(ancestor[node.name], astroid.FunctionDef): overridden = True break self._check_docstring(ftype, node, report_missing=not overridden, confidence=confidence) else: self._check_docstring(ftype, node)
def _check_raise_value(self, node, expr): """check for bad values, string exception and class inheritance """ value_found = True if isinstance(expr, astroid.Const): value = expr.value if isinstance(value, str): # raising-string will be emitted from python3 porting checker. pass else: self.add_message('raising-bad-type', node=node, args=value.__class__.__name__) elif (isinstance(expr, astroid.Name) and \ expr.name in ('None', 'True', 'False')) or \ isinstance(expr, (astroid.List, astroid.Dict, astroid.Tuple, astroid.Module, astroid.Function)): self.add_message('raising-bad-type', node=node, args=expr.name) elif ((isinstance(expr, astroid.Name) and expr.name == 'NotImplemented') or (isinstance(expr, astroid.CallFunc) and isinstance(expr.func, astroid.Name) and expr.func.name == 'NotImplemented')): self.add_message('notimplemented-raised', node=node) elif isinstance(expr, (Instance, astroid.Class)): if isinstance(expr, Instance): expr = expr._proxied if (isinstance(expr, astroid.Class) and not inherit_from_std_ex(expr) and expr.root().name != BUILTINS_NAME): if expr.newstyle: self.add_message('raising-non-exception', node=node) else: self.add_message( 'nonstandard-exception', node=node, confidence=INFERENCE if has_known_bases(expr) else INFERENCE_FAILURE) else: value_found = False else: value_found = False return value_found
def visit_functiondef(self, node: nodes.FunctionDef) -> None: # Do not emit any warnings if the method is just an implementation # of a base class method. confidence = interfaces.HIGH if node.is_method(): if utils.overrides_a_method(node.parent.frame(future=True), node.name): return confidence = (interfaces.INFERENCE if utils.has_known_bases( node.parent.frame( future=True)) else interfaces.INFERENCE_FAILURE) self._check_name( _determine_function_name_type(node, config=self.linter.config), node.name, node, confidence, ) # Check argument names args = node.args.args if args is not None: self._recursive_check_names(args)
def _emit_no_member(node, owner, owner_name, ignored_mixins): """Try to see if no-member should be emitted for the given owner. The following cases are ignored: * the owner is a function and it has decorators. * the owner is an instance and it has __getattr__, __getattribute__ implemented * the module is explicitly ignored from no-member checks * the owner is a class and the name can be found in its metaclass. * The access node is protected by an except handler, which handles AttributeError, Exception or bare except. """ if node_ignores_exception(node, AttributeError): return False # skip None anyway if isinstance(owner, astroid.Const) and owner.value is None: return False if is_super(owner) or getattr(owner, "type", None) == "metaclass": return False if ignored_mixins and owner_name[-5:].lower() == "mixin": return False if isinstance(owner, astroid.FunctionDef) and owner.decorators: return False if isinstance(owner, astroid.Instance): if owner.has_dynamic_getattr() or not has_known_bases(owner): return False if isinstance(owner, objects.Super): # Verify if we are dealing with an invalid Super object. # If it is invalid, then there's no point in checking that # it has the required attribute. Also, don't fail if the # MRO is invalid. try: owner.super_mro() except (exceptions.MroError, exceptions.SuperError): return False if not all(map(has_known_bases, owner.type.mro())): return False return True
def _emit_no_member(node, owner, owner_name, ignored_mixins): """Try to see if no-member should be emitted for the given owner. The following cases are ignored: * the owner is a function and it has decorators. * the owner is an instance and it has __getattr__, __getattribute__ implemented * the module is explicitly ignored from no-member checks * the owner is a class and the name can be found in its metaclass. * The access node is protected by an except handler, which handles AttributeError, Exception or bare except. """ if node_ignores_exception(node, AttributeError): return False # skip None anyway if isinstance(owner, astroid.Const) and owner.value is None: return False if is_super(owner) or getattr(owner, 'type', None) == 'metaclass': return False if ignored_mixins and owner_name[-5:].lower() == 'mixin': return False if isinstance(owner, astroid.FunctionDef) and owner.decorators: return False if isinstance(owner, astroid.Instance): if owner.has_dynamic_getattr() or not has_known_bases(owner): return False if isinstance(owner, objects.Super): # Verify if we are dealing with an invalid Super object. # If it is invalid, then there's no point in checking that # it has the required attribute. Also, don't fail if the # MRO is invalid. try: owner.super_mro() except (exceptions.MroError, exceptions.SuperError): return False if not all(map(has_known_bases, owner.type.mro())): return False return True
def visit_asyncwith(self, node: nodes.AsyncWith) -> None: for ctx_mgr, _ in node.items: inferred = checker_utils.safe_infer(ctx_mgr) if inferred is None or inferred is astroid.Uninferable: continue if isinstance(inferred, nodes.AsyncFunctionDef): # Check if we are dealing with a function decorated # with contextlib.asynccontextmanager. if decorated_with(inferred, self._async_generators): continue elif isinstance(inferred, astroid.bases.AsyncGenerator): # Check if we are dealing with a function decorated # with contextlib.asynccontextmanager. if decorated_with(inferred.parent, self._async_generators): continue else: try: inferred.getattr("__aenter__") inferred.getattr("__aexit__") except astroid.exceptions.NotFoundError: if isinstance(inferred, astroid.Instance): # If we do not know the bases of this class, # just skip it. if not checker_utils.has_known_bases(inferred): continue # Ignore mixin classes if they match the rgx option. if ("not-async-context-manager" in self.linter.namespace.ignored_checks_for_mixins and self._mixin_class_rgx.match( inferred.name)): continue else: continue self.add_message("not-async-context-manager", node=node, args=(inferred.name, ))
def leave_function(self, node): """leave function: check function's locals are consumed""" not_consumed = self._to_consume.pop()[0] if not (self.linter.is_message_enabled('unused-variable') or self.linter.is_message_enabled('unused-argument')): return # don't check arguments of function which are only raising an exception if is_error(node): return # don't check arguments of abstract methods or within an interface is_method = node.is_method() klass = node.parent.frame() if is_method and (klass.type == 'interface' or node.is_abstract()): return if is_method and isinstance(klass, astroid.Class): confidence = INFERENCE if has_known_bases(klass) else INFERENCE_FAILURE else: confidence = HIGH authorized_rgx = self.config.dummy_variables_rgx called_overridden = False argnames = node.argnames() global_names = set() nonlocal_names = set() for global_stmt in node.nodes_of_class(astroid.Global): global_names.update(set(global_stmt.names)) for nonlocal_stmt in node.nodes_of_class(astroid.Nonlocal): nonlocal_names.update(set(nonlocal_stmt.names)) for name, stmts in six.iteritems(not_consumed): # ignore some special names specified by user configuration if authorized_rgx.match(name): continue # ignore names imported by the global statement # FIXME: should only ignore them if it's assigned latter stmt = stmts[0] if isinstance(stmt, astroid.Global): continue if isinstance(stmt, (astroid.Import, astroid.From)): # Detect imports, assigned to global statements. if global_names: skip = False for import_name, import_alias in stmt.names: # If the import uses an alias, check only that. # Otherwise, check only the import name. if import_alias: if import_alias in global_names: skip = True break elif import_name in global_names: skip = True break if skip: continue # care about functions with unknown argument (builtins) if name in argnames: if is_method: # don't warn for the first argument of a (non static) method if node.type != 'staticmethod' and name == argnames[0]: continue # don't warn for argument of an overridden method if not called_overridden: overridden = overridden_method(klass, node.name) called_overridden = True if overridden is not None and name in overridden.argnames(): continue if node.name in PYMETHODS and node.name not in ('__init__', '__new__'): continue # don't check callback arguments if any(node.name.startswith(cb) or node.name.endswith(cb) for cb in self.config.callbacks): continue self.add_message('unused-argument', args=name, node=stmt, confidence=confidence) else: if stmt.parent and isinstance(stmt.parent, astroid.Assign): if name in nonlocal_names: continue self.add_message('unused-variable', args=name, node=stmt)
def visit_call(self, node): """check that called functions/methods are inferred to callable objects, and that the arguments passed to the function match the parameters in the inferred function's definition """ # Build the set of keyword arguments, checking for duplicate keywords, # and count the positional arguments. call_site = astroid.arguments.CallSite.from_call(node) num_positional_args = len(call_site.positional_arguments) keyword_args = list(call_site.keyword_arguments.keys()) # Determine if we don't have a context for our call and we use variadics. if isinstance(node.scope(), astroid.FunctionDef): has_no_context_positional_variadic = _no_context_variadic_positional(node) has_no_context_keywords_variadic = _no_context_variadic_keywords(node) else: has_no_context_positional_variadic = has_no_context_keywords_variadic = False called = safe_infer(node.func) # only function, generator and object defining __call__ are allowed if called and not called.callable(): if isinstance(called, astroid.Instance) and not has_known_bases(called): # Don't emit if we can't make sure this object is callable. pass else: self.add_message('not-callable', node=node, args=node.func.as_string()) self._check_uninferable_callfunc(node) try: called, implicit_args, callable_name = _determine_callable(called) except ValueError: # Any error occurred during determining the function type, most of # those errors are handled by different warnings. return num_positional_args += implicit_args if called.args.args is None: # Built-in functions have no argument information. return if len(called.argnames()) != len(set(called.argnames())): # Duplicate parameter name (see duplicate-argument). We can't really # make sense of the function call in this case, so just return. return # Warn about duplicated keyword arguments, such as `f=24, **{'f': 24}` for keyword in call_site.duplicated_keywords: self.add_message('repeated-keyword', node=node, args=(keyword, )) if call_site.has_invalid_arguments() or call_site.has_invalid_keywords(): # Can't make sense of this. return # Analyze the list of formal parameters. num_mandatory_parameters = len(called.args.args) - len(called.args.defaults) parameters = [] parameter_name_to_index = {} for i, arg in enumerate(called.args.args): if isinstance(arg, astroid.Tuple): name = None # Don't store any parameter names within the tuple, since those # are not assignable from keyword arguments. else: assert isinstance(arg, astroid.AssignName) # This occurs with: # def f( (a), (b) ): pass name = arg.name parameter_name_to_index[name] = i if i >= num_mandatory_parameters: defval = called.args.defaults[i - num_mandatory_parameters] else: defval = None parameters.append([(name, defval), False]) kwparams = {} for i, arg in enumerate(called.args.kwonlyargs): if isinstance(arg, astroid.Keyword): name = arg.arg else: assert isinstance(arg, astroid.AssignName) name = arg.name kwparams[name] = [called.args.kw_defaults[i], False] # Match the supplied arguments against the function parameters. # 1. Match the positional arguments. for i in range(num_positional_args): if i < len(parameters): parameters[i][1] = True elif called.args.vararg is not None: # The remaining positional arguments get assigned to the *args # parameter. break else: # Too many positional arguments. self.add_message('too-many-function-args', node=node, args=(callable_name,)) break # 2. Match the keyword arguments. for keyword in keyword_args: if keyword in parameter_name_to_index: i = parameter_name_to_index[keyword] if parameters[i][1]: # Duplicate definition of function parameter. # Might be too hardcoded, but this can actually # happen when using str.format and `self` is passed # by keyword argument, as in `.format(self=self)`. # It's perfectly valid to so, so we're just skipping # it if that's the case. if not (keyword == 'self' and called.qname() == STR_FORMAT): self.add_message('redundant-keyword-arg', node=node, args=(keyword, callable_name)) else: parameters[i][1] = True elif keyword in kwparams: if kwparams[keyword][1]: # XXX is that even possible? # Duplicate definition of function parameter. self.add_message('redundant-keyword-arg', node=node, args=(keyword, callable_name)) else: kwparams[keyword][1] = True elif called.args.kwarg is not None: # The keyword argument gets assigned to the **kwargs parameter. pass else: # Unexpected keyword argument. self.add_message('unexpected-keyword-arg', node=node, args=(keyword, callable_name)) # 3. Match the **kwargs, if any. if node.kwargs: for i, [(name, defval), assigned] in enumerate(parameters): # Assume that *kwargs provides values for all remaining # unassigned named parameters. if name is not None: parameters[i][1] = True else: # **kwargs can't assign to tuples. pass # Check that any parameters without a default have been assigned # values. for [(name, defval), assigned] in parameters: if (defval is None) and not assigned: if name is None: display_name = '<tuple>' else: display_name = repr(name) # TODO(cpopa): this should be removed after PyCQA/astroid/issues/177 if not has_no_context_positional_variadic: self.add_message('no-value-for-parameter', node=node, args=(display_name, callable_name)) for name in kwparams: defval, assigned = kwparams[name] if defval is None and not assigned and not has_no_context_keywords_variadic: self.add_message('missing-kwoa', node=node, args=(name, callable_name))
def visit_tryexcept(self, node): """check for empty except""" exceptions_classes = [] nb_handlers = len(node.handlers) for index, handler in enumerate(node.handlers): # single except doing nothing but "pass" without else clause if is_empty(handler.body) and not node.orelse: self.add_message('pointless-except', node=handler.type or handler.body[0]) if handler.type is None: if not is_raising(handler.body): self.add_message('bare-except', node=handler) # check if a "except:" is followed by some other # except if index < (nb_handlers - 1): msg = 'empty except clause should always appear last' self.add_message('bad-except-order', node=node, args=msg) elif isinstance(handler.type, astroid.BoolOp): self.add_message('binary-op-exception', node=handler, args=handler.type.op) else: try: excs = list(_annotated_unpack_infer(handler.type)) except astroid.InferenceError: continue for part, exc in excs: if exc is YES: continue if isinstance(exc, astroid.Instance) and inherit_from_std_ex(exc): exc = exc._proxied if not isinstance(exc, astroid.Class): # Don't emit the warning if the infered stmt # is None, but the exception handler is something else, # maybe it was redefined. if (isinstance(exc, astroid.Const) and exc.value is None): if ((isinstance(handler.type, astroid.Const) and handler.type.value is None) or handler.type.parent_of(exc)): # If the exception handler catches None or # the exception component, which is None, is # defined by the entire exception handler, then # emit a warning. self.add_message('catching-non-exception', node=handler.type, args=(part.as_string(), )) else: self.add_message('catching-non-exception', node=handler.type, args=(part.as_string(), )) continue exc_ancestors = [anc for anc in exc.ancestors() if isinstance(anc, astroid.Class)] for previous_exc in exceptions_classes: if previous_exc in exc_ancestors: msg = '%s is an ancestor class of %s' % ( previous_exc.name, exc.name) self.add_message('bad-except-order', node=handler.type, args=msg) if (exc.name in self.config.overgeneral_exceptions and exc.root().name == EXCEPTIONS_MODULE and not is_raising(handler.body)): self.add_message('broad-except', args=exc.name, node=handler.type) if (not inherit_from_std_ex(exc) and exc.root().name != BUILTINS_NAME): if has_known_bases(exc): self.add_message('catching-non-exception', node=handler.type, args=(exc.name, )) exceptions_classes += [exc for _, exc in excs]
def visit_functiondef(self, node): """check use of super""" # ignore actual functions or method within a new style class if not node.is_method(): return klass = node.parent.frame() for stmt in node.nodes_of_class(astroid.Call): if node_frame_class(stmt) != node_frame_class(node): # Don't look down in other scopes. continue expr = stmt.func if not isinstance(expr, astroid.Attribute): continue call = expr.expr # skip the test if using super if not (isinstance(call, astroid.Call) and isinstance(call.func, astroid.Name) and call.func.name == 'super'): continue if not klass.newstyle and has_known_bases(klass): # super should not be used on an old style class self.add_message('super-on-old-class', node=node) else: # super first arg should be the class if not call.args: if sys.version_info[0] == 3: # unless Python 3 continue else: self.add_message('missing-super-argument', node=call) continue # calling super(type(self), self) can lead to recursion loop # in derived classes arg0 = call.args[0] if isinstance(arg0, astroid.Call) and \ isinstance(arg0.func, astroid.Name) and \ arg0.func.name == 'type': self.add_message('bad-super-call', node=call, args=('type', )) continue # calling super(self.__class__, self) can lead to recursion loop # in derived classes if len(call.args) >= 2 and \ isinstance(call.args[1], astroid.Name) and \ call.args[1].name == 'self' and \ isinstance(arg0, astroid.Attribute) and \ arg0.attrname == '__class__': self.add_message('bad-super-call', node=call, args=('self.__class__', )) continue try: supcls = call.args and next(call.args[0].infer(), None) except astroid.InferenceError: continue if klass is not supcls: name = None # if supcls is not Uninferable, then supcls was infered # and use its name. Otherwise, try to look # for call.args[0].name if supcls: name = supcls.name elif call.args and hasattr(call.args[0], 'name'): name = call.args[0].name if name: self.add_message('bad-super-call', node=call, args=(name, ))
def visit_functiondef(self, node: nodes.FunctionDef) -> None: """check use of super""" # ignore actual functions or method within a new style class if not node.is_method(): return klass = node.parent.frame() for stmt in node.nodes_of_class(nodes.Call): if node_frame_class(stmt) != node_frame_class(node): # Don't look down in other scopes. continue expr = stmt.func if not isinstance(expr, nodes.Attribute): continue call = expr.expr # skip the test if using super if not (isinstance(call, nodes.Call) and isinstance( call.func, nodes.Name) and call.func.name == "super"): continue # super should not be used on an old style class if klass.newstyle or not has_known_bases(klass): # super first arg should not be the class if not call.args: continue # calling super(type(self), self) can lead to recursion loop # in derived classes arg0 = call.args[0] if (isinstance(arg0, nodes.Call) and isinstance(arg0.func, nodes.Name) and arg0.func.name == "type"): self.add_message("bad-super-call", node=call, args=("type", )) continue # calling super(self.__class__, self) can lead to recursion loop # in derived classes if (len(call.args) >= 2 and isinstance(call.args[1], nodes.Name) and call.args[1].name == "self" and isinstance(arg0, nodes.Attribute) and arg0.attrname == "__class__"): self.add_message("bad-super-call", node=call, args=("self.__class__", )) continue try: supcls = call.args and next(call.args[0].infer(), None) except astroid.InferenceError: continue if klass is not supcls: name = None # if supcls is not Uninferable, then supcls was inferred # and use its name. Otherwise, try to look # for call.args[0].name if supcls: name = supcls.name elif call.args and hasattr(call.args[0], "name"): name = call.args[0].name if name: self.add_message("bad-super-call", node=call, args=(name, ))
def visit_classdef(self, cls): if not utils.inherit_from_std_ex(cls) and utils.has_known_bases(cls): if cls.newstyle: self._checker.add_message("raising-non-exception", node=self._node)
def visit_index(self, node): if not node.parent or not hasattr(node.parent, "value"): return # Look for index operations where the parent is a sequence type. # If the types can be determined, only allow indices to be int, # slice or instances with __index__. parent_type = safe_infer(node.parent.value) if not isinstance(parent_type, (astroid.ClassDef, astroid.Instance)): return if not has_known_bases(parent_type): return # Determine what method on the parent this index will use # The parent of this node will be a Subscript, and the parent of that # node determines if the Subscript is a get, set, or delete operation. if node.parent.ctx is astroid.Store: methodname = "__setitem__" elif node.parent.ctx is astroid.Del: methodname = "__delitem__" else: methodname = "__getitem__" # Check if this instance's __getitem__, __setitem__, or __delitem__, as # appropriate to the statement, is implemented in a builtin sequence # type. This way we catch subclasses of sequence types but skip classes # that override __getitem__ and which may allow non-integer indices. try: methods = parent_type.getattr(methodname) if methods is astroid.YES: return itemmethod = methods[0] except (exceptions.NotFoundError, IndexError): return if not isinstance(itemmethod, astroid.FunctionDef): return if itemmethod.root().name != BUILTINS: return if not itemmethod.parent: return if itemmethod.parent.name not in SEQUENCE_TYPES: return # For ExtSlice objects coming from visit_extslice, no further # inference is necessary, since if we got this far the ExtSlice # is an error. if isinstance(node, astroid.ExtSlice): index_type = node else: index_type = safe_infer(node) if index_type is None or index_type is astroid.YES: return # Constants must be of type int if isinstance(index_type, astroid.Const): if isinstance(index_type.value, int): return # Instance values must be int, slice, or have an __index__ method elif isinstance(index_type, astroid.Instance): if index_type.pytype() in (BUILTINS + ".int", BUILTINS + ".slice"): return try: index_type.getattr("__index__") return except exceptions.NotFoundError: pass elif isinstance(index_type, astroid.Slice): # Delegate to visit_slice. A slice can be present # here after inferring the index node, which could # be a `slice(...)` call for instance. return self.visit_slice(index_type) # Anything else is an error self.add_message("invalid-sequence-index", node=node)
def visit_classdef(self, node: nodes.ClassDef) -> None: if not utils.inherit_from_std_ex(node) and utils.has_known_bases(node): if node.newstyle: self._checker.add_message("raising-non-exception", node=self._node)
def leave_function(self, node): """leave function: check function's locals are consumed""" not_consumed = self._to_consume.pop()[0] if not (self.linter.is_message_enabled('unused-variable') or self.linter.is_message_enabled('unused-argument')): return # don't check arguments of function which are only raising an exception if is_error(node): return # don't check arguments of abstract methods or within an interface is_method = node.is_method() klass = node.parent.frame() if is_method and (klass.type == 'interface' or node.is_abstract()): return if is_method and isinstance(klass, astroid.Class): confidence = INFERENCE if has_known_bases( klass) else INFERENCE_FAILURE else: confidence = HIGH authorized_rgx = self.config.dummy_variables_rgx called_overridden = False argnames = node.argnames() global_names = set() nonlocal_names = set() for global_stmt in node.nodes_of_class(astroid.Global): global_names.update(set(global_stmt.names)) for nonlocal_stmt in node.nodes_of_class(astroid.Nonlocal): nonlocal_names.update(set(nonlocal_stmt.names)) for name, stmts in six.iteritems(not_consumed): # ignore some special names specified by user configuration if authorized_rgx.match(name): continue # ignore names imported by the global statement # FIXME: should only ignore them if it's assigned latter stmt = stmts[0] if isinstance(stmt, astroid.Global): continue if isinstance(stmt, (astroid.Import, astroid.From)): # Detect imports, assigned to global statements. if global_names: skip = False for import_name, import_alias in stmt.names: # If the import uses an alias, check only that. # Otherwise, check only the import name. if import_alias: if import_alias in global_names: skip = True break elif import_name in global_names: skip = True break if skip: continue # care about functions with unknown argument (builtins) if name in argnames: if is_method: # don't warn for the first argument of a (non static) method if node.type != 'staticmethod' and name == argnames[0]: continue # don't warn for argument of an overridden method if not called_overridden: overridden = overridden_method(klass, node.name) called_overridden = True if overridden is not None and name in overridden.argnames( ): continue if node.name in PYMETHODS and node.name not in ('__init__', '__new__'): continue # don't check callback arguments XXX should be configurable if node.name.startswith('cb_') or node.name.endswith('_cb'): continue self.add_message('unused-argument', args=name, node=stmt, confidence=confidence) else: if stmt.parent and isinstance(stmt.parent, astroid.Assign): if name in nonlocal_names: continue self.add_message('unused-variable', args=name, node=stmt)
def visit_index(self, node): if not node.parent or not hasattr(node.parent, "value"): return # Look for index operations where the parent is a sequence type. # If the types can be determined, only allow indices to be int, # slice or instances with __index__. parent_type = safe_infer(node.parent.value) if not isinstance(parent_type, (astroid.ClassDef, astroid.Instance)): return if not has_known_bases(parent_type): return # Determine what method on the parent this index will use # The parent of this node will be a Subscript, and the parent of that # node determines if the Subscript is a get, set, or delete operation. if node.parent.ctx is astroid.Store: methodname = '__setitem__' elif node.parent.ctx is astroid.Del: methodname = '__delitem__' else: methodname = '__getitem__' # Check if this instance's __getitem__, __setitem__, or __delitem__, as # appropriate to the statement, is implemented in a builtin sequence # type. This way we catch subclasses of sequence types but skip classes # that override __getitem__ and which may allow non-integer indices. try: methods = parent_type.getattr(methodname) if methods is astroid.YES: return itemmethod = methods[0] except (exceptions.NotFoundError, IndexError): return if not isinstance(itemmethod, astroid.FunctionDef): return if itemmethod.root().name != BUILTINS: return if not itemmethod.parent: return if itemmethod.parent.name not in SEQUENCE_TYPES: return # For ExtSlice objects coming from visit_extslice, no further # inference is necessary, since if we got this far the ExtSlice # is an error. if isinstance(node, astroid.ExtSlice): index_type = node else: index_type = safe_infer(node) if index_type is None or index_type is astroid.YES: return # Constants must be of type int if isinstance(index_type, astroid.Const): if isinstance(index_type.value, int): return # Instance values must be int, slice, or have an __index__ method elif isinstance(index_type, astroid.Instance): if index_type.pytype() in (BUILTINS + '.int', BUILTINS + '.slice'): return try: index_type.getattr('__index__') return except exceptions.NotFoundError: pass elif isinstance(index_type, astroid.Slice): # Delegate to visit_slice. A slice can be present # here after inferring the index node, which could # be a `slice(...)` call for instance. return self.visit_slice(index_type) # Anything else is an error self.add_message('invalid-sequence-index', node=node)