-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pyreverse autocolor #4521
Pyreverse autocolor #4521
Changes from all commits
53e99b4
832202b
7010985
72e2d01
71d6d29
bc0febf
128ea44
d2763be
738e7b9
36c3688
a32ca13
225ef45
e8e898f
e70c135
2dcfdb0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,8 +16,12 @@ | |
|
||
"""Utilities for creating VCG and Dot diagrams""" | ||
|
||
import itertools | ||
import os | ||
|
||
import astroid | ||
from astroid import modutils | ||
|
||
from pylint.graph import DotBackend | ||
from pylint.pyreverse.utils import is_exception | ||
from pylint.pyreverse.vcgutils import VCGPrinter | ||
|
@@ -49,7 +53,7 @@ def write_packages(self, diagram): | |
"""write a package diagram""" | ||
# sorted to get predictable (hence testable) results | ||
for i, obj in enumerate(sorted(diagram.modules(), key=lambda x: x.title)): | ||
self.printer.emit_node(i, label=self.get_title(obj), shape="box") | ||
self.printer.emit_node(i, **self.get_package_properties(obj)) | ||
obj.fig_id = i | ||
# package dependencies | ||
for rel in diagram.get_relationships("depends"): | ||
|
@@ -61,7 +65,7 @@ def write_classes(self, diagram): | |
"""write a class diagram""" | ||
# sorted to get predictable (hence testable) results | ||
for i, obj in enumerate(sorted(diagram.objects, key=lambda x: x.title)): | ||
self.printer.emit_node(i, **self.get_values(obj)) | ||
self.printer.emit_node(i, **self.get_class_properties(obj)) | ||
obj.fig_id = i | ||
# inheritance links | ||
for rel in diagram.get_relationships("specialization"): | ||
|
@@ -90,7 +94,11 @@ def get_title(self, obj): | |
"""get project title""" | ||
raise NotImplementedError | ||
|
||
def get_values(self, obj): | ||
def get_package_properties(self, obj): | ||
"""get label and shape for packages.""" | ||
raise NotImplementedError | ||
|
||
def get_class_properties(self, obj): | ||
"""get label and shape for classes.""" | ||
raise NotImplementedError | ||
|
||
|
@@ -111,6 +119,28 @@ def __init__(self, config): | |
fontcolor="green", arrowtail="none", arrowhead="diamond", style="solid" | ||
), | ||
] | ||
self.available_colors = itertools.cycle( | ||
[ | ||
"aliceblue", | ||
"antiquewhite", | ||
"aquamarine", | ||
"burlywood", | ||
"cadetblue", | ||
"chartreuse", | ||
"chocolate", | ||
"coral", | ||
"cornflowerblue", | ||
"cyan", | ||
"darkgoldenrod", | ||
"darkseagreen", | ||
"dodgerblue", | ||
"forestgreen", | ||
"gold", | ||
"hotpink", | ||
"mediumspringgreen", | ||
] | ||
) | ||
self.used_colors = {} | ||
DiagramWriter.__init__(self, config, styles) | ||
|
||
def set_printer(self, file_name, basename): | ||
|
@@ -123,7 +153,41 @@ def get_title(self, obj): | |
"""get project title""" | ||
return obj.title | ||
|
||
def get_values(self, obj): | ||
def get_style(self): | ||
"""get style of object""" | ||
if not self.config.colorized: | ||
return "solid" | ||
return "filled" | ||
|
||
def get_color(self, obj): | ||
"""get shape color""" | ||
if not self.config.colorized: | ||
return "black" | ||
qualified_name = obj.node.qname() | ||
if modutils.is_standard_module(qualified_name.split(".", maxsplit=1)[0]): | ||
return "grey" | ||
depth = self.config.max_color_depth | ||
if isinstance(obj.node, astroid.ClassDef): | ||
package = qualified_name.rsplit(".", maxsplit=2)[0] | ||
elif obj.node.package: | ||
package = qualified_name | ||
else: | ||
package = qualified_name.rsplit(".", maxsplit=1)[0] | ||
base_name = ".".join(package.split(".", depth)[:depth]) | ||
if base_name not in self.used_colors: | ||
self.used_colors[base_name] = next(self.available_colors) | ||
return self.used_colors[base_name] | ||
|
||
def get_package_properties(self, obj): | ||
"""get label and shape for packages.""" | ||
return dict( | ||
label=self.get_title(obj), | ||
shape="box", | ||
color=self.get_color(obj), | ||
style=self.get_style(), | ||
) | ||
|
||
def get_class_properties(self, obj): | ||
"""get label and shape for classes. | ||
|
||
The label contains all attributes and methods | ||
|
@@ -140,9 +204,14 @@ def get_values(self, obj): | |
args = [] | ||
label = r"{}{}({})\l".format(label, func.name, ", ".join(args)) | ||
label = "{%s}" % label | ||
if is_exception(obj.node): | ||
return dict(fontcolor="red", label=label, shape="record") | ||
return dict(label=label, shape="record") | ||
values = dict( | ||
label=label, | ||
shape="record", | ||
fontcolor="red" if is_exception(obj.node) else "black", | ||
style=self.get_style(), | ||
color=self.get_color(obj), | ||
) | ||
return values | ||
|
||
def close_graph(self): | ||
"""print the dot graph into <file_name>""" | ||
|
@@ -184,7 +253,14 @@ def get_title(self, obj): | |
"""get project title in vcg format""" | ||
return r"\fb%s\fn" % obj.title | ||
|
||
def get_values(self, obj): | ||
def get_package_properties(self, obj): | ||
"""get label and shape for packages.""" | ||
return dict( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be better to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that a NamedTuple would be the cleaner solution. However, the current (as in: this PR) implementation of the That being said: I have already started working on the PlantUML backend, and while doing so I extracted a common interface for the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with your other comment, it your implementation of plantuml make you upgrade the code with NamedTuple and proper typing it will be nice to rebase on it once it's merged. |
||
label=self.get_title(obj), | ||
shape="box", | ||
) | ||
|
||
def get_class_properties(self, obj): | ||
"""get label and shape for classes. | ||
|
||
The label contains all attributes and methods | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
digraph "classes_colorized" { | ||
charset="utf-8" | ||
rankdir=BT | ||
"0" [color="aliceblue", fontcolor="black", label="{Ancestor|attr : str\lcls_member\l|get_value()\lset_value(value)\l}", shape="record", style="filled"]; | ||
"1" [color="aliceblue", fontcolor="black", label="{DoNothing|\l|}", shape="record", style="filled"]; | ||
"2" [color="aliceblue", fontcolor="black", label="{Interface|\l|get_value()\lset_value(value)\l}", shape="record", style="filled"]; | ||
"3" [color="aliceblue", fontcolor="black", label="{Specialization|TYPE : str\lrelation\ltop : str\l|}", shape="record", style="filled"]; | ||
"3" -> "0" [arrowhead="empty", arrowtail="none"]; | ||
"0" -> "2" [arrowhead="empty", arrowtail="node", style="dashed"]; | ||
"1" -> "0" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="cls_member", style="solid"]; | ||
"1" -> "3" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="relation", style="solid"]; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
graph:{ | ||
title:"classes_vcg" | ||
layoutalgorithm:dfs | ||
late_edge_labels:yes | ||
port_sharing:no | ||
manhattan_edges:yes | ||
node: {title:"0" label:"\fbAncestor\fn\n\f____________\n\f08attr : str\n\f08cls_member\n\f____________\n\f10get_value()\n\f10set_value()" | ||
shape:box | ||
} | ||
node: {title:"1" label:"\fbDoNothing\fn\n\f___________" | ||
shape:box | ||
} | ||
node: {title:"2" label:"\fbInterface\fn\n\f___________\n\f10get_value()\n\f10set_value()" | ||
shape:box | ||
} | ||
node: {title:"3" label:"\fbSpecialization\fn\n\f________________\n\f08TYPE : str\n\f08relation\n\f08top : str\n\f________________" | ||
shape:box | ||
} | ||
edge: {sourcename:"3" targetname:"0" arrowstyle:solid | ||
backarrowstyle:none | ||
backarrowsize:10 | ||
} | ||
edge: {sourcename:"0" targetname:"2" arrowstyle:solid | ||
backarrowstyle:none | ||
linestyle:dotted | ||
backarrowsize:10 | ||
} | ||
edge: {sourcename:"1" targetname:"0" label:"cls_member" | ||
arrowstyle:solid | ||
backarrowstyle:none | ||
textcolor:green | ||
} | ||
edge: {sourcename:"1" targetname:"3" label:"relation" | ||
arrowstyle:solid | ||
backarrowstyle:none | ||
textcolor:green | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,8 @@ | ||
digraph "packages_No_Name" { | ||
charset="utf-8" | ||
rankdir=BT | ||
"0" [label="data", shape="box"]; | ||
"1" [label="data.clientmodule_test", shape="box"]; | ||
"2" [label="data.suppliermodule_test", shape="box"]; | ||
"0" [color="black", label="data", shape="box", style="solid"]; | ||
"1" [color="black", label="data.clientmodule_test", shape="box", style="solid"]; | ||
"2" [color="black", label="data.suppliermodule_test", shape="box", style="solid"]; | ||
"1" -> "2" [arrowhead="open", arrowtail="none"]; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
digraph "packages_colorized" { | ||
charset="utf-8" | ||
rankdir=BT | ||
"0" [color="aliceblue", label="data", shape="box", style="filled"]; | ||
"1" [color="aliceblue", label="data.clientmodule_test", shape="box", style="filled"]; | ||
"2" [color="aliceblue", label="data.suppliermodule_test", shape="box", style="filled"]; | ||
"1" -> "2" [arrowhead="open", arrowtail="none"]; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
graph:{ | ||
title:"packages_vcg" | ||
layoutalgorithm:dfs | ||
late_edge_labels:yes | ||
port_sharing:no | ||
manhattan_edges:yes | ||
node: {title:"0" label:"\fbdata\fn" | ||
shape:box | ||
} | ||
node: {title:"1" label:"\fbdata.clientmodule_test\fn" | ||
shape:box | ||
} | ||
node: {title:"2" label:"\fbdata.suppliermodule_test\fn" | ||
shape:box | ||
} | ||
edge: {sourcename:"1" targetname:"2" arrowstyle:solid | ||
backarrowstyle:none | ||
backarrowsize:0 | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add typing on new functions please ? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do!