def check_assignment(node): if utils.get_global_option(self, 'class-rgx').match(node.name): return # Type definitions are allowed if they assign to a class name if utils.get_global_option(self, 'const-rgx').match(node.name) or \ re.match('^__[a-z]+__$', node.name): return # Constants are allowed self.add_message('global-variable', node=node, args={'name': node.name})
def _skip_func_docstring(self, node): """True either if the function `node` has a name matching the `no-docstring-rgx` pattern, or a docstring shorter than `docstring-min-length`; False otherwise. """ # skip functions that match the 'no-docstring-rgx' config option no_docstring_rgx = get_global_option(self, "no-docstring-rgx") if no_docstring_rgx and re.match(no_docstring_rgx, node.name): return True # skip functions smaller than 'docstring-min-length' lines = checker_utils.get_node_last_lineno(node) - node.lineno max_lines = get_global_option(self, "docstring-min-length") return max_lines > -1 and lines < max_lines
def leave_classdef(self, cnode): """close a class node: check that instance attributes are defined in __init__ and check access to existent members """ # check access to existent members on non metaclass classes ignore_mixins = get_global_option(self, 'ignore-mixin-members', default=True) if ignore_mixins and cnode.name[-5:].lower() == 'mixin': # We are in a mixin class. No need to try to figure out if # something is missing, since it is most likely that it will # miss. return accessed = self._accessed.pop() if cnode.type != 'metaclass': self._check_accessed_members(cnode, accessed) # checks attributes are defined in an allowed method such as __init__ if not self.linter.is_message_enabled('attribute-defined-outside-init'): return defining_methods = self.config.defining_attr_methods current_module = cnode.root() for attr, nodes in six.iteritems(cnode.instance_attrs): # skip nodes which are not in the current module and it may screw up # the output, while it's not worth it nodes = [n for n in nodes if not isinstance(n.statement(), (astroid.Delete, astroid.AugAssign)) and n.root() is current_module] if not nodes: continue # error detected by typechecking # check if any method attr is defined in is a defining method if any(node.frame().name in defining_methods for node in nodes): continue # check attribute is defined in a parent's __init__ for parent in cnode.instance_attr_ancestors(attr): attr_defined = False # check if any parent method attr is defined in is a defining method for node in parent.instance_attrs[attr]: if node.frame().name in defining_methods: attr_defined = True if attr_defined: # we're done :) break else: # check attribute is defined as a class attribute try: cnode.local_attr(attr) except astroid.NotFoundError: for node in nodes: if node.frame().name not in defining_methods: # If the attribute was set by a callfunc in any # of the defining methods, then don't emit # the warning. if _called_in_methods(node.frame(), cnode, defining_methods): continue self.add_message('attribute-defined-outside-init', args=attr, node=node)
def open(self): """called before visiting project (i.e set of modules)""" self.linter.add_stats(dependencies={}) self.linter.add_stats(cycles=[]) self.stats = self.linter.stats self.import_graph = collections.defaultdict(set) self._ignored_modules = get_global_option(self, "ignored-modules", default=[])
def _check_module_attrs(self, node, module, module_names): """check that module_names (list of string) are accessible through the given module if the latest access name corresponds to a module, return it """ assert isinstance(module, astroid.Module), module ignored_modules = get_global_option(self, 'ignored-modules', default=[]) while module_names: name = module_names.pop(0) if name == '__dict__': module = None break try: module = next(module.getattr(name)[0].infer()) if module is astroid.YES: return None except astroid.NotFoundError: if module.name in ignored_modules: return None self.add_message('no-name-in-module', args=(name, module.name), node=node) return None except astroid.InferenceError: return None if module_names: # FIXME: other message if name is not the latest part of # module_names ? modname = module and module.name or '__dict__' self.add_message('no-name-in-module', node=node, args=('.'.join(module_names), modname)) return None if isinstance(module, astroid.Module): return module return None
def open(self): """initialize visit variables""" self.linter.stats.reset_node_count() self._returns = [] self._branches = defaultdict(int) self._stmts = [] self._exclude_too_few_public_methods = utils.get_global_option( self, "exclude-too-few-public-methods", default=[])
def open(self): """called before visiting project (i.e set of modules)""" self.linter.add_stats(dependencies={}) self.linter.add_stats(cycles=[]) self.stats = self.linter.stats self.import_graph = collections.defaultdict(set) self._ignored_modules = get_global_option( self, 'ignored-modules', default=[])
def open(self): """called before visiting project (i.e set of modules)""" self.linter.add_stats(dependencies={}) self.linter.add_stats(cycles=[]) self.stats = self.linter.stats self.import_graph = collections.defaultdict(set) self._module_pkg = {} # mapping of modules to the pkg they belong in self._excluded_edges = collections.defaultdict(set) self._ignored_modules = get_global_option(self, "ignored-modules", default=[])
def visit_functiondef(self, node: nodes.FunctionDef) -> None: """Called for function and method definitions (def). :param node: Node for a function or method definition in the AST :type node: :class:`astroid.scoped_nodes.Function` """ node_doc = utils.docstringify(node.doc, self.config.default_docstring_type) # skip functions that match the 'no-docstring-rgx' config option no_docstring_rgx = get_global_option(self, "no-docstring-rgx") if no_docstring_rgx and re.match(no_docstring_rgx, node.name): return # skip functions smaller than 'docstring-min-length' lines = checker_utils.get_node_last_lineno(node) - node.lineno max_lines = get_global_option(self, "docstring-min-length") if max_lines > -1 and lines < max_lines: return self.check_functiondef_params(node, node_doc) self.check_functiondef_returns(node, node_doc) self.check_functiondef_yields(node, node_doc)
def open(self): """called before visiting project (i.e set of modules)""" self.linter.add_stats(dependencies={}) self.linter.add_stats(cycles=[]) self.stats = self.linter.stats self.import_graph = collections.defaultdict(set) self._module_pkg = {} # mapping of modules to the pkg they belong in self._excluded_edges = collections.defaultdict(set) self._ignored_modules = get_global_option(self, "ignored-modules", default=[]) # Build a mapping {'module': 'preferred-module'} self.preferred_modules = dict( module.split(":") for module in self.config.preferred_modules if ":" in module)
def open(self): """Called before visiting project (i.e set of modules).""" self.linter.stats.dependencies = {} self.linter.stats = self.linter.stats self.import_graph = collections.defaultdict(set) self._module_pkg = {} # mapping of modules to the pkg they belong in self._excluded_edges = collections.defaultdict(set) self._ignored_modules = get_global_option(self, "ignored-modules", default=[]) # Build a mapping {'module': 'preferred-module'} self.preferred_modules = dict( module.split(":") for module in self.linter.namespace.preferred_modules if ":" in module) self._allow_any_import_level = set( self.linter.namespace.allow_any_import_level)
def get_imported_module(self, importnode, modname): try: return importnode.do_import_module(modname) except astroid.InferenceError as ex: if str(ex) != modname: args = '%r (%s)' % (modname, ex) else: args = repr(modname) ignored_modules = get_global_option(self, 'ignored-modules', default=[]) for submodule in self._qualified_names(modname): if submodule in ignored_modules: return None if not _except_import_error(importnode.parent): self.add_message("import-error", args=args, node=importnode)
def _check_redeclared_assign_name(self, targets): dummy_variables_rgx = lint_utils.get_global_option( self, "dummy-variables-rgx", default=None ) for target in targets: if not isinstance(target, nodes.Tuple): continue found_names = [] for element in target.elts: if isinstance(element, nodes.Tuple): self._check_redeclared_assign_name([element]) elif isinstance(element, nodes.AssignName) and element.name != "_": if dummy_variables_rgx and dummy_variables_rgx.match(element.name): return found_names.append(element.name) names = collections.Counter(found_names) for name, count in names.most_common(): if count > 1: self.add_message( "redeclared-assigned-name", args=(name,), node=target )
def _lazy_load_config(self): if self._config: return self._config with open(get_global_option(self, "global-config-path")) as f: self._config = yaml.safe_load(f) return self._config
def open(self) -> None: """Initialize visit variables and statistics.""" py_version = get_global_option(self, "py-version") self._py36_plus = py_version >= (3, 6) self._py38_plus = py_version >= (3, 8)
def _dummy_rgx(self): return lint_utils.get_global_option(self, 'dummy-variables-rgx', default=None)
def open(self): self._ignore_mixin_members = utils.get_global_option(self, 'ignore-mixin-members')
def open(self): self._ignore_mixin_members = utils.get_global_option( self, "ignore-mixin-members" ) self._async_generators = ["contextlib.asynccontextmanager"]
def open(self): self._ignore_mixin_members = utils.get_global_option( self, "ignore-mixin-members") self._async_generators = ["contextlib.asynccontextmanager"]
def open(self): self._ignore_mixin_members = utils.get_global_option( self, 'ignore-mixin-members')
def _dummy_rgx(self): return lint_utils.get_global_option( self, 'dummy-variables-rgx', default=None)
def _ignored_argument_names(self): return utils.get_global_option(self, 'ignored-argument-names', default=None)
def test_get_global_option(self) -> None: """Test that get_global_option emits a DeprecationWarning.""" checker = BaseChecker(self.linter) with pytest.warns(DeprecationWarning): get_global_option(checker, "test-opt") # type: ignore[call-overload]
def open(self): self._mixin_class_rgx = utils.get_global_option( self, "mixin-class-rgx") self._async_generators = ["contextlib.asynccontextmanager"]
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), )
def check_arguments_in_docstring( self, doc: Docstring, arguments_node: astroid.Arguments, warning_node: astroid.NodeNG, accept_no_param_doc: Optional[bool] = None, ): """Check that all parameters are consistent with the parameters mentioned in the parameter documentation (e.g. the Sphinx tags 'param' and 'type'). * Undocumented parameters except 'self' are noticed. * Undocumented parameter types except for 'self' and the ``*<args>`` and ``**<kwargs>`` parameters are noticed. * Parameters mentioned in the parameter documentation that don't or no longer exist in the function parameter list are noticed. * If the text "For the parameters, see" or "For the other parameters, see" (ignoring additional whitespace) is mentioned in the docstring, missing parameter documentation is tolerated. * If there's no Sphinx style, Google style or NumPy style parameter documentation at all, i.e. ``:param`` is never mentioned etc., the checker assumes that the parameters are documented in another format and the absence is tolerated. :param doc: Docstring for the function, method or class. :type doc: :class:`Docstring` :param arguments_node: Arguments node for the function, method or class constructor. :type arguments_node: :class:`astroid.scoped_nodes.Arguments` :param warning_node: The node to assign the warnings to :type warning_node: :class:`astroid.scoped_nodes.Node` :param accept_no_param_doc: Whether to allow no parameters to be documented. If None then this value is read from the configuration. :type accept_no_param_doc: bool or None """ # Tolerate missing param or type declarations if there is a link to # another method carrying the same name. if not doc.doc: return if accept_no_param_doc is None: accept_no_param_doc = self.linter.namespace.accept_no_param_doc tolerate_missing_params = doc.params_documented_elsewhere() # Collect the function arguments. expected_argument_names = {arg.name for arg in arguments_node.args} expected_argument_names.update(arg.name for arg in arguments_node.kwonlyargs) not_needed_type_in_docstring = self.not_needed_param_in_docstring.copy( ) expected_but_ignored_argument_names = set() ignored_argument_names = get_global_option(self, "ignored-argument-names") if ignored_argument_names: expected_but_ignored_argument_names = { arg for arg in expected_argument_names if ignored_argument_names.match(arg) } if arguments_node.vararg is not None: expected_argument_names.add(f"*{arguments_node.vararg}") not_needed_type_in_docstring.add(f"*{arguments_node.vararg}") if arguments_node.kwarg is not None: expected_argument_names.add(f"**{arguments_node.kwarg}") not_needed_type_in_docstring.add(f"**{arguments_node.kwarg}") params_with_doc, params_with_type = doc.match_param_docs() # Tolerate no parameter documentation at all. if not params_with_doc and not params_with_type and accept_no_param_doc: tolerate_missing_params = True # This is before the update of param_with_type because this must check only # the type documented in a docstring, not the one using pep484 # See #4117 and #4593 self._compare_ignored_args( params_with_type, "useless-type-doc", expected_but_ignored_argument_names, warning_node, ) for index, arg_name in enumerate(arguments_node.args): if arguments_node.annotations[index]: params_with_type.add(arg_name.name) for index, arg_name in enumerate(arguments_node.kwonlyargs): if arguments_node.kwonlyargs_annotations[index]: params_with_type.add(arg_name.name) if not tolerate_missing_params: missing_param_doc = (expected_argument_names - params_with_doc) - ( self.not_needed_param_in_docstring | expected_but_ignored_argument_names) missing_type_doc = (expected_argument_names - params_with_type) - ( not_needed_type_in_docstring | expected_but_ignored_argument_names) if (missing_param_doc == expected_argument_names == missing_type_doc and len(expected_argument_names) != 0): self.add_message( "missing-any-param-doc", args=(warning_node.name, ), node=warning_node, ) else: self._compare_missing_args( params_with_doc, "missing-param-doc", self.not_needed_param_in_docstring | expected_but_ignored_argument_names, expected_argument_names, warning_node, ) self._compare_missing_args( params_with_type, "missing-type-doc", not_needed_type_in_docstring | expected_but_ignored_argument_names, expected_argument_names, warning_node, ) self._compare_different_args( params_with_doc, "differing-param-doc", self.not_needed_param_in_docstring, expected_argument_names, warning_node, ) self._compare_different_args( params_with_type, "differing-type-doc", not_needed_type_in_docstring, expected_argument_names, warning_node, ) self._compare_ignored_args( params_with_doc, "useless-param-doc", expected_but_ignored_argument_names, warning_node, )