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, astroid.AsyncFunctionDef): # Check if we are dealing with a function decorated # with contextlib.asynccontextmanager. if decorated_with(inferred, self._async_generators): continue elif 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_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_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_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 _check_unexpected_method_signature(self, node): expected_params = SPECIAL_METHODS_PARAMS[node.name] if expected_params is None: # This can support a variable number of parameters. return if not node.args.args and not node.args.vararg: # Method has no parameter, will be caught # by no-method-argument. return if decorated_with(node, ["builtins.staticmethod"]): # We expect to not take in consideration self. all_args = node.args.args else: all_args = node.args.args[1:] mandatory = len(all_args) - len(node.args.defaults) optional = len(node.args.defaults) current_params = mandatory + optional emit = False # If we don't know we choose a false negative if isinstance(expected_params, tuple): # The expected number of parameters can be any value from this # tuple, although the user should implement the method # to take all of them in consideration. emit = mandatory not in expected_params # pylint: disable-next=consider-using-f-string expected_params = "between %d or %d" % expected_params else: # If the number of mandatory parameters doesn't # suffice, the expected parameters for this # function will be deduced from the optional # parameters. rest = expected_params - mandatory if rest == 0: emit = False elif rest < 0: emit = True elif rest > 0: emit = not ((optional - rest) >= 0 or node.args.vararg) if emit: verb = "was" if current_params <= 1 else "were" self.add_message( "unexpected-special-method-signature", args=(node.name, expected_params, current_params, verb), node=node, )
def _check_unexpected_method_signature(self, node): expected_params = SPECIAL_METHODS_PARAMS[node.name] if expected_params is None: # This can support a variable number of parameters. return if not len(node.args.args) and not node.args.vararg: # Method has no parameter, will be catched # by no-method-argument. return if decorated_with(node, [BUILTINS + ".staticmethod"]): # We expect to not take in consideration self. all_args = node.args.args else: all_args = node.args.args[1:] mandatory = len(all_args) - len(node.args.defaults) optional = len(node.args.defaults) current_params = mandatory + optional if isinstance(expected_params, tuple): # The expected number of parameters can be any value from this # tuple, although the user should implement the method # to take all of them in consideration. emit = mandatory not in expected_params expected_params = "between %d or %d" % expected_params else: # If the number of mandatory parameters doesn't # suffice, the expected parameters for this # function will be deduced from the optional # parameters. rest = expected_params - mandatory if rest == 0: emit = False elif rest < 0: emit = True elif rest > 0: emit = not ((optional - rest) >= 0 or node.args.vararg) if emit: verb = "was" if current_params <= 1 else "were" self.add_message('unexpected-special-method-signature', args=(node.name, expected_params, current_params, verb), node=node)
def visit_functiondef(self, node): if node.is_method(): if node.name in self._unused_magic_methods: method_name = node.name if node.name.startswith('__'): method_name = node.name[2:-2] self.add_message(method_name + '-method', node=node) elif node.name == 'next': # If there is a method named `next` declared, if it is invokable # with zero arguments then it implements the Iterator protocol. # This means if the method is an instance method or a # classmethod 1 argument should cause a failure, if it is a # staticmethod 0 arguments should cause a failure. failing_arg_count = 1 if utils.decorated_with(node, [bases.BUILTINS + ".staticmethod"]): failing_arg_count = 0 if len(node.args.args) == failing_arg_count: self.add_message('next-method-defined', node=node)
def visit_functiondef(self, node): if node.is_method(): if node.name in self._unused_magic_methods: method_name = node.name if node.name.startswith('__'): method_name = node.name[2:-2] self.add_message(method_name + '-method', node=node) elif node.name == 'next': # If there is a method named `next` declared, if it is invokable # with zero arguments then it implements the Iterator protocol. # This means if the method is a instance method or a # classmethod 1 argument should cause a failure, if it is a # staticmethod 0 arguments should cause a failure. failing_arg_count = 1 if utils.decorated_with(node, [bases.BUILTINS + ".staticmethod"]): failing_arg_count = 0 if len(node.args.args) == failing_arg_count: self.add_message('next-method-defined', node=node)
def visit_with(self, node): for ctx_mgr, _ in node.items: infered = safe_infer(ctx_mgr) if infered is None or infered is astroid.YES: continue # Check if we are dealing with a function decorated # with contextlib.contextmanager. if isinstance(infered, astroid.bases.Generator): func = infered.parent if not decorated_with(func, ['contextlib.contextmanager']): self.add_message('not-context-manager', node=node, args=(infered.name, )) else: try: infered.getattr('__enter__') infered.getattr('__exit__') except astroid.NotFoundError: self.add_message('not-context-manager', node=node, args=(infered.name, ))