memory-graph 0.2.5__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {memory_graph-0.2.5 → memory_graph-0.3.2}/PKG-INFO +2 -2
- {memory_graph-0.2.5 → memory_graph-0.3.2}/README.md +1 -1
- {memory_graph-0.2.5 → memory_graph-0.3.2}/memory_graph/__init__.py +12 -8
- {memory_graph-0.2.5 → memory_graph-0.3.2}/memory_graph/config.py +4 -3
- {memory_graph-0.2.5 → memory_graph-0.3.2}/memory_graph/config_default.py +19 -23
- {memory_graph-0.2.5 → memory_graph-0.3.2}/memory_graph/config_helpers.py +9 -27
- memory_graph-0.3.2/memory_graph/extension_numpy.py +26 -0
- {memory_graph-0.2.5 → memory_graph-0.3.2}/memory_graph/extension_pandas.py +1 -1
- {memory_graph-0.2.5 → memory_graph-0.3.2}/memory_graph/html_table.py +14 -8
- memory_graph-0.3.2/memory_graph/list_view.py +53 -0
- memory_graph-0.3.2/memory_graph/memory_to_nodes.py +187 -0
- memory_graph-0.3.2/memory_graph/node_base.py +132 -0
- memory_graph-0.3.2/memory_graph/node_key_value.py +118 -0
- memory_graph-0.3.2/memory_graph/node_linear.py +94 -0
- memory_graph-0.3.2/memory_graph/node_table.py +79 -0
- memory_graph-0.3.2/memory_graph/sequence.py +107 -0
- memory_graph-0.3.2/memory_graph/slicer.py +42 -0
- memory_graph-0.3.2/memory_graph/slices.py +159 -0
- memory_graph-0.3.2/memory_graph/slices_iterator.py +51 -0
- memory_graph-0.3.2/memory_graph/slices_table_iterator.py +75 -0
- memory_graph-0.3.2/memory_graph/t.py +15 -0
- {memory_graph-0.2.5 → memory_graph-0.3.2}/memory_graph/test.py +82 -19
- memory_graph-0.3.2/memory_graph/test_memory_graph.py +11 -0
- memory_graph-0.3.2/memory_graph/test_memory_to_nodes.py +11 -0
- memory_graph-0.3.2/memory_graph/test_sequence.py +33 -0
- memory_graph-0.3.2/memory_graph/test_slicer.py +46 -0
- memory_graph-0.3.2/memory_graph/test_slices.py +87 -0
- memory_graph-0.3.2/memory_graph/test_slices_iterator.py +24 -0
- {memory_graph-0.2.5 → memory_graph-0.3.2}/memory_graph/utils.py +26 -4
- {memory_graph-0.2.5 → memory_graph-0.3.2}/memory_graph.egg-info/PKG-INFO +2 -2
- {memory_graph-0.2.5 → memory_graph-0.3.2}/memory_graph.egg-info/SOURCES.txt +13 -6
- {memory_graph-0.2.5 → memory_graph-0.3.2}/setup.py +1 -1
- memory_graph-0.2.5/memory_graph/extension_numpy.py +0 -44
- memory_graph-0.2.5/memory_graph/memory_graph.py +0 -76
- memory_graph-0.2.5/memory_graph/memory_visitor.py +0 -81
- memory_graph-0.2.5/memory_graph/node.py +0 -111
- memory_graph-0.2.5/memory_graph/node_hidden.py +0 -26
- memory_graph-0.2.5/memory_graph/node_key_value.py +0 -103
- memory_graph-0.2.5/memory_graph/node_linear.py +0 -66
- memory_graph-0.2.5/memory_graph/node_table.py +0 -90
- memory_graph-0.2.5/memory_graph/sliced.py +0 -114
- memory_graph-0.2.5/memory_graph/slicer.py +0 -169
- memory_graph-0.2.5/memory_graph/test_memory_graph.py +0 -13
- memory_graph-0.2.5/memory_graph/test_memory_visitor.py +0 -10
- {memory_graph-0.2.5 → memory_graph-0.3.2}/LICENSE.txt +0 -0
- {memory_graph-0.2.5 → memory_graph-0.3.2}/MANIFEST.in +0 -0
- {memory_graph-0.2.5 → memory_graph-0.3.2}/memory_graph.egg-info/dependency_links.txt +0 -0
- {memory_graph-0.2.5 → memory_graph-0.3.2}/memory_graph.egg-info/requires.txt +0 -0
- {memory_graph-0.2.5 → memory_graph-0.3.2}/memory_graph.egg-info/top_level.txt +0 -0
- {memory_graph-0.2.5 → memory_graph-0.3.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: memory_graph
|
|
3
|
-
Version: 0.2
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Draws a graph of your data to analyze its structure.
|
|
5
5
|
Home-page: https://github.com/bterwijn/memory_graph
|
|
6
6
|
Author: Bas Terwijn
|
|
@@ -302,7 +302,7 @@ def factorial(n):
|
|
|
302
302
|
memory_graph.show( memory_graph.get_call_stack(), block=True )
|
|
303
303
|
return result
|
|
304
304
|
|
|
305
|
-
factorial(3)
|
|
305
|
+
print(factorial(3))
|
|
306
306
|
```
|
|
307
307
|

|
|
308
308
|
|
|
@@ -283,7 +283,7 @@ def factorial(n):
|
|
|
283
283
|
memory_graph.show( memory_graph.get_call_stack(), block=True )
|
|
284
284
|
return result
|
|
285
285
|
|
|
286
|
-
factorial(3)
|
|
286
|
+
print(factorial(3))
|
|
287
287
|
```
|
|
288
288
|

|
|
289
289
|
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import memory_graph.config_default
|
|
1
|
+
import memory_graph.memory_to_nodes as memory_to_nodes
|
|
2
|
+
import memory_graph.config as config
|
|
3
|
+
import memory_graph.config_default
|
|
4
|
+
import memory_graph.config_helpers as config_helper
|
|
4
5
|
import memory_graph.utils as utils
|
|
5
6
|
|
|
6
7
|
import inspect
|
|
7
8
|
import sys
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
import graphviz
|
|
11
|
+
|
|
12
|
+
__version__ = "0.3.02"
|
|
10
13
|
__author__ = 'Bas Terwijn'
|
|
11
14
|
|
|
12
15
|
log_file=sys.stdout
|
|
@@ -30,8 +33,9 @@ def create_graph(data,
|
|
|
30
33
|
vertical_orientations = None,
|
|
31
34
|
slicers = None):
|
|
32
35
|
""" Creates and returns a memory graph from 'data'. """
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
config_helper.set_config(colors, vertical_orientations, slicers)
|
|
37
|
+
graphviz_graph = memory_to_nodes.memory_to_nodes(data)
|
|
38
|
+
return graphviz_graph
|
|
35
39
|
|
|
36
40
|
def show(data=None ,block=False, stack_index=2,
|
|
37
41
|
colors = None,
|
|
@@ -45,7 +49,7 @@ def show(data=None ,block=False, stack_index=2,
|
|
|
45
49
|
if block:
|
|
46
50
|
input(f"showing '{graph.filename}', {get_source_location(2)}, {press_enter_text}")
|
|
47
51
|
|
|
48
|
-
def render(data=None,
|
|
52
|
+
def render(data=None, outfile=None, block=False, stack_index=2,
|
|
49
53
|
colors = None,
|
|
50
54
|
vertical_orientations = None,
|
|
51
55
|
slicers = None):
|
|
@@ -53,7 +57,7 @@ def render(data=None, output_filename=None, block=False, stack_index=2,
|
|
|
53
57
|
if data is None:
|
|
54
58
|
data=get_locals_from_calling_frame(stack_index)
|
|
55
59
|
graph = create_graph(data, colors, vertical_orientations, slicers)
|
|
56
|
-
filename =
|
|
60
|
+
filename = outfile if outfile else graph.filename+".pdf"
|
|
57
61
|
graph.render(outfile=filename)
|
|
58
62
|
if block:
|
|
59
63
|
input(f"rendering '{filename}', {get_source_location(2)}, {press_enter_text}")
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
""" Configuration file for the graph visualizer. The configuration values are set later by the 'config_default.py' file. """
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
max_tree_depth = None
|
|
4
|
+
max_missing_edges = None
|
|
5
|
+
max_string_length = None
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
not_node_types = {}
|
|
7
8
|
no_child_references_types = set()
|
|
8
9
|
|
|
9
10
|
type_to_node = { }
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
""" Sets the default configuration values for the memory graph. """
|
|
2
|
-
from memory_graph.
|
|
3
|
-
from memory_graph.node_linear
|
|
2
|
+
from memory_graph.node_base import Node_Base
|
|
3
|
+
from memory_graph.node_linear import Node_Linear
|
|
4
4
|
from memory_graph.node_key_value import Node_Key_Value
|
|
5
|
-
from memory_graph.
|
|
6
|
-
|
|
5
|
+
from memory_graph.node_table import Node_Table
|
|
6
|
+
|
|
7
7
|
from memory_graph.slicer import Slicer
|
|
8
8
|
|
|
9
9
|
import memory_graph.config as config
|
|
@@ -11,39 +11,35 @@ import memory_graph.utils as utils
|
|
|
11
11
|
|
|
12
12
|
import types
|
|
13
13
|
|
|
14
|
-
""" The
|
|
15
|
-
config.
|
|
14
|
+
""" The maximum depth of nodes in the graph. When the graph gets too big set this to a small positive number. A `★` symbol indictes where the graph is cut short. """
|
|
15
|
+
config.max_tree_depth = -1
|
|
16
|
+
|
|
17
|
+
config.max_missing_edges = 3
|
|
16
18
|
|
|
17
19
|
""" The maximum length of strings shown in the graph. Longer strings will be truncated. """
|
|
18
20
|
config.max_string_length = 42
|
|
19
21
|
|
|
20
22
|
""" Types that by default will not have references pointing to them in the graph but instead will be visualized in the node of their parent. """
|
|
21
|
-
config.
|
|
22
|
-
type(None)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
float : lambda d: d,
|
|
26
|
-
complex : lambda d: d,
|
|
27
|
-
str : lambda d: d,
|
|
28
|
-
types.FunctionType : lambda d: str(d.__qualname__),
|
|
29
|
-
types.MethodType : lambda d: str(d.__qualname__),
|
|
23
|
+
config.not_node_types = {
|
|
24
|
+
type(None), bool, int, float, complex, str,
|
|
25
|
+
types.FunctionType,
|
|
26
|
+
types.MethodType,
|
|
30
27
|
}
|
|
31
28
|
|
|
32
29
|
""" Types that will not have references pointing to their children in the graph but instead will have their children visualized in their node. """
|
|
33
30
|
config.no_child_references_types = {dict, types.MappingProxyType}
|
|
34
31
|
|
|
35
|
-
""" Conversion from type to
|
|
32
|
+
""" Conversion from type to Node_Base objects. """
|
|
36
33
|
config.type_to_node = {
|
|
37
|
-
str: lambda data:
|
|
34
|
+
str: lambda data: Node_Base(data), # visit as whole string, don't iterate over characters
|
|
35
|
+
types.FunctionType: lambda data: Node_Base(data.__qualname__),
|
|
36
|
+
types.MethodType: lambda data: Node_Base(data.__qualname__),
|
|
38
37
|
range: lambda data: Node_Key_Value(data, {'start':data.start, 'stop':data.stop, 'step':data.step}.items()),
|
|
39
|
-
types.FunctionType: lambda data: Node(data.__qualname__),
|
|
40
|
-
types.MethodType: lambda data: Node(data.__qualname__),
|
|
41
38
|
dict: lambda data: (
|
|
42
|
-
Node_Key_Value(data, utils.
|
|
39
|
+
Node_Key_Value(data, utils.filter_dict(data.items()) )
|
|
43
40
|
if dict in config.no_child_references_types else
|
|
44
|
-
Node_Linear(data, utils.
|
|
41
|
+
Node_Linear(data, utils.filter_dict(data.items()) )
|
|
45
42
|
),
|
|
46
|
-
Node_Hidden: lambda data: data,
|
|
47
43
|
}
|
|
48
44
|
|
|
49
45
|
""" Colors of different types in the graph. """
|
|
@@ -71,7 +67,7 @@ config.type_to_color = {
|
|
|
71
67
|
}
|
|
72
68
|
|
|
73
69
|
""" Types that will be visualized in vertical orientation if 'True', or horizontal orientation
|
|
74
|
-
if 'False'. Otherwise the
|
|
70
|
+
if 'False'. Otherwise the Node_Base decides based on it having references."""
|
|
75
71
|
config.type_to_vertical_orientation = {
|
|
76
72
|
}
|
|
77
73
|
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
from memory_graph.slicer import Slicer
|
|
3
3
|
|
|
4
4
|
import memory_graph.config as config
|
|
5
|
-
import memory_graph.utils as utils
|
|
6
5
|
|
|
7
6
|
type_to_color = None
|
|
8
7
|
type_to_vertical_orientation = None
|
|
@@ -32,39 +31,22 @@ def get_property(data_id, data_type, node_type, dictionary, default):
|
|
|
32
31
|
return default
|
|
33
32
|
|
|
34
33
|
def get_color(node, default='white'):
|
|
35
|
-
return get_property(
|
|
36
|
-
|
|
37
|
-
type(node),
|
|
34
|
+
return get_property(node.get_id(),
|
|
35
|
+
node.get_type(),
|
|
36
|
+
type(node),
|
|
38
37
|
type_to_color,
|
|
39
38
|
default)
|
|
40
39
|
|
|
41
40
|
def get_vertical_orientation(node, default):
|
|
42
|
-
return get_property(
|
|
43
|
-
|
|
44
|
-
type(node),
|
|
41
|
+
return get_property(node.get_id(),
|
|
42
|
+
node.get_type(),
|
|
43
|
+
type(node),
|
|
45
44
|
type_to_vertical_orientation,
|
|
46
45
|
default)
|
|
47
46
|
|
|
48
|
-
def
|
|
49
|
-
|
|
50
|
-
type(data),
|
|
51
|
-
type(node),
|
|
52
|
-
type_to_slicer,
|
|
53
|
-
default)
|
|
54
|
-
if type(slicer) is Slicer:
|
|
55
|
-
return slicer
|
|
56
|
-
if utils.is_iterable(slicer):
|
|
57
|
-
return next(iter(slicer))
|
|
58
|
-
return default
|
|
59
|
-
|
|
60
|
-
def get_slicer_2d(node, data, default=Slicer(5,5)):
|
|
61
|
-
slicer = get_property(id(data),
|
|
47
|
+
def get_slicer(node, data, default=Slicer(3,2,3)):
|
|
48
|
+
return get_property(id(data),
|
|
62
49
|
type(data),
|
|
63
50
|
type(node),
|
|
64
51
|
type_to_slicer,
|
|
65
|
-
default)
|
|
66
|
-
if type(slicer) is Slicer:
|
|
67
|
-
return slicer, slicer
|
|
68
|
-
if len(slicer) == 2:
|
|
69
|
-
return slicer
|
|
70
|
-
return default, default
|
|
52
|
+
default)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
""" Extension to add the memory graph configuration for Numpy types. """
|
|
2
|
+
from memory_graph.node_linear import Node_Linear
|
|
3
|
+
from memory_graph.node_table import Node_Table
|
|
4
|
+
|
|
5
|
+
import memory_graph.config as config
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
config.not_node_types |= {
|
|
10
|
+
np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.uint32, np.uint64,
|
|
11
|
+
np.float16, np.float32, np.float64, np.float_,
|
|
12
|
+
np.complex64, np.complex128, np.complex_,
|
|
13
|
+
np.bool_, np.bytes_, np.str_, np.datetime64, np.timedelta64
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
def ndarrayy_to_node(ndarrayy_data):
|
|
17
|
+
if len(ndarrayy_data.shape) == 2:
|
|
18
|
+
return Node_Table(ndarrayy_data, ndarrayy_data)
|
|
19
|
+
else:
|
|
20
|
+
return Node_Linear(ndarrayy_data, ndarrayy_data)
|
|
21
|
+
|
|
22
|
+
config.type_to_node[np.matrix] = lambda data : Node_Table(data, np.asarray(data)) # convert to ndarray to avoid infinite recursion due to index issue
|
|
23
|
+
config.type_to_node[np.ndarray] = lambda data : ndarrayy_to_node(data)
|
|
24
|
+
|
|
25
|
+
config.type_to_color[np.ndarray] = "hotpink1"
|
|
26
|
+
config.type_to_color[np.matrix] = "hotpink2"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
from memory_graph.
|
|
1
|
+
from memory_graph.node_base import Node_Base
|
|
2
|
+
import memory_graph.node_base
|
|
2
3
|
|
|
3
4
|
import memory_graph.config as config
|
|
4
5
|
|
|
@@ -59,7 +60,6 @@ class HTML_Table:
|
|
|
59
60
|
def add_string(self, s):
|
|
60
61
|
""" Add a string s to the outer table. """
|
|
61
62
|
self.html += format_string(s)
|
|
62
|
-
#self.col_count += 1
|
|
63
63
|
|
|
64
64
|
def add_index(self, s):
|
|
65
65
|
""" Add an index s to the inner table. """
|
|
@@ -67,10 +67,16 @@ class HTML_Table:
|
|
|
67
67
|
self.html += f'<TD><font color="#505050">{str(s)}</font></TD>'
|
|
68
68
|
self.col_count += 1
|
|
69
69
|
|
|
70
|
-
def add_entry(self, node, child, rounded=False, border=1):
|
|
71
|
-
""" Add child to the inner table either as reference if it a
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
def add_entry(self, node, nodes, child, id_to_slices, rounded=False, border=1, dashed=False):
|
|
71
|
+
""" Add child to the inner table either as reference if it is a Node_Base or as a value otherwise. """
|
|
72
|
+
#print('child:', child)
|
|
73
|
+
child_id = id(child)
|
|
74
|
+
if child_id in nodes:
|
|
75
|
+
child = nodes[child_id]
|
|
76
|
+
if child_id in id_to_slices:
|
|
77
|
+
self.add_reference(node, child, rounded, border, dashed)
|
|
78
|
+
else:
|
|
79
|
+
self.add_value("✂", rounded, border)
|
|
74
80
|
else:
|
|
75
81
|
self.add_value(child, rounded, border)
|
|
76
82
|
|
|
@@ -81,13 +87,13 @@ class HTML_Table:
|
|
|
81
87
|
self.html += f'<TD BORDER="{border}"{r}> {format_string(s)} </TD>'
|
|
82
88
|
self.col_count += 1
|
|
83
89
|
|
|
84
|
-
def add_reference(self, node, child, rounded=False, border=1):
|
|
90
|
+
def add_reference(self, node, child, rounded=False, border=1, dashed=False):
|
|
85
91
|
""" Helper function to add a reference to the inner table. """
|
|
86
92
|
self.check_add_new_line()
|
|
87
93
|
r = ' STYLE="ROUNDED"' if rounded else ''
|
|
88
94
|
self.html += f'<TD BORDER="{border}" PORT="ref{self.ref_count}"{r}> </TD>'
|
|
89
95
|
self.edges.append( (f'{node.get_name()}:ref{self.ref_count}',
|
|
90
|
-
child.get_name()) )
|
|
96
|
+
child.get_name(), dashed) )
|
|
91
97
|
self.ref_count += 1
|
|
92
98
|
self.col_count += 1
|
|
93
99
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
|
|
2
|
+
class List_View:
|
|
3
|
+
def __init__(self, base_list, begin, end):
|
|
4
|
+
self.base_list = base_list
|
|
5
|
+
self.begin = max(0, begin)
|
|
6
|
+
self.end = min(end, len(base_list))
|
|
7
|
+
|
|
8
|
+
def __getitem__(self, index):
|
|
9
|
+
if isinstance(index, slice):
|
|
10
|
+
# Calculate new begin and end indices within the bounds of the current view
|
|
11
|
+
start, stop, step = index.indices(self.end - self.begin)
|
|
12
|
+
if step != 1:
|
|
13
|
+
raise ValueError("List_View does not support slices with steps other than 1")
|
|
14
|
+
# Adjust the indices relative to the base list
|
|
15
|
+
new_start = self.begin + start
|
|
16
|
+
new_end = self.begin + stop
|
|
17
|
+
return List_View(self.base_list, new_start, new_end)
|
|
18
|
+
elif isinstance(index, int):
|
|
19
|
+
if index < 0 or index >= (self.end - self.begin):
|
|
20
|
+
raise IndexError("list index out of range")
|
|
21
|
+
return self.base_list[self.begin + index]
|
|
22
|
+
else:
|
|
23
|
+
raise TypeError("Invalid index type")
|
|
24
|
+
|
|
25
|
+
def __setitem__(self, index, value):
|
|
26
|
+
if index < 0 or index >= (self.end - self.begin):
|
|
27
|
+
raise IndexError("list index out of range")
|
|
28
|
+
self.base_list[self.begin + index] = value
|
|
29
|
+
|
|
30
|
+
def __len__(self):
|
|
31
|
+
return self.end - self.begin
|
|
32
|
+
|
|
33
|
+
def __iter__(self):
|
|
34
|
+
for i in range(self.begin, self.end):
|
|
35
|
+
yield self.base_list[i]
|
|
36
|
+
|
|
37
|
+
def __repr__(self):
|
|
38
|
+
return f"List_View({self.base_list[self.begin:self.end]})"
|
|
39
|
+
|
|
40
|
+
def test_list_vew():
|
|
41
|
+
original_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
42
|
+
list_view = List_View(original_list, 3, 8)
|
|
43
|
+
print(list_view) # Output: List_View([3, 4, 5, 6, 7])
|
|
44
|
+
print(list_view[1:4]) # Output: List_View([4, 5, 6])
|
|
45
|
+
# 2D array
|
|
46
|
+
n = 4
|
|
47
|
+
data = [i for i in range(n*n)]
|
|
48
|
+
list_views = [List_View(data, i, i+n) for i in range(0,len(data),n)]
|
|
49
|
+
for row in list_views:
|
|
50
|
+
print(row)
|
|
51
|
+
|
|
52
|
+
if __name__ == "__main__":
|
|
53
|
+
test_list_vew()
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
from memory_graph.node_base import Node_Base
|
|
2
|
+
from memory_graph.node_linear import Node_Linear
|
|
3
|
+
from memory_graph.node_key_value import Node_Key_Value
|
|
4
|
+
|
|
5
|
+
import memory_graph.utils as utils
|
|
6
|
+
import memory_graph.config as config
|
|
7
|
+
|
|
8
|
+
import graphviz
|
|
9
|
+
|
|
10
|
+
def read_nodes(data):
|
|
11
|
+
|
|
12
|
+
def data_to_node(data_type, data):
|
|
13
|
+
if data_type in config.type_to_node: # for predefined types
|
|
14
|
+
return config.type_to_node[data_type](data)
|
|
15
|
+
elif utils.has_dict_attributes(data): # for user defined classes
|
|
16
|
+
return Node_Key_Value(data, utils.filter_dict_attributes(utils.get_dict_attributes(data)) )
|
|
17
|
+
elif utils.is_iterable(data): # for lists, tuples, sets, ...
|
|
18
|
+
return Node_Linear(data, data)
|
|
19
|
+
else:
|
|
20
|
+
return Node_Base(data)
|
|
21
|
+
|
|
22
|
+
def memory_to_nodes_recursive(nodes, data, parent, parent_index):
|
|
23
|
+
data_type = type(data)
|
|
24
|
+
if not data_type in config.not_node_types or parent is None:
|
|
25
|
+
data_id = id(data)
|
|
26
|
+
if data_id in nodes:
|
|
27
|
+
node = nodes[data_id]
|
|
28
|
+
else:
|
|
29
|
+
node = data_to_node(data_type, data)
|
|
30
|
+
nodes[data_id] = node
|
|
31
|
+
for index in node.get_children().indices_all():
|
|
32
|
+
child = node.get_children()[index]
|
|
33
|
+
memory_to_nodes_recursive(nodes, child, node, index)
|
|
34
|
+
if not parent is None:
|
|
35
|
+
node.add_parent_index(parent, parent_index)
|
|
36
|
+
|
|
37
|
+
nodes = {}
|
|
38
|
+
memory_to_nodes_recursive(nodes, data, None, None)
|
|
39
|
+
root_id = id(data)
|
|
40
|
+
return nodes, root_id
|
|
41
|
+
|
|
42
|
+
# --------------------------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
def slice_nodes(nodes, root_id, max_tree_depth):
|
|
45
|
+
|
|
46
|
+
def slice_nodes_recursive(nodes, node_id, id_to_slices, max_tree_depth):
|
|
47
|
+
if max_tree_depth == 0 or node_id in id_to_slices:
|
|
48
|
+
return
|
|
49
|
+
if node_id in nodes:
|
|
50
|
+
node = nodes[node_id]
|
|
51
|
+
children = node.get_children()
|
|
52
|
+
if children.is_empty():
|
|
53
|
+
id_to_slices[node_id] = None
|
|
54
|
+
else:
|
|
55
|
+
slicer = node.get_slicer()
|
|
56
|
+
slices = children.slice(slicer)
|
|
57
|
+
id_to_slices[node_id] = slices
|
|
58
|
+
if not node.is_hidden_node():
|
|
59
|
+
max_tree_depth -= 1
|
|
60
|
+
for index in slices:
|
|
61
|
+
slice_nodes_recursive(nodes, id(children[index]), id_to_slices, max_tree_depth)
|
|
62
|
+
id_to_slices = {}
|
|
63
|
+
slice_nodes_recursive(nodes, root_id, id_to_slices, max_tree_depth)
|
|
64
|
+
return id_to_slices
|
|
65
|
+
|
|
66
|
+
# --------------------------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
def add_parent_indices(nodes, type_to_parent_indices, id_to_slices, max_missing_edges):
|
|
69
|
+
#print('add_parent_indices type_to_parent_indices:',type_to_parent_indices)
|
|
70
|
+
for _, parent_indices in type_to_parent_indices.items():
|
|
71
|
+
dashed = len(parent_indices) > max_missing_edges
|
|
72
|
+
for parent, index in parent_indices[0:max_missing_edges]:
|
|
73
|
+
new_parent = False
|
|
74
|
+
parent_id = parent.get_id()
|
|
75
|
+
if not parent_id in id_to_slices:
|
|
76
|
+
new_parent = True
|
|
77
|
+
id_to_slices[parent_id] = parent.get_children().empty_slices()
|
|
78
|
+
slices = id_to_slices[parent_id]
|
|
79
|
+
slices.add_index(index, dashed=dashed)
|
|
80
|
+
if new_parent:
|
|
81
|
+
add_indices_to_parents(nodes, parent_id, id_to_slices, max_missing_edges)
|
|
82
|
+
|
|
83
|
+
def add_indices_to_parents(nodes, node_id, id_to_slices, max_missing_edges):
|
|
84
|
+
#print('add_indices_to_parents node_id:',node_id)
|
|
85
|
+
type_to_parent_indices = {}
|
|
86
|
+
parent_indices = nodes[node_id].get_parent_indices()
|
|
87
|
+
for parent, indices in parent_indices.items():
|
|
88
|
+
if parent is None:
|
|
89
|
+
continue
|
|
90
|
+
parent_type = parent.get_type()
|
|
91
|
+
parent_id = parent.get_id()
|
|
92
|
+
if (parent_type in type_to_parent_indices and
|
|
93
|
+
len(type_to_parent_indices[parent_type]) > max_missing_edges): # early stop
|
|
94
|
+
continue
|
|
95
|
+
parent_slices = None
|
|
96
|
+
if parent_id in id_to_slices:
|
|
97
|
+
parent_slices = id_to_slices[parent_id]
|
|
98
|
+
for index in indices:
|
|
99
|
+
if parent_slices is None or not parent_slices.has_index(index):
|
|
100
|
+
if not parent_type in type_to_parent_indices:
|
|
101
|
+
type_to_parent_indices[parent_type] = []
|
|
102
|
+
parent_indices = type_to_parent_indices[parent_type]
|
|
103
|
+
if len(parent_indices) > max_missing_edges:
|
|
104
|
+
break
|
|
105
|
+
else:
|
|
106
|
+
parent_indices.append((parent, index))
|
|
107
|
+
add_parent_indices(nodes, type_to_parent_indices, id_to_slices, max_missing_edges)
|
|
108
|
+
|
|
109
|
+
def add_missing_edges(nodes, id_to_slices, max_missing_edges=3):
|
|
110
|
+
old_id_to_slices_keys = set(id_to_slices.keys())
|
|
111
|
+
for node_id in old_id_to_slices_keys:
|
|
112
|
+
add_indices_to_parents(nodes, node_id, id_to_slices, max_missing_edges)
|
|
113
|
+
return id_to_slices
|
|
114
|
+
|
|
115
|
+
# --------------------------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
import memory_graph.config_helpers as config_helpers
|
|
118
|
+
|
|
119
|
+
def create_depth_of_nodes(nodes, nodes_at_depth):
|
|
120
|
+
depth_of_nodes = {}
|
|
121
|
+
for node_id, depth in nodes_at_depth.items():
|
|
122
|
+
node = nodes[node_id]
|
|
123
|
+
if node_id in nodes and not node.is_hidden_node():
|
|
124
|
+
if not depth in depth_of_nodes:
|
|
125
|
+
depth_of_nodes[depth] = []
|
|
126
|
+
depth_of_nodes[depth].append(node)
|
|
127
|
+
return depth_of_nodes
|
|
128
|
+
|
|
129
|
+
def add_subgraph(graphviz_graph, nodes_to_subgraph):
|
|
130
|
+
new_node_names = [node.get_name() for node in nodes_to_subgraph]
|
|
131
|
+
if len(new_node_names) > 1:
|
|
132
|
+
#graphviz_graph.body.append('{ rank="same" '+(" -> ".join(new_node_names))+' [weight=999,style=invis]; }\n')
|
|
133
|
+
graphviz_graph.body.append('{ rank="same" '+('; '.join(new_node_names))+'; }\n')
|
|
134
|
+
|
|
135
|
+
def add_to_graphviz_graph(graphviz_graph, nodes, node, slices, id_to_slices, subgraphed_nodes, depth):
|
|
136
|
+
html_table = node.get_html_table(nodes, slices, id_to_slices)
|
|
137
|
+
edges = html_table.get_edges()
|
|
138
|
+
color = config_helpers.get_color(node)
|
|
139
|
+
border = 3 if node.is_root() else 1
|
|
140
|
+
graphviz_graph.node(node.get_name(),
|
|
141
|
+
html_table.to_string(border, color),
|
|
142
|
+
xlabel=node.get_label(slices))
|
|
143
|
+
# ------------ edges
|
|
144
|
+
for parent,child,dashed in edges:
|
|
145
|
+
graphviz_graph.edge(parent, child+':table', style='dashed' if dashed else 'solid')
|
|
146
|
+
|
|
147
|
+
def build_graph_depth_first(graphviz_graph, nodes, node_id, id_to_slices, nodes_at_depth, subgraphed_nodes, depth):
|
|
148
|
+
if node_id in id_to_slices:
|
|
149
|
+
if node_id in nodes_at_depth:
|
|
150
|
+
return
|
|
151
|
+
nodes_at_depth[node_id] = depth
|
|
152
|
+
node = nodes[node_id]
|
|
153
|
+
children = node.get_children()
|
|
154
|
+
slices = None
|
|
155
|
+
if node_id in id_to_slices:
|
|
156
|
+
slices = id_to_slices[node_id]
|
|
157
|
+
if not slices is None:
|
|
158
|
+
for index in slices:
|
|
159
|
+
child_id = id(children[index])
|
|
160
|
+
build_graph_depth_first(graphviz_graph, nodes, child_id, id_to_slices, nodes_at_depth, subgraphed_nodes, depth+1)
|
|
161
|
+
if not node.is_hidden_node():
|
|
162
|
+
add_to_graphviz_graph(graphviz_graph, nodes, node, slices, id_to_slices, subgraphed_nodes, depth)
|
|
163
|
+
|
|
164
|
+
def build_graph(graphviz_graph, nodes, root_id, id_to_slices):
|
|
165
|
+
nodes_at_depth = {}
|
|
166
|
+
build_graph_depth_first(graphviz_graph, nodes, root_id, id_to_slices, nodes_at_depth, set(), 0)
|
|
167
|
+
depth_of_nodes = create_depth_of_nodes(nodes, nodes_at_depth)
|
|
168
|
+
#print('nodes_at_depth:',nodes_at_depth,'depth_of_nodes:', depth_of_nodes)
|
|
169
|
+
for depth, depth_nodes in depth_of_nodes.items():
|
|
170
|
+
add_subgraph(graphviz_graph, depth_nodes)
|
|
171
|
+
|
|
172
|
+
def memory_to_nodes(data):
|
|
173
|
+
nodes, root_id = read_nodes(data)
|
|
174
|
+
#print('nodes:',nodes,'root_id:',root_id)
|
|
175
|
+
id_to_slices = slice_nodes(nodes, root_id, config.max_tree_depth)
|
|
176
|
+
#print('id_to_slices:',id_to_slices)
|
|
177
|
+
id_to_slices = add_missing_edges(nodes, id_to_slices, config.max_missing_edges)
|
|
178
|
+
#print('id_to_slices:',id_to_slices)
|
|
179
|
+
graphviz_graph_attr = {}
|
|
180
|
+
graphviz_node_attr = {'shape':'plaintext'}
|
|
181
|
+
graphviz_edge_attr = {}
|
|
182
|
+
graphviz_graph=graphviz.Digraph('memory_graph',
|
|
183
|
+
graph_attr=graphviz_graph_attr,
|
|
184
|
+
node_attr=graphviz_node_attr,
|
|
185
|
+
edge_attr=graphviz_edge_attr)
|
|
186
|
+
build_graph(graphviz_graph, nodes, root_id, id_to_slices)
|
|
187
|
+
return graphviz_graph
|