Ejemplo n.º 1
0
    def visit_classdef(self, node: nodes.ClassDef) -> None:
        """Visit an astroid.Class node.

        * set the locals_type and instance_attrs_type mappings
        * set the implements list and build it
        * optionally tag the node with a unique id
        """
        if hasattr(node, "locals_type"):
            return
        node.locals_type = collections.defaultdict(list)
        if self.tag:
            node.uid = self.generate_id()
        # resolve ancestors
        for baseobj in node.ancestors(recurs=False):
            specializations = getattr(baseobj, "specializations", [])
            specializations.append(node)
            baseobj.specializations = specializations
        # resolve instance attributes
        node.instance_attrs_type = collections.defaultdict(list)
        for assignattrs in node.instance_attrs.values():
            for assignattr in assignattrs:
                if not isinstance(assignattr, nodes.Unknown):
                    self.handle_assignattr_type(assignattr, node)
        # resolve implemented interface
        try:
            node.implements = list(interfaces(node, self.inherited_interfaces))
        except astroid.InferenceError:
            node.implements = []
Ejemplo n.º 2
0
def _get_parents_iter(
        node: nodes.ClassDef,
        ignored_parents: FrozenSet[str]) -> Iterator[nodes.ClassDef]:
    r"""Get parents of ``node``, excluding ancestors of ``ignored_parents``.

    If we have the following inheritance diagram:

             F
            /
        D  E
         \/
          B  C
           \/
            A      # class A(B, C): ...

    And ``ignored_parents`` is ``{"E"}``, then this function will return
    ``{A, B, C, D}`` -- both ``E`` and its ancestors are excluded.
    """
    parents: Set[nodes.ClassDef] = set()
    to_explore = cast(List[nodes.ClassDef], list(node.ancestors(recurs=False)))
    while to_explore:
        parent = to_explore.pop()
        if parent.qname() in ignored_parents:
            continue
        if parent not in parents:
            # This guard might appear to be performing the same function as
            # adding the resolved parents to a set to eliminate duplicates
            # (legitimate due to diamond inheritance patterns), but its
            # additional purpose is to prevent cycles (not normally possible,
            # but potential due to inference) and thus guarantee termination
            # of the while-loop
            yield parent
            parents.add(parent)
            to_explore.extend(parent.ancestors(recurs=False))
Ejemplo n.º 3
0
 def visit_classdef(self, node: nodes.ClassDef) -> None:
     """Called when a ClassDef node is visited."""
     ancestor: nodes.ClassDef
     for ancestor in node.ancestors():
         for class_matches in self._class_matchers:
             if ancestor.name == class_matches.base_class:
                 self._visit_class_functions(node, class_matches.matches)
Ejemplo n.º 4
0
    def visit_classdef(self, node: nodes.ClassDef):
        """check size of inheritance hierarchy and number of instance attributes"""
        nb_parents = sum(
            1 for ancestor in node.ancestors()
            if ancestor.qname() not in STDLIB_CLASSES_IGNORE_ANCESTOR)
        if nb_parents > self.config.max_parents:
            self.add_message(
                "too-many-ancestors",
                node=node,
                args=(nb_parents, self.config.max_parents),
            )

        if len(node.instance_attrs) > self.config.max_attributes:
            self.add_message(
                "too-many-instance-attributes",
                node=node,
                args=(len(node.instance_attrs), self.config.max_attributes),
            )
Ejemplo n.º 5
0
    def leave_classdef(self, node: nodes.ClassDef) -> None:
        """check number of public methods"""
        my_methods = sum(1 for method in node.mymethods()
                         if not method.name.startswith("_"))

        # Does the class contain less than n public methods ?
        # This checks only the methods defined in the current class,
        # since the user might not have control over the classes
        # from the ancestors. It avoids some false positives
        # for classes such as unittest.TestCase, which provides
        # a lot of assert methods. It doesn't make sense to warn
        # when the user subclasses TestCase to add his own tests.
        if my_methods > self.config.max_public_methods:
            self.add_message(
                "too-many-public-methods",
                node=node,
                args=(my_methods, self.config.max_public_methods),
            )

        # Stop here if the class is excluded via configuration.
        if node.type == "class" and self._exclude_too_few_public_methods:
            for ancestor in node.ancestors():
                if any(
                        pattern.match(ancestor.qname())
                        for pattern in self._exclude_too_few_public_methods):
                    return

        # Stop here for exception, metaclass, interface classes and other
        # classes for which we don't need to count the methods.
        if node.type != "class" or _is_exempt_from_public_methods(node):
            return

        # Does the class contain more than n public methods ?
        # This checks all the methods defined by ancestors and
        # by the current class.
        all_methods = _count_methods_in_class(node)
        if all_methods < self.config.min_public_methods:
            self.add_message(
                "too-few-public-methods",
                node=node,
                args=(all_methods, self.config.min_public_methods),
            )
