diff options
Diffstat (limited to 'venv/Lib/site-packages/pylint/pyreverse/diadefslib.py')
-rw-r--r-- | venv/Lib/site-packages/pylint/pyreverse/diadefslib.py | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/venv/Lib/site-packages/pylint/pyreverse/diadefslib.py b/venv/Lib/site-packages/pylint/pyreverse/diadefslib.py new file mode 100644 index 0000000..de4e9fd --- /dev/null +++ b/venv/Lib/site-packages/pylint/pyreverse/diadefslib.py @@ -0,0 +1,238 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2006, 2008-2010, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr> +# Copyright (c) 2014 Brett Cannon <brett@python.org> +# Copyright (c) 2014 Arun Persaud <arun@nubati.net> +# Copyright (c) 2015-2018 Claudiu Popa <pcmanticore@gmail.com> +# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org> +# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro> +# Copyright (c) 2016 Ashley Whetter <ashley@awhetter.co.uk> +# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com> +# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com> +# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com> +# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com> + +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/master/COPYING + +"""handle diagram generation options for class diagram or default diagrams +""" + +import astroid + +from pylint.pyreverse.diagrams import ClassDiagram, PackageDiagram +from pylint.pyreverse.utils import LocalsVisitor + +BUILTINS_NAME = "builtins" + +# diagram generators ########################################################## + + +class DiaDefGenerator: + """handle diagram generation options""" + + def __init__(self, linker, handler): + """common Diagram Handler initialization""" + self.config = handler.config + self._set_default_options() + self.linker = linker + self.classdiagram = None # defined by subclasses + + def get_title(self, node): + """get title for objects""" + title = node.name + if self.module_names: + title = "%s.%s" % (node.root().name, title) + return title + + def _set_option(self, option): + """activate some options if not explicitly deactivated""" + # if we have a class diagram, we want more information by default; + # so if the option is None, we return True + if option is None: + return bool(self.config.classes) + return option + + def _set_default_options(self): + """set different default options with _default dictionary""" + self.module_names = self._set_option(self.config.module_names) + all_ancestors = self._set_option(self.config.all_ancestors) + all_associated = self._set_option(self.config.all_associated) + anc_level, association_level = (0, 0) + if all_ancestors: + anc_level = -1 + if all_associated: + association_level = -1 + if self.config.show_ancestors is not None: + anc_level = self.config.show_ancestors + if self.config.show_associated is not None: + association_level = self.config.show_associated + self.anc_level, self.association_level = anc_level, association_level + + def _get_levels(self): + """help function for search levels""" + return self.anc_level, self.association_level + + def show_node(self, node): + """true if builtins and not show_builtins""" + if self.config.show_builtin: + return True + return node.root().name != BUILTINS_NAME + + def add_class(self, node): + """visit one class and add it to diagram""" + self.linker.visit(node) + self.classdiagram.add_object(self.get_title(node), node) + + def get_ancestors(self, node, level): + """return ancestor nodes of a class node""" + if level == 0: + return + for ancestor in node.ancestors(recurs=False): + if not self.show_node(ancestor): + continue + yield ancestor + + def get_associated(self, klass_node, level): + """return associated nodes of a class node""" + if level == 0: + return + for association_nodes in list(klass_node.instance_attrs_type.values()) + list( + klass_node.locals_type.values() + ): + for node in association_nodes: + if isinstance(node, astroid.Instance): + node = node._proxied + if not (isinstance(node, astroid.ClassDef) and self.show_node(node)): + continue + yield node + + def extract_classes(self, klass_node, anc_level, association_level): + """extract recursively classes related to klass_node""" + if self.classdiagram.has_node(klass_node) or not self.show_node(klass_node): + return + self.add_class(klass_node) + + for ancestor in self.get_ancestors(klass_node, anc_level): + self.extract_classes(ancestor, anc_level - 1, association_level) + + for node in self.get_associated(klass_node, association_level): + self.extract_classes(node, anc_level, association_level - 1) + + +class DefaultDiadefGenerator(LocalsVisitor, DiaDefGenerator): + """generate minimum diagram definition for the project : + + * a package diagram including project's modules + * a class diagram including project's classes + """ + + def __init__(self, linker, handler): + DiaDefGenerator.__init__(self, linker, handler) + LocalsVisitor.__init__(self) + + def visit_project(self, node): + """visit a pyreverse.utils.Project node + + create a diagram definition for packages + """ + mode = self.config.mode + if len(node.modules) > 1: + self.pkgdiagram = PackageDiagram("packages %s" % node.name, mode) + else: + self.pkgdiagram = None + self.classdiagram = ClassDiagram("classes %s" % node.name, mode) + + def leave_project(self, node): # pylint: disable=unused-argument + """leave the pyreverse.utils.Project node + + return the generated diagram definition + """ + if self.pkgdiagram: + return self.pkgdiagram, self.classdiagram + return (self.classdiagram,) + + def visit_module(self, node): + """visit an astroid.Module node + + add this class to the package diagram definition + """ + if self.pkgdiagram: + self.linker.visit(node) + self.pkgdiagram.add_object(node.name, node) + + def visit_classdef(self, node): + """visit an astroid.Class node + + add this class to the class diagram definition + """ + anc_level, association_level = self._get_levels() + self.extract_classes(node, anc_level, association_level) + + def visit_importfrom(self, node): + """visit astroid.ImportFrom and catch modules for package diagram + """ + if self.pkgdiagram: + self.pkgdiagram.add_from_depend(node, node.modname) + + +class ClassDiadefGenerator(DiaDefGenerator): + """generate a class diagram definition including all classes related to a + given class + """ + + def __init__(self, linker, handler): + DiaDefGenerator.__init__(self, linker, handler) + + def class_diagram(self, project, klass): + """return a class diagram definition for the given klass and its + related klasses + """ + + self.classdiagram = ClassDiagram(klass, self.config.mode) + if len(project.modules) > 1: + module, klass = klass.rsplit(".", 1) + module = project.get_module(module) + else: + module = project.modules[0] + klass = klass.split(".")[-1] + klass = next(module.ilookup(klass)) + + anc_level, association_level = self._get_levels() + self.extract_classes(klass, anc_level, association_level) + return self.classdiagram + + +# diagram handler ############################################################# + + +class DiadefsHandler: + """handle diagram definitions : + + get it from user (i.e. xml files) or generate them + """ + + def __init__(self, config): + self.config = config + + def get_diadefs(self, project, linker): + """Get the diagrams configuration data + + :param project:The pyreverse project + :type project: pyreverse.utils.Project + :param linker: The linker + :type linker: pyreverse.inspector.Linker(IdGeneratorMixIn, LocalsVisitor) + + :returns: The list of diagram definitions + :rtype: list(:class:`pylint.pyreverse.diagrams.ClassDiagram`) + """ + + # read and interpret diagram definitions (Diadefs) + diagrams = [] + generator = ClassDiadefGenerator(linker, self) + for klass in self.config.classes: + diagrams.append(generator.class_diagram(project, klass)) + if not diagrams: + diagrams = DefaultDiadefGenerator(linker, self).visit(project) + for diagram in diagrams: + diagram.extract_relationships() + return diagrams |