Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

diadefslib.py 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. # -*- coding: utf-8 -*-
  2. # Copyright (c) 2006, 2008-2010, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
  3. # Copyright (c) 2014 Brett Cannon <brett@python.org>
  4. # Copyright (c) 2014 Arun Persaud <arun@nubati.net>
  5. # Copyright (c) 2015-2017 Claudiu Popa <pcmanticore@gmail.com>
  6. # Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
  7. # Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
  8. # Copyright (c) 2016 Ashley Whetter <ashley@awhetter.co.uk>
  9. # Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
  10. # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  11. # For details: https://github.com/PyCQA/pylint/blob/master/COPYING
  12. """handle diagram generation options for class diagram or default diagrams
  13. """
  14. from six.moves import builtins
  15. import astroid
  16. from pylint.pyreverse.diagrams import PackageDiagram, ClassDiagram
  17. from pylint.pyreverse.utils import LocalsVisitor
  18. BUILTINS_NAME = builtins.__name__
  19. # diagram generators ##########################################################
  20. class DiaDefGenerator(object):
  21. """handle diagram generation options"""
  22. def __init__(self, linker, handler):
  23. """common Diagram Handler initialization"""
  24. self.config = handler.config
  25. self._set_default_options()
  26. self.linker = linker
  27. self.classdiagram = None # defined by subclasses
  28. def get_title(self, node):
  29. """get title for objects"""
  30. title = node.name
  31. if self.module_names:
  32. title = '%s.%s' % (node.root().name, title)
  33. return title
  34. def _set_option(self, option):
  35. """activate some options if not explicitly deactivated"""
  36. # if we have a class diagram, we want more information by default;
  37. # so if the option is None, we return True
  38. if option is None:
  39. return bool(self.config.classes)
  40. return option
  41. def _set_default_options(self):
  42. """set different default options with _default dictionary"""
  43. self.module_names = self._set_option(self.config.module_names)
  44. all_ancestors = self._set_option(self.config.all_ancestors)
  45. all_associated = self._set_option(self.config.all_associated)
  46. anc_level, ass_level = (0, 0)
  47. if all_ancestors:
  48. anc_level = -1
  49. if all_associated:
  50. ass_level = -1
  51. if self.config.show_ancestors is not None:
  52. anc_level = self.config.show_ancestors
  53. if self.config.show_associated is not None:
  54. ass_level = self.config.show_associated
  55. self.anc_level, self.ass_level = anc_level, ass_level
  56. def _get_levels(self):
  57. """help function for search levels"""
  58. return self.anc_level, self.ass_level
  59. def show_node(self, node):
  60. """true if builtins and not show_builtins"""
  61. if self.config.show_builtin:
  62. return True
  63. return node.root().name != BUILTINS_NAME
  64. def add_class(self, node):
  65. """visit one class and add it to diagram"""
  66. self.linker.visit(node)
  67. self.classdiagram.add_object(self.get_title(node), node)
  68. def get_ancestors(self, node, level):
  69. """return ancestor nodes of a class node"""
  70. if level == 0:
  71. return
  72. for ancestor in node.ancestors(recurs=False):
  73. if not self.show_node(ancestor):
  74. continue
  75. yield ancestor
  76. def get_associated(self, klass_node, level):
  77. """return associated nodes of a class node"""
  78. if level == 0:
  79. return
  80. for ass_nodes in list(klass_node.instance_attrs_type.values()) + \
  81. list(klass_node.locals_type.values()):
  82. for ass_node in ass_nodes:
  83. if isinstance(ass_node, astroid.Instance):
  84. ass_node = ass_node._proxied
  85. if not (isinstance(ass_node, astroid.ClassDef)
  86. and self.show_node(ass_node)):
  87. continue
  88. yield ass_node
  89. def extract_classes(self, klass_node, anc_level, ass_level):
  90. """extract recursively classes related to klass_node"""
  91. if self.classdiagram.has_node(klass_node) or not self.show_node(klass_node):
  92. return
  93. self.add_class(klass_node)
  94. for ancestor in self.get_ancestors(klass_node, anc_level):
  95. self.extract_classes(ancestor, anc_level-1, ass_level)
  96. for ass_node in self.get_associated(klass_node, ass_level):
  97. self.extract_classes(ass_node, anc_level, ass_level-1)
  98. class DefaultDiadefGenerator(LocalsVisitor, DiaDefGenerator):
  99. """generate minimum diagram definition for the project :
  100. * a package diagram including project's modules
  101. * a class diagram including project's classes
  102. """
  103. def __init__(self, linker, handler):
  104. DiaDefGenerator.__init__(self, linker, handler)
  105. LocalsVisitor.__init__(self)
  106. def visit_project(self, node):
  107. """visit an pyreverse.utils.Project node
  108. create a diagram definition for packages
  109. """
  110. mode = self.config.mode
  111. if len(node.modules) > 1:
  112. self.pkgdiagram = PackageDiagram('packages %s' % node.name, mode)
  113. else:
  114. self.pkgdiagram = None
  115. self.classdiagram = ClassDiagram('classes %s' % node.name, mode)
  116. def leave_project(self, node): # pylint: disable=unused-argument
  117. """leave the pyreverse.utils.Project node
  118. return the generated diagram definition
  119. """
  120. if self.pkgdiagram:
  121. return self.pkgdiagram, self.classdiagram
  122. return self.classdiagram,
  123. def visit_module(self, node):
  124. """visit an astroid.Module node
  125. add this class to the package diagram definition
  126. """
  127. if self.pkgdiagram:
  128. self.linker.visit(node)
  129. self.pkgdiagram.add_object(node.name, node)
  130. def visit_classdef(self, node):
  131. """visit an astroid.Class node
  132. add this class to the class diagram definition
  133. """
  134. anc_level, ass_level = self._get_levels()
  135. self.extract_classes(node, anc_level, ass_level)
  136. def visit_importfrom(self, node):
  137. """visit astroid.ImportFrom and catch modules for package diagram
  138. """
  139. if self.pkgdiagram:
  140. self.pkgdiagram.add_from_depend(node, node.modname)
  141. class ClassDiadefGenerator(DiaDefGenerator):
  142. """generate a class diagram definition including all classes related to a
  143. given class
  144. """
  145. def __init__(self, linker, handler):
  146. DiaDefGenerator.__init__(self, linker, handler)
  147. def class_diagram(self, project, klass):
  148. """return a class diagram definition for the given klass and its
  149. related klasses
  150. """
  151. self.classdiagram = ClassDiagram(klass, self.config.mode)
  152. if len(project.modules) > 1:
  153. module, klass = klass.rsplit('.', 1)
  154. module = project.get_module(module)
  155. else:
  156. module = project.modules[0]
  157. klass = klass.split('.')[-1]
  158. klass = next(module.ilookup(klass))
  159. anc_level, ass_level = self._get_levels()
  160. self.extract_classes(klass, anc_level, ass_level)
  161. return self.classdiagram
  162. # diagram handler #############################################################
  163. class DiadefsHandler(object):
  164. """handle diagram definitions :
  165. get it from user (i.e. xml files) or generate them
  166. """
  167. def __init__(self, config):
  168. self.config = config
  169. def get_diadefs(self, project, linker):
  170. """Get the diagrams configuration data
  171. :param project:The pyreverse project
  172. :type project: pyreverse.utils.Project
  173. :param linker: The linker
  174. :type linker: pyreverse.inspector.Linker(IdGeneratorMixIn, LocalsVisitor)
  175. :returns: The list of diagram definitions
  176. :rtype: list(:class:`pylint.pyreverse.diagrams.ClassDiagram`)
  177. """
  178. # read and interpret diagram definitions (Diadefs)
  179. diagrams = []
  180. generator = ClassDiadefGenerator(linker, self)
  181. for klass in self.config.classes:
  182. diagrams.append(generator.class_diagram(project, klass))
  183. if not diagrams:
  184. diagrams = DefaultDiadefGenerator(linker, self).visit(project)
  185. for diagram in diagrams:
  186. diagram.extract_relationships()
  187. return diagrams