memory-graph 0.3.30__py3-none-any.whl
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/__init__.py +322 -0
- memory_graph/call_stack.py +9 -0
- memory_graph/config.py +35 -0
- memory_graph/config_default.py +108 -0
- memory_graph/config_helpers.py +56 -0
- memory_graph/extension_numpy.py +30 -0
- memory_graph/extension_pandas.py +26 -0
- memory_graph/html_table.py +137 -0
- memory_graph/list_view.py +57 -0
- memory_graph/memory_to_nodes.py +200 -0
- memory_graph/node_base.py +136 -0
- memory_graph/node_key_value.py +124 -0
- memory_graph/node_leaf.py +23 -0
- memory_graph/node_linear.py +100 -0
- memory_graph/node_table.py +83 -0
- memory_graph/sequence.py +111 -0
- memory_graph/slicer.py +46 -0
- memory_graph/slices.py +163 -0
- memory_graph/slices_iterator.py +55 -0
- memory_graph/slices_table_iterator.py +79 -0
- memory_graph/test.py +245 -0
- memory_graph/test_max_graph_depth.py +27 -0
- memory_graph/test_memory_graph.py +15 -0
- memory_graph/test_memory_to_nodes.py +15 -0
- memory_graph/test_sequence.py +37 -0
- memory_graph/test_slicer.py +50 -0
- memory_graph/test_slices.py +91 -0
- memory_graph/test_slices_iterator.py +28 -0
- memory_graph/utils.py +105 -0
- memory_graph-0.3.30.dist-info/METADATA +897 -0
- memory_graph-0.3.30.dist-info/RECORD +34 -0
- memory_graph-0.3.30.dist-info/WHEEL +5 -0
- memory_graph-0.3.30.dist-info/licenses/LICENSE.txt +25 -0
- memory_graph-0.3.30.dist-info/top_level.txt +1 -0
memory_graph/__init__.py
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# This file is part of memory_graph.
|
|
2
|
+
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
|
|
5
|
+
import memory_graph.memory_to_nodes as memory_to_nodes
|
|
6
|
+
import memory_graph.config as config
|
|
7
|
+
import memory_graph.config_default
|
|
8
|
+
import memory_graph.config_helpers as config_helper
|
|
9
|
+
import memory_graph.utils as utils
|
|
10
|
+
|
|
11
|
+
import inspect
|
|
12
|
+
import sys
|
|
13
|
+
import itertools as it
|
|
14
|
+
from memory_graph.call_stack import call_stack
|
|
15
|
+
|
|
16
|
+
import graphviz
|
|
17
|
+
|
|
18
|
+
# Add 'mg' to builtins so it is available in all subsequent imports
|
|
19
|
+
import memory_graph as mg
|
|
20
|
+
import builtins
|
|
21
|
+
if not hasattr(builtins, "mg"):
|
|
22
|
+
builtins.mg = mg
|
|
23
|
+
|
|
24
|
+
__version__ = "0.3.30"
|
|
25
|
+
__author__ = 'Bas Terwijn'
|
|
26
|
+
render_filename = 'memory_graph.pdf'
|
|
27
|
+
render_filename_count = 0
|
|
28
|
+
last_show_filename = None
|
|
29
|
+
block_prints_location = True
|
|
30
|
+
press_enter_message = "Press <Enter> to continue..."
|
|
31
|
+
|
|
32
|
+
def get_source_location(stack_index=0):
|
|
33
|
+
""" Helper function to get the source location of the stack with 'stack_index' of the call stack. """
|
|
34
|
+
frameInfo = inspect.stack()[1+stack_index] # get frameInfo of calling frame
|
|
35
|
+
filename= frameInfo.filename
|
|
36
|
+
line_nr= frameInfo.lineno
|
|
37
|
+
function = frameInfo.function
|
|
38
|
+
return f'{filename}:{line_nr} in "{function}"'
|
|
39
|
+
|
|
40
|
+
def block(fun=None, *args, **kwargs):
|
|
41
|
+
"""
|
|
42
|
+
Calls the given function `fun` with specified arguments and keyword arguments,
|
|
43
|
+
waits for the user to press Enter, and returns the result of `fun`.
|
|
44
|
+
"""
|
|
45
|
+
stack_index = 0
|
|
46
|
+
if 'stack_index' in kwargs:
|
|
47
|
+
stack_index = kwargs['stack_index']
|
|
48
|
+
del kwargs['stack_index']
|
|
49
|
+
result = None
|
|
50
|
+
if callable(fun):
|
|
51
|
+
result = fun(*args, **kwargs)
|
|
52
|
+
if memory_graph.block_prints_location:
|
|
53
|
+
print('blocked at ' + get_source_location(1+stack_index), end=', ')
|
|
54
|
+
if memory_graph.press_enter_message:
|
|
55
|
+
print(memory_graph.press_enter_message)
|
|
56
|
+
input()
|
|
57
|
+
return result
|
|
58
|
+
|
|
59
|
+
def block_deprecated_message():
|
|
60
|
+
print("Warning: 'block=True' deprecated, use mg.block(fun) instead.")
|
|
61
|
+
input(f"{get_source_location(3)}, Press <Enter> to continue...")
|
|
62
|
+
|
|
63
|
+
def create_graph(data,
|
|
64
|
+
colors = None,
|
|
65
|
+
vertical_orientations = None,
|
|
66
|
+
slicers = None):
|
|
67
|
+
""" Creates and returns a memory graph from 'data'. """
|
|
68
|
+
config_helper.set_config(colors, vertical_orientations, slicers)
|
|
69
|
+
graphviz_graph = memory_to_nodes.memory_to_nodes(data)
|
|
70
|
+
return graphviz_graph
|
|
71
|
+
|
|
72
|
+
def number_filename(outfile):
|
|
73
|
+
""" Returns the 'outfile' with 'render_filename_count'. """
|
|
74
|
+
global render_filename_count
|
|
75
|
+
splits = outfile.split('.')
|
|
76
|
+
if len(splits)>1:
|
|
77
|
+
splits[-2]+=str(render_filename_count)
|
|
78
|
+
render_filename_count += 1
|
|
79
|
+
return '.'.join(splits)
|
|
80
|
+
return self.filename
|
|
81
|
+
|
|
82
|
+
def render(data, outfile=None, view=False, block=False,
|
|
83
|
+
colors = None,
|
|
84
|
+
vertical_orientations = None,
|
|
85
|
+
slicers = None,
|
|
86
|
+
numbered = False):
|
|
87
|
+
""" Renders the graph of 'data' to 'outfile' or `memory_graph.render_filename` when not specified. """
|
|
88
|
+
if outfile is None:
|
|
89
|
+
outfile = memory_graph.render_filename
|
|
90
|
+
graph = create_graph(data, colors, vertical_orientations, slicers)
|
|
91
|
+
if numbered:
|
|
92
|
+
outfile = number_filename(outfile)
|
|
93
|
+
if outfile.endswith('.gv') or outfile.endswith('.dot'):
|
|
94
|
+
graph.save(filename=outfile)
|
|
95
|
+
else:
|
|
96
|
+
graph.render(outfile=outfile, view=view, cleanup=False, quiet=False, quiet_view=False)
|
|
97
|
+
if block:
|
|
98
|
+
block_deprecated_message()
|
|
99
|
+
|
|
100
|
+
def show(data, outfile=None, view=False, block=False,
|
|
101
|
+
colors = None,
|
|
102
|
+
vertical_orientations = None,
|
|
103
|
+
slicers = None,
|
|
104
|
+
numbered = False):
|
|
105
|
+
""" Shows the graph of 'data' by first rendering and then opening the default viewer
|
|
106
|
+
application by file extension at first call, when the outfile changes, or
|
|
107
|
+
when view is True. """
|
|
108
|
+
if outfile is None:
|
|
109
|
+
outfile = memory_graph.render_filename
|
|
110
|
+
open_view = (outfile != memory_graph.last_show_filename) or view
|
|
111
|
+
render(data=data, outfile=outfile, view=open_view, block=block,
|
|
112
|
+
colors=colors,
|
|
113
|
+
vertical_orientations=vertical_orientations,
|
|
114
|
+
slicers=slicers, numbered=numbered)
|
|
115
|
+
memory_graph.last_show_filename = outfile
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# ------------ aliases
|
|
119
|
+
|
|
120
|
+
def sl(stack_index=0, colors = None, vertical_orientations = None, slicers = None):
|
|
121
|
+
"""
|
|
122
|
+
Shows the graph of locals() and blocks.
|
|
123
|
+
"""
|
|
124
|
+
data = get_locals_from_call_stack(stack_index=1+stack_index)
|
|
125
|
+
memory_graph.show(data, colors=colors, vertical_orientations=vertical_orientations, slicers=slicers)
|
|
126
|
+
|
|
127
|
+
def ss(stack_index=0, colors = None, vertical_orientations = None, slicers = None):
|
|
128
|
+
"""
|
|
129
|
+
Shows the graph of mg.stack() and blocks.
|
|
130
|
+
"""
|
|
131
|
+
data = stack(stack_index=1+stack_index)
|
|
132
|
+
memory_graph.show(data, colors=colors, vertical_orientations=vertical_orientations, slicers=slicers)
|
|
133
|
+
|
|
134
|
+
def bsl(stack_index=0, colors = None, vertical_orientations = None, slicers = None):
|
|
135
|
+
"""
|
|
136
|
+
Shows the graph of locals() and blocks.
|
|
137
|
+
"""
|
|
138
|
+
data = get_locals_from_call_stack(stack_index=1+stack_index)
|
|
139
|
+
memory_graph.block(memory_graph.show, data, stack_index=1+stack_index, block=False,
|
|
140
|
+
colors=colors, vertical_orientations=vertical_orientations, slicers=slicers)
|
|
141
|
+
|
|
142
|
+
def bss(stack_index=0, colors = None, vertical_orientations = None, slicers = None):
|
|
143
|
+
"""
|
|
144
|
+
Shows the graph of mg.stack() and blocks.
|
|
145
|
+
"""
|
|
146
|
+
data = stack(stack_index=1+stack_index)
|
|
147
|
+
memory_graph.block(memory_graph.show, data, stack_index=1+stack_index, block=False,
|
|
148
|
+
colors=colors, vertical_orientations=vertical_orientations, slicers=slicers)
|
|
149
|
+
|
|
150
|
+
def rl(stack_index=0, colors = None, vertical_orientations = None, slicers = None):
|
|
151
|
+
"""
|
|
152
|
+
Shows the graph of locals() and blocks.
|
|
153
|
+
"""
|
|
154
|
+
data = get_locals_from_call_stack(stack_index=1+stack_index)
|
|
155
|
+
memory_graph.render(data, block=False, colors=colors, vertical_orientations=vertical_orientations, slicers=slicers)
|
|
156
|
+
|
|
157
|
+
def rs(stack_index=0, colors = None, vertical_orientations = None, slicers = None):
|
|
158
|
+
"""
|
|
159
|
+
Shows the graph of mg.stack() and blocks.
|
|
160
|
+
"""
|
|
161
|
+
data = stack(stack_index=1+stack_index)
|
|
162
|
+
memory_graph.render(data, block=False, colors=colors, vertical_orientations=vertical_orientations, slicers=slicers)
|
|
163
|
+
|
|
164
|
+
def brl(stack_index=0, colors = None, vertical_orientations = None, slicers = None):
|
|
165
|
+
"""
|
|
166
|
+
Shows the graph of locals() and blocks.
|
|
167
|
+
"""
|
|
168
|
+
data = get_locals_from_call_stack(stack_index=1+stack_index)
|
|
169
|
+
memory_graph.block(memory_graph.render, data, stack_index=1+stack_index, block=False,
|
|
170
|
+
colors=colors, vertical_orientations=vertical_orientations, slicers=slicers)
|
|
171
|
+
|
|
172
|
+
def brs(stack_index=0, colors = None, vertical_orientations = None, slicers = None):
|
|
173
|
+
"""
|
|
174
|
+
Shows the graph of mg.stack() and blocks.
|
|
175
|
+
"""
|
|
176
|
+
data = stack(stack_index=1+stack_index)
|
|
177
|
+
memory_graph.block(memory_graph.render, data, stack_index=1+stack_index, block=False,
|
|
178
|
+
colors=colors, vertical_orientations=vertical_orientations, slicers=slicers)
|
|
179
|
+
|
|
180
|
+
def l(stack_index=0, colors = None, vertical_orientations = None, slicers = None):
|
|
181
|
+
"""
|
|
182
|
+
Shows the graph of locals() and blocks.
|
|
183
|
+
"""
|
|
184
|
+
bsl(stack_index=1+stack_index, colors=colors, vertical_orientations=vertical_orientations, slicers=slicers)
|
|
185
|
+
|
|
186
|
+
def s(stack_index=0, colors = None, vertical_orientations = None, slicers = None):
|
|
187
|
+
"""
|
|
188
|
+
Shows the graph of mg.stack() and blocks.
|
|
189
|
+
"""
|
|
190
|
+
bss(stack_index=1+stack_index, colors=colors, vertical_orientations=vertical_orientations, slicers=slicers)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# ------------ call stack
|
|
194
|
+
|
|
195
|
+
def get_locals_from_call_stack(stack_index=0):
|
|
196
|
+
""" Helper function to get locals of the stack with 'stack_inex' of the call stack. """
|
|
197
|
+
frameInfo = inspect.stack()[1+stack_index] # get frameInfo of calling frame
|
|
198
|
+
return frameInfo.frame.f_locals
|
|
199
|
+
|
|
200
|
+
def get_function_name(frameInfo):
|
|
201
|
+
frame = frameInfo.frame
|
|
202
|
+
func_name = frame.f_code.co_name
|
|
203
|
+
if 'self' in frame.f_locals: # instance method
|
|
204
|
+
return f"{frame.f_locals['self'].__class__.__name__}.{func_name}"
|
|
205
|
+
elif 'cls' in frame.f_locals: # class method
|
|
206
|
+
return f"{frame.f_locals['cls'].__name__}.{func_name}"
|
|
207
|
+
else: # forget about static method, too complex
|
|
208
|
+
return func_name # just the function
|
|
209
|
+
|
|
210
|
+
def stack_frames_to_dict(frames):
|
|
211
|
+
""" Returns a dictionary representing the data on the call stack.
|
|
212
|
+
Each key is the stack level and function name, each value is the locals of the frame at that level.
|
|
213
|
+
"""
|
|
214
|
+
def to_dict(value): # fix by TerenceTux for Python 3.13
|
|
215
|
+
return {k: v for k, v in value.items()}
|
|
216
|
+
return call_stack({f"{level}: {get_function_name(frameInfo)}" : to_dict(frameInfo.frame.f_locals)
|
|
217
|
+
for level, frameInfo in enumerate(frames)})
|
|
218
|
+
|
|
219
|
+
def locals():
|
|
220
|
+
""" Returns local variables. """
|
|
221
|
+
return locals()
|
|
222
|
+
|
|
223
|
+
def stack(through_function="<module>",stack_index=0):
|
|
224
|
+
return get_call_stack(through_function, 1+stack_index)
|
|
225
|
+
def get_call_stack(through_function="<module>",stack_index=0):
|
|
226
|
+
""" Gets the call stack up to and including the function 'through_function'. """
|
|
227
|
+
frames = reversed(list(
|
|
228
|
+
utils.take_through(lambda i: i.function==through_function, inspect.stack()[1+stack_index:])
|
|
229
|
+
))
|
|
230
|
+
return stack_frames_to_dict(frames)
|
|
231
|
+
|
|
232
|
+
def stack_after_through(after_function,through_function="<module>"):
|
|
233
|
+
return get_call_stack_after_through(after_function, through_function)
|
|
234
|
+
def get_call_stack_after_through(after_function,through_function="<module>", drop=0):
|
|
235
|
+
""" Gets the call stack after the function 'after_function' up to and includeing the function 'through_function'
|
|
236
|
+
and drops the first 'drop' stack frames. """
|
|
237
|
+
frames = reversed(list(it.islice(
|
|
238
|
+
utils.take_through(lambda i: i.function == through_function,
|
|
239
|
+
utils.take_after(lambda i: i.function == after_function, inspect.stack()))
|
|
240
|
+
, drop, None)))
|
|
241
|
+
return stack_frames_to_dict(frames)
|
|
242
|
+
|
|
243
|
+
def stack_pdb(after_function="trace_dispatch",through_function="<module>"):
|
|
244
|
+
return get_call_stack_pdb(after_function, through_function)
|
|
245
|
+
def get_call_stack_pdb(after_function="trace_dispatch",through_function="<module>"):
|
|
246
|
+
""" Get the call stack in a 'pdb' debugger session, filtering out the 'pdb' functions that polute the graph. """
|
|
247
|
+
return get_call_stack_after_through(after_function,through_function)
|
|
248
|
+
|
|
249
|
+
def stack_vscode(after_function="do_wait_suspend",through_function="<module>"):
|
|
250
|
+
return get_call_stack_vscode(after_function, through_function)
|
|
251
|
+
def get_call_stack_vscode(after_function="do_wait_suspend",through_function="<module>"):
|
|
252
|
+
""" Get the call stack in a 'vscode' debugger session, filtering out the 'vscode' functions that polute the graph. """
|
|
253
|
+
return get_call_stack_after_through(after_function,through_function)
|
|
254
|
+
|
|
255
|
+
def stack_cursor(after_function="do_wait_suspend",through_function="<module>"):
|
|
256
|
+
return get_call_stack_cursor(after_function, through_function)
|
|
257
|
+
def get_call_stack_cursor(after_function="do_wait_suspend",through_function="<module>"):
|
|
258
|
+
""" Get the call stack in a 'cursor' debugger session, filtering out the 'cursor' functions that polute the graph. """
|
|
259
|
+
return get_call_stack_after_through(after_function,through_function)
|
|
260
|
+
|
|
261
|
+
def stack_pycharm(after_function="do_wait_suspend",through_function="<module>"):
|
|
262
|
+
return get_call_stack_pycharm(after_function, through_function)
|
|
263
|
+
def get_call_stack_pycharm(after_function="do_wait_suspend",through_function="<module>"):
|
|
264
|
+
""" Get the call stack in a 'vscode' debugger session, filtering out the 'vscode' functions that polute the graph. """
|
|
265
|
+
return get_call_stack_after_through(after_function,through_function, 1)
|
|
266
|
+
|
|
267
|
+
def save_call_stack(filename):
|
|
268
|
+
""" Saves the call stack to 'filename' for inspection to see what functions need to be
|
|
269
|
+
filtered out to create the desired graph. """
|
|
270
|
+
with open(filename,'w') as file:
|
|
271
|
+
for frame in inspect.stack():
|
|
272
|
+
file.write(f"function:{frame.function} filename:{frame.filename}\n")
|
|
273
|
+
|
|
274
|
+
def print_call_stack_vars(stack_index=0):
|
|
275
|
+
""" Prints all variables on the call stack. """
|
|
276
|
+
for level, frameInfo in enumerate(reversed(inspect.stack())):
|
|
277
|
+
print('=====',level,frameInfo.function)
|
|
278
|
+
print(tuple(frameInfo.frame.f_locals.keys()))
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
# ------------ jupyter filtering
|
|
282
|
+
|
|
283
|
+
jupyter_filter_keys = {'exit','quit','v','In','Out','jupyter_filter_keys'}
|
|
284
|
+
def jupyter_locals_filter(jupyter_locals):
|
|
285
|
+
""" Filter out the jupyter specific keys that polute the graph. """
|
|
286
|
+
return {k:v for k,v in utils.filter_dict(jupyter_locals)
|
|
287
|
+
if k not in jupyter_filter_keys and k[0] != '_'}
|
|
288
|
+
|
|
289
|
+
def locals_jupyter(stack_index=0):
|
|
290
|
+
""" Get the locals of the calling frame in a jupyter notebook, filtering out the jupyter specific keys. """
|
|
291
|
+
return jupyter_locals_filter(get_locals_from_call_stack(1+stack_index))
|
|
292
|
+
|
|
293
|
+
def stack_jupyter(through_function="<module>",stack_index=0):
|
|
294
|
+
return get_call_stack_jupyter(through_function, 1+stack_index)
|
|
295
|
+
def get_call_stack_jupyter(through_function="<module>",stack_index=0):
|
|
296
|
+
""" Get the call stack in a jupyter notebook, filtering out the jupyter specific keys. """
|
|
297
|
+
call_stack = get_call_stack(through_function,1+stack_index)
|
|
298
|
+
globals_frame = next(iter(call_stack))
|
|
299
|
+
call_stack[globals_frame] = jupyter_locals_filter(call_stack[globals_frame])
|
|
300
|
+
return call_stack
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
# ------------ ipython filtering
|
|
304
|
+
|
|
305
|
+
ipython_filter_keys = {'mg_visualization_status', 'sys', 'ipython', 'In', 'Out', 'get_ipython', 'exit', 'quit', 'open'}
|
|
306
|
+
def ipython_locals_filter(ipython_locals):
|
|
307
|
+
""" Filter out the ipython specific keys that polute the graph. """
|
|
308
|
+
return {k:v for k,v in utils.filter_dict(ipython_locals)
|
|
309
|
+
if k not in ipython_filter_keys and k[0] != '_'}
|
|
310
|
+
|
|
311
|
+
def locals_ipython(stack_index=0):
|
|
312
|
+
""" Get the locals of the calling frame in a ipython, filtering out the ipython specific keys. """
|
|
313
|
+
return ipython_locals_filter(get_locals_from_call_stack(1+stack_index))
|
|
314
|
+
|
|
315
|
+
def stack_ipython(through_function="<module>",stack_index=0):
|
|
316
|
+
return get_call_stack_ipython(through_function, 1+stack_index)
|
|
317
|
+
def get_call_stack_ipython(through_function="<module>",stack_index=0):
|
|
318
|
+
""" Get the call stack in a ipython, filtering out the ipython specific keys. """
|
|
319
|
+
call_stack = get_call_stack(through_function,1+stack_index)
|
|
320
|
+
globals_frame = next(iter(call_stack))
|
|
321
|
+
call_stack[globals_frame] = ipython_locals_filter(call_stack[globals_frame])
|
|
322
|
+
return call_stack
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# This file is part of memory_graph.
|
|
2
|
+
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
|
|
5
|
+
class call_stack(dict):
|
|
6
|
+
"""Inherits from dict to give the call stack it own name and color. """
|
|
7
|
+
|
|
8
|
+
def __init__(self, *args, **kwargs):
|
|
9
|
+
super().__init__(*args, **kwargs)
|
memory_graph/config.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# This file is part of memory_graph.
|
|
2
|
+
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
|
|
5
|
+
""" Configuration file for the graph visualizer. The configuration values are set later by the 'config_default.py' file. """
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
max_string_length = None
|
|
9
|
+
graph_stability = None
|
|
10
|
+
|
|
11
|
+
not_node_types = {}
|
|
12
|
+
no_child_references_types = set()
|
|
13
|
+
|
|
14
|
+
type_to_string = { }
|
|
15
|
+
|
|
16
|
+
def to_string(data):
|
|
17
|
+
""" Convert data to string. """
|
|
18
|
+
data_type = type(data)
|
|
19
|
+
if data_type in type_to_string:
|
|
20
|
+
return type_to_string[data_type](data)
|
|
21
|
+
return str(data)
|
|
22
|
+
|
|
23
|
+
type_to_node = { }
|
|
24
|
+
|
|
25
|
+
type_to_color = { }
|
|
26
|
+
|
|
27
|
+
type_to_vertical_orientation = { }
|
|
28
|
+
|
|
29
|
+
type_to_slicer = { }
|
|
30
|
+
|
|
31
|
+
max_graph_depth = None
|
|
32
|
+
graph_cut_symbol = None
|
|
33
|
+
max_missing_edges = None
|
|
34
|
+
|
|
35
|
+
type_to_depth = { }
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# This file is part of memory_graph.
|
|
2
|
+
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
|
|
5
|
+
""" Sets the default configuration values for the memory graph. """
|
|
6
|
+
from memory_graph.node_leaf import Node_Leaf
|
|
7
|
+
from memory_graph.node_linear import Node_Linear
|
|
8
|
+
from memory_graph.node_key_value import Node_Key_Value
|
|
9
|
+
from memory_graph.node_table import Node_Table
|
|
10
|
+
|
|
11
|
+
from memory_graph.call_stack import call_stack
|
|
12
|
+
from memory_graph.slicer import Slicer
|
|
13
|
+
|
|
14
|
+
import memory_graph.config as config
|
|
15
|
+
import memory_graph.utils as utils
|
|
16
|
+
|
|
17
|
+
import types
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
""" The maximum length of strings shown in the graph. Longer strings will be truncated. """
|
|
21
|
+
config.max_string_length = 42
|
|
22
|
+
|
|
23
|
+
""" The number of references keeping child nodes in order versus other references pulling them out. """
|
|
24
|
+
config.graph_stability = 10
|
|
25
|
+
|
|
26
|
+
""" 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. """
|
|
27
|
+
config.not_node_types = {
|
|
28
|
+
type(None), bool, int, float, complex, str,
|
|
29
|
+
types.FunctionType,
|
|
30
|
+
types.MethodType,
|
|
31
|
+
classmethod,
|
|
32
|
+
staticmethod,
|
|
33
|
+
type(len),
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
""" Types that will not have references pointing to their children in the graph but instead will have their children visualized in their node. """
|
|
37
|
+
config.no_child_references_types = {dict, types.MappingProxyType}
|
|
38
|
+
|
|
39
|
+
""" Types that need an special conversion """
|
|
40
|
+
config.type_to_string = {
|
|
41
|
+
types.FunctionType: lambda data: data.__qualname__,
|
|
42
|
+
types.MethodType: lambda data: data.__qualname__,
|
|
43
|
+
classmethod: lambda data: data.__qualname__,
|
|
44
|
+
staticmethod: lambda data: data.__qualname__,
|
|
45
|
+
type(len): lambda data: data.__qualname__,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
""" Conversion from type to Node objects. """
|
|
49
|
+
config.type_to_node = {
|
|
50
|
+
str: lambda data: Node_Leaf(data, data), # visit as whole string, don't iterate over characters
|
|
51
|
+
call_stack: lambda data: Node_Key_Value(data, data.items()),
|
|
52
|
+
type: lambda data: Node_Key_Value(data, utils.filter_type_attributes(vars(data).items())),
|
|
53
|
+
range: lambda data: Node_Key_Value(data, {'start':data.start, 'stop':data.stop, 'step':data.step}.items()),
|
|
54
|
+
dict: lambda data: (
|
|
55
|
+
Node_Key_Value(data, utils.filter_dict(data) )
|
|
56
|
+
if dict in config.no_child_references_types else
|
|
57
|
+
Node_Linear(data, utils.filter_dict(data) )
|
|
58
|
+
),
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
""" Colors of different types in the graph. """
|
|
62
|
+
config.type_to_color = {
|
|
63
|
+
# ================= singular
|
|
64
|
+
type(None) : "gray",
|
|
65
|
+
bool : "pink",
|
|
66
|
+
int : "green",
|
|
67
|
+
float : "violetred1",
|
|
68
|
+
complex : "yellow",
|
|
69
|
+
str : "cyan",
|
|
70
|
+
# ================= linear
|
|
71
|
+
tuple : "orange",
|
|
72
|
+
list : "lightcoral",
|
|
73
|
+
set : "orchid1",
|
|
74
|
+
frozenset : "orchid2",
|
|
75
|
+
bytes : "khaki1",
|
|
76
|
+
bytearray : "khaki2",
|
|
77
|
+
# ================= key_value
|
|
78
|
+
Node_Key_Value : "seagreen1", # for classes
|
|
79
|
+
call_stack : 'khaki',
|
|
80
|
+
type: "seagreen3", # where class variables are stored
|
|
81
|
+
dict : "#60a5ff",
|
|
82
|
+
types.MappingProxyType : "dodgerblue2", # not used
|
|
83
|
+
range : "cornsilk2",
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
""" Types that will be visualized in vertical orientation if 'True', or horizontal orientation
|
|
87
|
+
if 'False'. Otherwise the Node decides based on it having references."""
|
|
88
|
+
config.type_to_vertical_orientation = {
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
""" Slicer objects for different types. """
|
|
92
|
+
config.type_to_slicer = {
|
|
93
|
+
Node_Linear: Slicer(5,3,5),
|
|
94
|
+
Node_Key_Value: Slicer(5,3,5),
|
|
95
|
+
Node_Table: (Slicer(3,2,3), Slicer(3,2,3)),
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
""" 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. """
|
|
99
|
+
config.max_graph_depth = 12
|
|
100
|
+
config.graph_cut_symbol = '✂'
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
""" Maximum introspection depth for different types. """
|
|
104
|
+
config.type_to_depth = {
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
""" Maximum number of missing edges that are shown. """
|
|
108
|
+
config.max_missing_edges = 2
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# This file is part of memory_graph.
|
|
2
|
+
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
|
|
5
|
+
""" This module provides helper functions to access the configuration of the memory graph. """
|
|
6
|
+
from memory_graph.slicer import Slicer
|
|
7
|
+
|
|
8
|
+
import memory_graph.config as config
|
|
9
|
+
|
|
10
|
+
type_to_color = None
|
|
11
|
+
type_to_vertical_orientation = None
|
|
12
|
+
type_to_slicer = None
|
|
13
|
+
|
|
14
|
+
def set_config(colors=None, vertical_orientations=None, slicers=None):
|
|
15
|
+
global type_to_color
|
|
16
|
+
global type_to_vertical_orientation
|
|
17
|
+
global type_to_slicer
|
|
18
|
+
type_to_color = config.type_to_color.copy()
|
|
19
|
+
type_to_vertical_orientation = config.type_to_vertical_orientation.copy()
|
|
20
|
+
type_to_slicer = config.type_to_slicer.copy()
|
|
21
|
+
if colors:
|
|
22
|
+
type_to_color |= colors
|
|
23
|
+
if vertical_orientations:
|
|
24
|
+
type_to_vertical_orientation |= vertical_orientations
|
|
25
|
+
if slicers:
|
|
26
|
+
type_to_slicer |= slicers
|
|
27
|
+
|
|
28
|
+
def get_property(data_id, data_type, node_type, dictionary, default):
|
|
29
|
+
if data_id in dictionary:
|
|
30
|
+
return dictionary[data_id]
|
|
31
|
+
if data_type in dictionary:
|
|
32
|
+
return dictionary[data_type]
|
|
33
|
+
if node_type in dictionary:
|
|
34
|
+
return dictionary[node_type]
|
|
35
|
+
return default
|
|
36
|
+
|
|
37
|
+
def get_color(node, default='white'):
|
|
38
|
+
return get_property(node.get_id(),
|
|
39
|
+
node.get_type(),
|
|
40
|
+
type(node),
|
|
41
|
+
type_to_color,
|
|
42
|
+
default)
|
|
43
|
+
|
|
44
|
+
def get_vertical_orientation(node, default):
|
|
45
|
+
return get_property(node.get_id(),
|
|
46
|
+
node.get_type(),
|
|
47
|
+
type(node),
|
|
48
|
+
type_to_vertical_orientation,
|
|
49
|
+
default)
|
|
50
|
+
|
|
51
|
+
def get_slicer(node, data, default=Slicer(3,2,3)):
|
|
52
|
+
return get_property(id(data),
|
|
53
|
+
type(data),
|
|
54
|
+
type(node),
|
|
55
|
+
type_to_slicer,
|
|
56
|
+
default)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# This file is part of memory_graph.
|
|
2
|
+
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
|
|
5
|
+
""" Extension to add the memory graph configuration for Numpy types. """
|
|
6
|
+
from memory_graph.node_linear import Node_Linear
|
|
7
|
+
from memory_graph.node_table import Node_Table
|
|
8
|
+
|
|
9
|
+
import memory_graph.config as config
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
|
|
13
|
+
config.not_node_types |= {
|
|
14
|
+
np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.uint32, np.uint64,
|
|
15
|
+
np.float16, np.float32, np.float64,
|
|
16
|
+
np.complex64, np.complex128,
|
|
17
|
+
np.bool_, np.bytes_, np.str_, np.datetime64, np.timedelta64
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
def ndarrayy_to_node(ndarrayy_data):
|
|
21
|
+
if len(ndarrayy_data.shape) == 2:
|
|
22
|
+
return Node_Table(ndarrayy_data, ndarrayy_data)
|
|
23
|
+
else:
|
|
24
|
+
return Node_Linear(ndarrayy_data, ndarrayy_data)
|
|
25
|
+
|
|
26
|
+
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
|
|
27
|
+
config.type_to_node[np.ndarray] = lambda data : ndarrayy_to_node(data)
|
|
28
|
+
|
|
29
|
+
config.type_to_color[np.ndarray] = "hotpink1"
|
|
30
|
+
config.type_to_color[np.matrix] = "hotpink2"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# This file is part of memory_graph.
|
|
2
|
+
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
|
|
5
|
+
""" Extension to add the memory graph configuration for Pandas type. """
|
|
6
|
+
from memory_graph.node_linear import Node_Linear
|
|
7
|
+
from memory_graph.node_table import Node_Table
|
|
8
|
+
|
|
9
|
+
import memory_graph.config as config
|
|
10
|
+
|
|
11
|
+
import pandas as pd
|
|
12
|
+
|
|
13
|
+
config.type_to_node[pd.DataFrame] = lambda data : (
|
|
14
|
+
Node_Table(data,
|
|
15
|
+
data.values.tolist(),
|
|
16
|
+
col_names = data.columns.tolist(),
|
|
17
|
+
row_names = [ str(i) for i in data.index.tolist()]
|
|
18
|
+
)
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
config.type_to_node[pd.Series] = lambda data : (
|
|
22
|
+
Node_Linear(data, data.tolist())
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
config.type_to_color[pd.DataFrame] = "olivedrab1"
|
|
26
|
+
config.type_to_color[pd.Series] = "olivedrab2"
|