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})
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
 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=[])
Ejemplo n.º 5
0
    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)
Ejemplo n.º 6
0
 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
Ejemplo n.º 7
0
 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=[])
Ejemplo n.º 8
0
 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=[])
Ejemplo n.º 9
0
 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=[])
Ejemplo n.º 10
0
 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=[])
Ejemplo n.º 11
0
    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)
Ejemplo n.º 12
0
 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)
Ejemplo n.º 13
0
 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)
Ejemplo n.º 14
0
    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)
Ejemplo n.º 15
0
 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
Ejemplo n.º 16
0
    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
                    )
Ejemplo n.º 17
0
 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
Ejemplo n.º 18
0
 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)
Ejemplo n.º 19
0
 def _dummy_rgx(self):
     return lint_utils.get_global_option(self,
                                         'dummy-variables-rgx',
                                         default=None)
Ejemplo n.º 20
0
 def open(self):
     self._ignore_mixin_members = utils.get_global_option(self, 'ignore-mixin-members')
Ejemplo n.º 21
0
 def open(self):
     self._ignore_mixin_members = utils.get_global_option(
         self, "ignore-mixin-members"
     )
     self._async_generators = ["contextlib.asynccontextmanager"]
Ejemplo n.º 22
0
 def open(self):
     self._ignore_mixin_members = utils.get_global_option(
         self, "ignore-mixin-members")
     self._async_generators = ["contextlib.asynccontextmanager"]
Ejemplo n.º 23
0
 def open(self):
     self._ignore_mixin_members = utils.get_global_option(
         self, 'ignore-mixin-members')
Ejemplo n.º 24
0
 def _dummy_rgx(self):
     return lint_utils.get_global_option(
         self, 'dummy-variables-rgx', default=None)
Ejemplo n.º 25
0
 def _ignored_argument_names(self):
     return utils.get_global_option(self,
                                    'ignored-argument-names',
                                    default=None)
Ejemplo n.º 26
0
 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]
Ejemplo n.º 27
0
 def _ignored_argument_names(self):
     return utils.get_global_option(self, 'ignored-argument-names', default=None)
Ejemplo n.º 28
0
 def open(self):
     self._mixin_class_rgx = utils.get_global_option(
         self, "mixin-class-rgx")
     self._async_generators = ["contextlib.asynccontextmanager"]
Ejemplo n.º 29
0
    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),
            )
Ejemplo n.º 30
0
    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,
        )