gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / pylint / pyreverse / diadefslib.py @ 1026
History | View | Annotate | Download (8.15 KB)
1 | 745 | jjdelcerro | # Copyright (c) 2000-2013 LOGILAB S.A. (Paris, FRANCE).
|
---|---|---|---|
2 | # http://www.logilab.fr/ -- mailto:contact@logilab.fr
|
||
3 | #
|
||
4 | # This program is free software; you can redistribute it and/or modify it under
|
||
5 | # the terms of the GNU General Public License as published by the Free Software
|
||
6 | # Foundation; either version 2 of the License, or (at your option) any later
|
||
7 | # version.
|
||
8 | #
|
||
9 | # This program is distributed in the hope that it will be useful, but WITHOUT
|
||
10 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||
11 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||
12 | #
|
||
13 | # You should have received a copy of the GNU General Public License along with
|
||
14 | # this program; if not, write to the Free Software Foundation, Inc.,
|
||
15 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
16 | """handle diagram generation options for class diagram or default diagrams
|
||
17 | """
|
||
18 | |||
19 | from six.moves import builtins |
||
20 | |||
21 | import astroid |
||
22 | |||
23 | from pylint.pyreverse.diagrams import PackageDiagram, ClassDiagram |
||
24 | from pylint.pyreverse.utils import LocalsVisitor |
||
25 | |||
26 | BUILTINS_NAME = builtins.__name__ |
||
27 | |||
28 | # diagram generators ##########################################################
|
||
29 | |||
30 | class DiaDefGenerator(object): |
||
31 | """handle diagram generation options"""
|
||
32 | |||
33 | def __init__(self, linker, handler): |
||
34 | """common Diagram Handler initialization"""
|
||
35 | self.config = handler.config
|
||
36 | self._set_default_options()
|
||
37 | self.linker = linker
|
||
38 | self.classdiagram = None # defined by subclasses |
||
39 | |||
40 | def get_title(self, node): |
||
41 | """get title for objects"""
|
||
42 | title = node.name |
||
43 | if self.module_names: |
||
44 | title = '%s.%s' % (node.root().name, title)
|
||
45 | return title
|
||
46 | |||
47 | def _set_option(self, option): |
||
48 | """activate some options if not explicitly deactivated"""
|
||
49 | # if we have a class diagram, we want more information by default;
|
||
50 | # so if the option is None, we return True
|
||
51 | if option is None: |
||
52 | return bool(self.config.classes) |
||
53 | return option
|
||
54 | |||
55 | def _set_default_options(self): |
||
56 | """set different default options with _default dictionary"""
|
||
57 | self.module_names = self._set_option(self.config.module_names) |
||
58 | all_ancestors = self._set_option(self.config.all_ancestors) |
||
59 | all_associated = self._set_option(self.config.all_associated) |
||
60 | anc_level, ass_level = (0, 0) |
||
61 | if all_ancestors:
|
||
62 | anc_level = -1
|
||
63 | if all_associated:
|
||
64 | ass_level = -1
|
||
65 | if self.config.show_ancestors is not None: |
||
66 | anc_level = self.config.show_ancestors
|
||
67 | if self.config.show_associated is not None: |
||
68 | ass_level = self.config.show_associated
|
||
69 | self.anc_level, self.ass_level = anc_level, ass_level |
||
70 | |||
71 | def _get_levels(self): |
||
72 | """help function for search levels"""
|
||
73 | return self.anc_level, self.ass_level |
||
74 | |||
75 | def show_node(self, node): |
||
76 | """true if builtins and not show_builtins"""
|
||
77 | if self.config.show_builtin: |
||
78 | return True |
||
79 | return node.root().name != BUILTINS_NAME
|
||
80 | |||
81 | def add_class(self, node): |
||
82 | """visit one class and add it to diagram"""
|
||
83 | self.linker.visit(node)
|
||
84 | self.classdiagram.add_object(self.get_title(node), node) |
||
85 | |||
86 | def get_ancestors(self, node, level): |
||
87 | """return ancestor nodes of a class node"""
|
||
88 | if level == 0: |
||
89 | return
|
||
90 | for ancestor in node.ancestors(recurs=False): |
||
91 | if not self.show_node(ancestor): |
||
92 | continue
|
||
93 | yield ancestor
|
||
94 | |||
95 | def get_associated(self, klass_node, level): |
||
96 | """return associated nodes of a class node"""
|
||
97 | if level == 0: |
||
98 | return
|
||
99 | for ass_nodes in list(klass_node.instance_attrs_type.values()) + \ |
||
100 | list(klass_node.locals_type.values()):
|
||
101 | for ass_node in ass_nodes: |
||
102 | if isinstance(ass_node, astroid.Instance): |
||
103 | ass_node = ass_node._proxied |
||
104 | if not (isinstance(ass_node, astroid.ClassDef) |
||
105 | and self.show_node(ass_node)): |
||
106 | continue
|
||
107 | yield ass_node
|
||
108 | |||
109 | def extract_classes(self, klass_node, anc_level, ass_level): |
||
110 | """extract recursively classes related to klass_node"""
|
||
111 | if self.classdiagram.has_node(klass_node) or not self.show_node(klass_node): |
||
112 | return
|
||
113 | self.add_class(klass_node)
|
||
114 | |||
115 | for ancestor in self.get_ancestors(klass_node, anc_level): |
||
116 | self.extract_classes(ancestor, anc_level-1, ass_level) |
||
117 | |||
118 | for ass_node in self.get_associated(klass_node, ass_level): |
||
119 | self.extract_classes(ass_node, anc_level, ass_level-1) |
||
120 | |||
121 | |||
122 | class DefaultDiadefGenerator(LocalsVisitor, DiaDefGenerator): |
||
123 | """generate minimum diagram definition for the project :
|
||
124 |
|
||
125 | * a package diagram including project's modules
|
||
126 | * a class diagram including project's classes
|
||
127 | """
|
||
128 | |||
129 | def __init__(self, linker, handler): |
||
130 | DiaDefGenerator.__init__(self, linker, handler)
|
||
131 | LocalsVisitor.__init__(self)
|
||
132 | |||
133 | def visit_project(self, node): |
||
134 | """visit an pyreverse.utils.Project node
|
||
135 |
|
||
136 | create a diagram definition for packages
|
||
137 | """
|
||
138 | mode = self.config.mode
|
||
139 | if len(node.modules) > 1: |
||
140 | self.pkgdiagram = PackageDiagram('packages %s' % node.name, mode) |
||
141 | else:
|
||
142 | self.pkgdiagram = None |
||
143 | self.classdiagram = ClassDiagram('classes %s' % node.name, mode) |
||
144 | |||
145 | def leave_project(self, node): # pylint: disable=unused-argument |
||
146 | """leave the pyreverse.utils.Project node
|
||
147 |
|
||
148 | return the generated diagram definition
|
||
149 | """
|
||
150 | if self.pkgdiagram: |
||
151 | return self.pkgdiagram, self.classdiagram |
||
152 | return self.classdiagram, |
||
153 | |||
154 | def visit_module(self, node): |
||
155 | """visit an astroid.Module node
|
||
156 |
|
||
157 | add this class to the package diagram definition
|
||
158 | """
|
||
159 | if self.pkgdiagram: |
||
160 | self.linker.visit(node)
|
||
161 | self.pkgdiagram.add_object(node.name, node)
|
||
162 | |||
163 | def visit_classdef(self, node): |
||
164 | """visit an astroid.Class node
|
||
165 |
|
||
166 | add this class to the class diagram definition
|
||
167 | """
|
||
168 | anc_level, ass_level = self._get_levels()
|
||
169 | self.extract_classes(node, anc_level, ass_level)
|
||
170 | |||
171 | def visit_importfrom(self, node): |
||
172 | """visit astroid.From and catch modules for package diagram
|
||
173 | """
|
||
174 | if self.pkgdiagram: |
||
175 | self.pkgdiagram.add_from_depend(node, node.modname)
|
||
176 | |||
177 | |||
178 | class ClassDiadefGenerator(DiaDefGenerator): |
||
179 | """generate a class diagram definition including all classes related to a
|
||
180 | given class
|
||
181 | """
|
||
182 | |||
183 | def __init__(self, linker, handler): |
||
184 | DiaDefGenerator.__init__(self, linker, handler)
|
||
185 | |||
186 | def class_diagram(self, project, klass): |
||
187 | """return a class diagram definition for the given klass and its
|
||
188 | related klasses
|
||
189 | """
|
||
190 | |||
191 | self.classdiagram = ClassDiagram(klass, self.config.mode) |
||
192 | if len(project.modules) > 1: |
||
193 | module, klass = klass.rsplit('.', 1) |
||
194 | module = project.get_module(module) |
||
195 | else:
|
||
196 | module = project.modules[0]
|
||
197 | klass = klass.split('.')[-1] |
||
198 | klass = next(module.ilookup(klass))
|
||
199 | |||
200 | anc_level, ass_level = self._get_levels()
|
||
201 | self.extract_classes(klass, anc_level, ass_level)
|
||
202 | return self.classdiagram |
||
203 | |||
204 | # diagram handler #############################################################
|
||
205 | |||
206 | class DiadefsHandler(object): |
||
207 | """handle diagram definitions :
|
||
208 |
|
||
209 | get it from user (i.e. xml files) or generate them
|
||
210 | """
|
||
211 | |||
212 | def __init__(self, config): |
||
213 | self.config = config
|
||
214 | |||
215 | def get_diadefs(self, project, linker): |
||
216 | """get the diagrams configuration data
|
||
217 | :param linker: pyreverse.inspector.Linker(IdGeneratorMixIn, LocalsVisitor)
|
||
218 | :param project: pyreverse.utils.Project
|
||
219 | """
|
||
220 | |||
221 | # read and interpret diagram definitions (Diadefs)
|
||
222 | diagrams = [] |
||
223 | generator = ClassDiadefGenerator(linker, self)
|
||
224 | for klass in self.config.classes: |
||
225 | diagrams.append(generator.class_diagram(project, klass)) |
||
226 | if not diagrams: |
||
227 | diagrams = DefaultDiadefGenerator(linker, self).visit(project)
|
||
228 | for diagram in diagrams: |
||
229 | diagram.extract_relationships() |
||
230 | return diagrams |