Ejemplo n.º 6
0
    def visit_classdef(self, node: nodes.ClassDef) -> None:
        """Visit an astroid.Class node.

        * set the locals_type and instance_attrs_type mappings
        * set the implements list and build it
        * optionally tag the node with a unique id
        """
        if hasattr(node, "locals_type"):
            return
        node.locals_type = collections.defaultdict(list)
        if self.tag:
            node.uid = self.generate_id()
        # resolve ancestors
        for baseobj in node.ancestors(recurs=False):
            specializations = getattr(baseobj, "specializations", [])
            specializations.append(node)
            baseobj.specializations = specializations
        # resolve instance attributes
        node.instance_attrs_type = collections.defaultdict(list)
        for assignattrs in tuple(node.instance_attrs.values()):
            for assignattr in assignattrs:
                if not isinstance(assignattr, nodes.Unknown):
                    self.handle_assignattr_type(assignattr, node)
        # resolve implemented interface
        try:
            ifaces = interfaces(node)
            if ifaces is not None:
                node.implements = list(ifaces)
                if node.implements:
                    # TODO: 3.0: Remove support for __implements__
                    warnings.warn(
                        "pyreverse will drop support for resolving and displaying implemented interfaces in pylint 3.0. "
                        "The implementation relies on the '__implements__'  attribute proposed in PEP 245, which was rejected "
                        "in 2006.",
                        DeprecationWarning,
                    )
            else:
                node.implements = []
        except astroid.InferenceError:
            node.implements = []
Ejemplo n.º 7
0
Archivo: app.py Proyecto: 45deg/pypuml
    def visit_classdef(self, ast: ast_node.ClassDef):

        self.write("class %s {" % ast.name)
        self.indent += 1

        for instance_attrs in ast.instance_attrs.values():
            for attr in instance_attrs:
                if isinstance(attr, ast_node.AssignAttr):
                    attr.accept(self)
                    break

        for local in ast.values():
            local.accept(self)

        self.indent -= 1
        self.write("}")

        for anc in ast.ancestors(recurs=False):
            cls = next(anc.infer())
            if isinstance(cls, ast_node.ClassDef):
                module = cls.root().name
                if is_target_module(module):
                    self.write("%s <|- %s.%s" % (ast.name, cls.root().name, cls.name))
Ejemplo n.º 8
0
def _process_class_member_and_attrs(cldef: ClassDef,
                                    pkgfiles: Set[str]) -> Set[str]:
    """
    Determine the class attributes, methods and instance attributes defined by
    a class. The function will inspect and populate them from the parents while
    the parent is defined by a module file in the same mmpack package.

    This function is called recursively to populate a class.

    Args:
        cldef: the astroid node defining the class
        pkgfiles: set of files in the same mmpack package

    Returns:
        set of name corresponding to the class attributes and methods and
        instance attributes.
    """
    syms = set()

    # add member and attributes from parent classes implemented in files of
    # the same package
    for base in cldef.ancestors(recurs=False):
        mod = base.root()
        if _is_module_packaged(mod, pkgfiles):
            syms.update(_process_class_member_and_attrs(base, pkgfiles))

    # Add public class attributes
    for attr in cldef.locals:
        if (isinstance(cldef.locals[attr][-1], AssignName)
                and _is_public_sym(attr)):
            syms.add(attr)

    # Add public class methods and instance attributes
    syms.update({m.name for m in cldef.mymethods() if _is_public_sym(m.name)})
    syms.update({a for a in cldef.instance_attrs if _is_public_sym(a)})

    return syms
Ejemplo n.º 9
0
 def base_classes_of_node(instance: nodes.ClassDef) -> List[nodes.Name]:
     """Return all the classes names that a ClassDef inherit from including 'object'."""
     try:
         return [instance.name] + [x.name for x in instance.ancestors()]
     except TypeError:
         return [instance.name]