memory-graph 0.3.12__tar.gz → 0.3.13__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.3.12/memory_graph.egg-info → memory_graph-0.3.13}/PKG-INFO +24 -3
- {memory_graph-0.3.12 → memory_graph-0.3.13}/README.md +23 -2
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/__init__.py +1 -1
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/html_table.py +26 -31
- memory_graph-0.3.13/memory_graph/t.py +6 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13/memory_graph.egg-info}/PKG-INFO +24 -3
- memory_graph-0.3.13/memory_graph.egg-info/SOURCES.txt +37 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/setup.py +1 -1
- memory_graph-0.3.12/TODO.txt +0 -9
- memory_graph-0.3.12/images/add_one.png +0 -0
- memory_graph-0.3.12/images/add_one.py +0 -18
- memory_graph-0.3.12/images/avltree.py +0 -43
- memory_graph-0.3.12/images/avltree_base.png +0 -0
- memory_graph-0.3.12/images/avltree_dir.png +0 -0
- memory_graph-0.3.12/images/avltree_fail.png +0 -0
- memory_graph-0.3.12/images/avltree_key_value.png +0 -0
- memory_graph-0.3.12/images/avltree_linear.png +0 -0
- memory_graph-0.3.12/images/avltree_table.png +0 -0
- memory_graph-0.3.12/images/bin_tree.png +0 -0
- memory_graph-0.3.12/images/bin_tree.py +0 -47
- memory_graph-0.3.12/images/copies.png +0 -0
- memory_graph-0.3.12/images/copies.py +0 -15
- memory_graph-0.3.12/images/copy_method.png +0 -0
- memory_graph-0.3.12/images/copy_method.py +0 -22
- memory_graph-0.3.12/images/create_gif.sh +0 -19
- memory_graph-0.3.12/images/create_images.sh +0 -34
- memory_graph-0.3.12/images/debug_vscode.png +0 -0
- memory_graph-0.3.12/images/debugging.gif +0 -0
- memory_graph-0.3.12/images/debugging.py +0 -19
- memory_graph-0.3.12/images/extension_numpy.png +0 -0
- memory_graph-0.3.12/images/extension_numpy.py +0 -14
- memory_graph-0.3.12/images/extension_pandas.png +0 -0
- memory_graph-0.3.12/images/extension_pandas.py +0 -17
- memory_graph-0.3.12/images/factorial.gif +0 -0
- memory_graph-0.3.12/images/factorial.py +0 -24
- memory_graph-0.3.12/images/hash_set.png +0 -0
- memory_graph-0.3.12/images/hash_set.py +0 -39
- memory_graph-0.3.12/images/highlight.png +0 -0
- memory_graph-0.3.12/images/highlight.py +0 -15
- memory_graph-0.3.12/images/immutable.py +0 -11
- memory_graph-0.3.12/images/immutable1.png +0 -0
- memory_graph-0.3.12/images/immutable2.png +0 -0
- memory_graph-0.3.12/images/ipython.png +0 -0
- memory_graph-0.3.12/images/jupyter_example.ipynb +0 -85
- memory_graph-0.3.12/images/jupyter_example.png +0 -0
- memory_graph-0.3.12/images/linked_list.png +0 -0
- memory_graph-0.3.12/images/linked_list.py +0 -39
- memory_graph-0.3.12/images/many_types.png +0 -0
- memory_graph-0.3.12/images/many_types.py +0 -13
- memory_graph-0.3.12/images/mutable.py +0 -11
- memory_graph-0.3.12/images/mutable1.png +0 -0
- memory_graph-0.3.12/images/mutable2.png +0 -0
- memory_graph-0.3.12/images/power_set.gif +0 -0
- memory_graph-0.3.12/images/power_set.py +0 -28
- memory_graph-0.3.12/images/pyodide.png +0 -0
- memory_graph-0.3.12/images/uva.png +0 -0
- memory_graph-0.3.12/install.txt +0 -31
- memory_graph-0.3.12/memory_graph.egg-info/SOURCES.txt +0 -88
- memory_graph-0.3.12/src/auto_memory_graph.py +0 -21
- memory_graph-0.3.12/src/pyodide.html +0 -182
- memory_graph-0.3.12/uml/memory_graph.uxf +0 -322
- {memory_graph-0.3.12 → memory_graph-0.3.13}/LICENSE.txt +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/MANIFEST.in +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/config.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/config_default.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/config_helpers.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/extension_numpy.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/extension_pandas.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/list_view.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/memory_to_nodes.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/node_base.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/node_key_value.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/node_linear.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/node_table.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/sequence.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/slicer.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/slices.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/slices_iterator.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/slices_table_iterator.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/test.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/test_max_graph_depth.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/test_memory_graph.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/test_memory_to_nodes.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/test_sequence.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/test_slicer.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/test_slices.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/test_slices_iterator.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph/utils.py +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph.egg-info/dependency_links.txt +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph.egg-info/requires.txt +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/memory_graph.egg-info/top_level.txt +0 -0
- {memory_graph-0.3.12 → memory_graph-0.3.13}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: memory_graph
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.13
|
|
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
|
|
@@ -534,6 +534,27 @@ Different aspects of memory_graph can be configured. The default configuration i
|
|
|
534
534
|
- ***mg.config.type_to_slicer*** : dict
|
|
535
535
|
- Maps each type to a Slicer. A slicer determines how many elements of a data type are shown in the graph to prevent the graph from getting too big. 'Slicer()' does no slicing, 'Slicer(1,2,3)' shows just 1 element at the beginning, 2 in the middle, and 3 at the end.
|
|
536
536
|
|
|
537
|
+
### Simplified Graph ###
|
|
538
|
+
Memory_graph simplifies the visualization (and the viewer's mental model) by **not** showing separate nodes for immutable types like `bool`, `int`, `float`, `complex`, and `str` by default. This simplification can sometimes be slightly misleading. As in the example below, after a shallow copy, lists `a` and `b` technically share their `int` values, but the graph makes it appear as though `a` and `b` each have their own copies. However, since `int` is immutable, this simplification will never lead to unexpected changes—changing `a` won’t effect `b`.
|
|
539
|
+
|
|
540
|
+
The simplification strikes a balance: it is slightly misleading but keeps the graph clean and easy to understand and focuses on the mutable types where unexpected changes can occur. This is why it is the default behavior. If you do want to show separate nodes for `int` values, such as for educational purposes, you can simply remove `int` from the `mg.config.not_node_types` set:
|
|
541
|
+
```python
|
|
542
|
+
import memory_graph as mg
|
|
543
|
+
|
|
544
|
+
a = [100, 200, 300]
|
|
545
|
+
b = a.copy()
|
|
546
|
+
mg.render(locals(), 'not_node_types1.png')
|
|
547
|
+
|
|
548
|
+
mg.config.not_node_types.remove(int) # now show separate nodes for int values
|
|
549
|
+
|
|
550
|
+
mg.render(locals(), 'not_node_types2.png')
|
|
551
|
+
```
|
|
552
|
+
|  |  |
|
|
553
|
+
|:-----------------------------------------------------------:|:-------------------------------------------------------------:|
|
|
554
|
+
| not_node_types1.png, simplified | not_node_types2.png, technically correct |
|
|
555
|
+
|
|
556
|
+
Additionally, the simplification hides the [reuse of small `int` values](https://docs.python.org/3/c-api/long.html#c.PyLong_FromLong) in the current CPython implementation, an optimization that might otherwise confuse beginner Python programmers. For instance, after executing `a[1]+=1; b[1]+=1`, the `201` value is, maybe surprisingly, still shared between `a` and `b`, whereas executing `a[2]+=1; b[2]+=1` does not result in sharing the `301` value.
|
|
557
|
+
|
|
537
558
|
### Temporary Configuration ###
|
|
538
559
|
In addition to the global configuration, a temporary configuration can be set for a single `show()` or `render()` call to change the colors, orientation, and slicer. This example highlights a particular list element in red, gives it a horizontal orientation, and overwrites the default slicer for lists:
|
|
539
560
|
|
|
@@ -545,7 +566,7 @@ data = [ list(range(20)) for i in range(1,5)]
|
|
|
545
566
|
highlight = data[2]
|
|
546
567
|
|
|
547
568
|
mg.show( locals(),
|
|
548
|
-
colors = {id(highlight): "red" }, # set color to
|
|
569
|
+
colors = {id(highlight): "red" }, # set color to red
|
|
549
570
|
vertical_orientations = {id(highlight): False }, # set horizontal orientation
|
|
550
571
|
slicers = {id(highlight): Slicer()} # set no slicing
|
|
551
572
|
)
|
|
@@ -736,7 +757,7 @@ display( mg.create_graph(mg.locals_jupyter()) ) # display the local variables in
|
|
|
736
757
|
mg.block(display, mg.create_graph(mg.locals_jupyter()) ) # the same but blocked
|
|
737
758
|
```
|
|
738
759
|
|
|
739
|
-
See for example [jupyter_example.ipynb](https://raw.githubusercontent.com/bterwijn/memory_graph/main/
|
|
760
|
+
See for example [jupyter_example.ipynb](https://raw.githubusercontent.com/bterwijn/memory_graph/main/src/jupyter_example.ipynb).
|
|
740
761
|

|
|
741
762
|
|
|
742
763
|
## ipython ##
|
|
@@ -515,6 +515,27 @@ Different aspects of memory_graph can be configured. The default configuration i
|
|
|
515
515
|
- ***mg.config.type_to_slicer*** : dict
|
|
516
516
|
- Maps each type to a Slicer. A slicer determines how many elements of a data type are shown in the graph to prevent the graph from getting too big. 'Slicer()' does no slicing, 'Slicer(1,2,3)' shows just 1 element at the beginning, 2 in the middle, and 3 at the end.
|
|
517
517
|
|
|
518
|
+
### Simplified Graph ###
|
|
519
|
+
Memory_graph simplifies the visualization (and the viewer's mental model) by **not** showing separate nodes for immutable types like `bool`, `int`, `float`, `complex`, and `str` by default. This simplification can sometimes be slightly misleading. As in the example below, after a shallow copy, lists `a` and `b` technically share their `int` values, but the graph makes it appear as though `a` and `b` each have their own copies. However, since `int` is immutable, this simplification will never lead to unexpected changes—changing `a` won’t effect `b`.
|
|
520
|
+
|
|
521
|
+
The simplification strikes a balance: it is slightly misleading but keeps the graph clean and easy to understand and focuses on the mutable types where unexpected changes can occur. This is why it is the default behavior. If you do want to show separate nodes for `int` values, such as for educational purposes, you can simply remove `int` from the `mg.config.not_node_types` set:
|
|
522
|
+
```python
|
|
523
|
+
import memory_graph as mg
|
|
524
|
+
|
|
525
|
+
a = [100, 200, 300]
|
|
526
|
+
b = a.copy()
|
|
527
|
+
mg.render(locals(), 'not_node_types1.png')
|
|
528
|
+
|
|
529
|
+
mg.config.not_node_types.remove(int) # now show separate nodes for int values
|
|
530
|
+
|
|
531
|
+
mg.render(locals(), 'not_node_types2.png')
|
|
532
|
+
```
|
|
533
|
+
|  |  |
|
|
534
|
+
|:-----------------------------------------------------------:|:-------------------------------------------------------------:|
|
|
535
|
+
| not_node_types1.png, simplified | not_node_types2.png, technically correct |
|
|
536
|
+
|
|
537
|
+
Additionally, the simplification hides the [reuse of small `int` values](https://docs.python.org/3/c-api/long.html#c.PyLong_FromLong) in the current CPython implementation, an optimization that might otherwise confuse beginner Python programmers. For instance, after executing `a[1]+=1; b[1]+=1`, the `201` value is, maybe surprisingly, still shared between `a` and `b`, whereas executing `a[2]+=1; b[2]+=1` does not result in sharing the `301` value.
|
|
538
|
+
|
|
518
539
|
### Temporary Configuration ###
|
|
519
540
|
In addition to the global configuration, a temporary configuration can be set for a single `show()` or `render()` call to change the colors, orientation, and slicer. This example highlights a particular list element in red, gives it a horizontal orientation, and overwrites the default slicer for lists:
|
|
520
541
|
|
|
@@ -526,7 +547,7 @@ data = [ list(range(20)) for i in range(1,5)]
|
|
|
526
547
|
highlight = data[2]
|
|
527
548
|
|
|
528
549
|
mg.show( locals(),
|
|
529
|
-
colors = {id(highlight): "red" }, # set color to
|
|
550
|
+
colors = {id(highlight): "red" }, # set color to red
|
|
530
551
|
vertical_orientations = {id(highlight): False }, # set horizontal orientation
|
|
531
552
|
slicers = {id(highlight): Slicer()} # set no slicing
|
|
532
553
|
)
|
|
@@ -717,7 +738,7 @@ display( mg.create_graph(mg.locals_jupyter()) ) # display the local variables in
|
|
|
717
738
|
mg.block(display, mg.create_graph(mg.locals_jupyter()) ) # the same but blocked
|
|
718
739
|
```
|
|
719
740
|
|
|
720
|
-
See for example [jupyter_example.ipynb](https://raw.githubusercontent.com/bterwijn/memory_graph/main/
|
|
741
|
+
See for example [jupyter_example.ipynb](https://raw.githubusercontent.com/bterwijn/memory_graph/main/src/jupyter_example.ipynb).
|
|
721
742
|

|
|
722
743
|
|
|
723
744
|
## ipython ##
|
|
@@ -4,20 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
from memory_graph.node_base import Node_Base
|
|
6
6
|
import memory_graph.node_base
|
|
7
|
-
|
|
8
7
|
import memory_graph.config as config
|
|
9
|
-
|
|
10
8
|
import html
|
|
11
9
|
|
|
12
|
-
def
|
|
13
|
-
""" Helper function to add the
|
|
14
|
-
return (f'<\n<TABLE BORDER="
|
|
15
|
-
s + '
|
|
16
|
-
|
|
17
|
-
def inner_html_table(s):
|
|
18
|
-
""" Helper function to add the innner HTML table tags to the string s. """
|
|
19
|
-
return (' <TABLE BORDER="0" CELLBORDER="0" CELLSPACING="5" CELLPADDING="0">\n <TR>' +
|
|
20
|
-
s + '</TR>\n </TABLE>')
|
|
10
|
+
def html_table_frame(s, border, color, spacing=5):
|
|
11
|
+
""" Helper function to add the HTML table frame to the string s setting the 'border' and 'color'. """
|
|
12
|
+
return (f'<\n<TABLE BORDER="{border}" CELLBORDER="1" CELLSPACING="{spacing}" CELLPADDING="0" BGCOLOR="{color}" PORT="table">\n <TR>' +
|
|
13
|
+
s + '</TR>\n</TABLE>\n>')
|
|
21
14
|
|
|
22
15
|
def format_string(s):
|
|
23
16
|
""" Helper function to format the string s to be shown in the graph. Setting the max_string_length and escaping html characters. """
|
|
@@ -37,6 +30,7 @@ class HTML_Table:
|
|
|
37
30
|
"""
|
|
38
31
|
self.html = ''
|
|
39
32
|
self.add_new_line_flag = False
|
|
33
|
+
self.is_empty = True
|
|
40
34
|
self.col_count = 0
|
|
41
35
|
self.row_count = 0
|
|
42
36
|
self.ref_count = 0
|
|
@@ -61,18 +55,19 @@ class HTML_Table:
|
|
|
61
55
|
self.html += '</TR>\n <TR>'
|
|
62
56
|
self.add_new_line_flag = False
|
|
63
57
|
|
|
64
|
-
def add_string(self, s):
|
|
65
|
-
""" Add a string s to the
|
|
66
|
-
self.html += format_string(s)
|
|
58
|
+
def add_string(self, s, border=0):
|
|
59
|
+
""" Add a string s to the table. """
|
|
60
|
+
self.html += f'<TD BORDER="{border}">'+format_string(s)+'</TD>'
|
|
61
|
+
self.is_empty = False
|
|
67
62
|
|
|
68
63
|
def add_index(self, s):
|
|
69
|
-
""" Add an index s to the
|
|
64
|
+
""" Add an index s to the table. """
|
|
70
65
|
self.check_add_new_line()
|
|
71
|
-
self.html += f'<TD><font color="#505050">{str(s)}</font></TD>'
|
|
66
|
+
self.html += f'<TD BORDER="0"><font color="#505050">{str(s)}</font></TD>'
|
|
72
67
|
self.col_count += 1
|
|
73
68
|
|
|
74
69
|
def add_entry(self, node, nodes, child, id_to_slices, rounded=False, border=1, dashed=False):
|
|
75
|
-
""" Add child to the
|
|
70
|
+
""" Add child to the table either as reference if it is a Node_Base or as a value otherwise. """
|
|
76
71
|
#print('child:', child)
|
|
77
72
|
child_id = id(child)
|
|
78
73
|
if child_id in nodes:
|
|
@@ -85,14 +80,14 @@ class HTML_Table:
|
|
|
85
80
|
self.add_value(child, rounded, border)
|
|
86
81
|
|
|
87
82
|
def add_value(self, s, rounded=False, border=1):
|
|
88
|
-
""" Helper function to add a value s to the
|
|
83
|
+
""" Helper function to add a value s to the table. """
|
|
89
84
|
self.check_add_new_line()
|
|
90
85
|
r = ' STYLE="ROUNDED"' if rounded else ''
|
|
91
86
|
self.html += f'<TD BORDER="{border}"{r}> {format_string(s)} </TD>'
|
|
92
87
|
self.col_count += 1
|
|
93
88
|
|
|
94
89
|
def add_reference(self, node, child, rounded=False, border=1, dashed=False):
|
|
95
|
-
""" Helper function to add a reference to the
|
|
90
|
+
""" Helper function to add a reference to the table. """
|
|
96
91
|
self.check_add_new_line()
|
|
97
92
|
r = ' STYLE="ROUNDED"' if rounded else ''
|
|
98
93
|
self.html += f'<TD BORDER="{border}" PORT="ref{self.ref_count}"{r}> </TD>'
|
|
@@ -102,7 +97,7 @@ class HTML_Table:
|
|
|
102
97
|
self.col_count += 1
|
|
103
98
|
|
|
104
99
|
def add_dots(self, rounded=False, border=1):
|
|
105
|
-
""" Helper function to add dots to the
|
|
100
|
+
""" Helper function to add dots to the table. """
|
|
106
101
|
self.check_add_new_line()
|
|
107
102
|
r = 'STYLE="ROUNDED"' if rounded else ''
|
|
108
103
|
self.html += f'<TD BORDER="{border}" {r}>...</TD>'
|
|
@@ -110,26 +105,26 @@ class HTML_Table:
|
|
|
110
105
|
|
|
111
106
|
def to_string(self, border=1, color='white'):
|
|
112
107
|
""" Construct the HTML table string with the 'border' and 'color' settings. """
|
|
113
|
-
if self.col_count
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
self.html
|
|
117
|
-
return
|
|
108
|
+
if self.col_count == 0 and self.row_count == 0:
|
|
109
|
+
if self.is_empty:
|
|
110
|
+
self.add_string(' ')
|
|
111
|
+
return html_table_frame(self.html, border, color, spacing=0)
|
|
112
|
+
return html_table_frame(self.html, border, color)
|
|
118
113
|
|
|
119
114
|
def get_column(self):
|
|
120
|
-
""" Get the number of columns in the
|
|
115
|
+
""" Get the number of columns in the table. """
|
|
121
116
|
return self.col_count
|
|
122
117
|
|
|
123
118
|
def get_max_column(self):
|
|
124
|
-
""" Get the maximum value of the number of columns of rows in the
|
|
119
|
+
""" Get the maximum value of the number of columns of rows in the table. """
|
|
125
120
|
return self.max_col_count
|
|
126
121
|
|
|
127
122
|
def get_row(self):
|
|
128
|
-
""" Get the number of rows in the
|
|
123
|
+
""" Get the number of rows in the table. """
|
|
129
124
|
return self.row_count
|
|
130
125
|
|
|
131
126
|
def get_edges(self):
|
|
132
|
-
""" Get the edges that need to be
|
|
127
|
+
""" Get the edges that need to be added to connect the table to other tables in the graph. """
|
|
133
128
|
return self.edges
|
|
134
129
|
|
|
135
130
|
if __name__ == '__main__':
|
|
@@ -138,6 +133,6 @@ if __name__ == '__main__':
|
|
|
138
133
|
columns = 5
|
|
139
134
|
for r in range(rows):
|
|
140
135
|
for c in range(columns):
|
|
141
|
-
table.
|
|
136
|
+
table.add_value(f'{c},{r}')
|
|
142
137
|
table.add_new_line()
|
|
143
|
-
print(table)
|
|
138
|
+
print(table.to_string())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: memory_graph
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.13
|
|
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
|
|
@@ -534,6 +534,27 @@ Different aspects of memory_graph can be configured. The default configuration i
|
|
|
534
534
|
- ***mg.config.type_to_slicer*** : dict
|
|
535
535
|
- Maps each type to a Slicer. A slicer determines how many elements of a data type are shown in the graph to prevent the graph from getting too big. 'Slicer()' does no slicing, 'Slicer(1,2,3)' shows just 1 element at the beginning, 2 in the middle, and 3 at the end.
|
|
536
536
|
|
|
537
|
+
### Simplified Graph ###
|
|
538
|
+
Memory_graph simplifies the visualization (and the viewer's mental model) by **not** showing separate nodes for immutable types like `bool`, `int`, `float`, `complex`, and `str` by default. This simplification can sometimes be slightly misleading. As in the example below, after a shallow copy, lists `a` and `b` technically share their `int` values, but the graph makes it appear as though `a` and `b` each have their own copies. However, since `int` is immutable, this simplification will never lead to unexpected changes—changing `a` won’t effect `b`.
|
|
539
|
+
|
|
540
|
+
The simplification strikes a balance: it is slightly misleading but keeps the graph clean and easy to understand and focuses on the mutable types where unexpected changes can occur. This is why it is the default behavior. If you do want to show separate nodes for `int` values, such as for educational purposes, you can simply remove `int` from the `mg.config.not_node_types` set:
|
|
541
|
+
```python
|
|
542
|
+
import memory_graph as mg
|
|
543
|
+
|
|
544
|
+
a = [100, 200, 300]
|
|
545
|
+
b = a.copy()
|
|
546
|
+
mg.render(locals(), 'not_node_types1.png')
|
|
547
|
+
|
|
548
|
+
mg.config.not_node_types.remove(int) # now show separate nodes for int values
|
|
549
|
+
|
|
550
|
+
mg.render(locals(), 'not_node_types2.png')
|
|
551
|
+
```
|
|
552
|
+
|  |  |
|
|
553
|
+
|:-----------------------------------------------------------:|:-------------------------------------------------------------:|
|
|
554
|
+
| not_node_types1.png, simplified | not_node_types2.png, technically correct |
|
|
555
|
+
|
|
556
|
+
Additionally, the simplification hides the [reuse of small `int` values](https://docs.python.org/3/c-api/long.html#c.PyLong_FromLong) in the current CPython implementation, an optimization that might otherwise confuse beginner Python programmers. For instance, after executing `a[1]+=1; b[1]+=1`, the `201` value is, maybe surprisingly, still shared between `a` and `b`, whereas executing `a[2]+=1; b[2]+=1` does not result in sharing the `301` value.
|
|
557
|
+
|
|
537
558
|
### Temporary Configuration ###
|
|
538
559
|
In addition to the global configuration, a temporary configuration can be set for a single `show()` or `render()` call to change the colors, orientation, and slicer. This example highlights a particular list element in red, gives it a horizontal orientation, and overwrites the default slicer for lists:
|
|
539
560
|
|
|
@@ -545,7 +566,7 @@ data = [ list(range(20)) for i in range(1,5)]
|
|
|
545
566
|
highlight = data[2]
|
|
546
567
|
|
|
547
568
|
mg.show( locals(),
|
|
548
|
-
colors = {id(highlight): "red" }, # set color to
|
|
569
|
+
colors = {id(highlight): "red" }, # set color to red
|
|
549
570
|
vertical_orientations = {id(highlight): False }, # set horizontal orientation
|
|
550
571
|
slicers = {id(highlight): Slicer()} # set no slicing
|
|
551
572
|
)
|
|
@@ -736,7 +757,7 @@ display( mg.create_graph(mg.locals_jupyter()) ) # display the local variables in
|
|
|
736
757
|
mg.block(display, mg.create_graph(mg.locals_jupyter()) ) # the same but blocked
|
|
737
758
|
```
|
|
738
759
|
|
|
739
|
-
See for example [jupyter_example.ipynb](https://raw.githubusercontent.com/bterwijn/memory_graph/main/
|
|
760
|
+
See for example [jupyter_example.ipynb](https://raw.githubusercontent.com/bterwijn/memory_graph/main/src/jupyter_example.ipynb).
|
|
740
761
|

|
|
741
762
|
|
|
742
763
|
## ipython ##
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
LICENSE.txt
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
setup.py
|
|
5
|
+
memory_graph/__init__.py
|
|
6
|
+
memory_graph/config.py
|
|
7
|
+
memory_graph/config_default.py
|
|
8
|
+
memory_graph/config_helpers.py
|
|
9
|
+
memory_graph/extension_numpy.py
|
|
10
|
+
memory_graph/extension_pandas.py
|
|
11
|
+
memory_graph/html_table.py
|
|
12
|
+
memory_graph/list_view.py
|
|
13
|
+
memory_graph/memory_to_nodes.py
|
|
14
|
+
memory_graph/node_base.py
|
|
15
|
+
memory_graph/node_key_value.py
|
|
16
|
+
memory_graph/node_linear.py
|
|
17
|
+
memory_graph/node_table.py
|
|
18
|
+
memory_graph/sequence.py
|
|
19
|
+
memory_graph/slicer.py
|
|
20
|
+
memory_graph/slices.py
|
|
21
|
+
memory_graph/slices_iterator.py
|
|
22
|
+
memory_graph/slices_table_iterator.py
|
|
23
|
+
memory_graph/t.py
|
|
24
|
+
memory_graph/test.py
|
|
25
|
+
memory_graph/test_max_graph_depth.py
|
|
26
|
+
memory_graph/test_memory_graph.py
|
|
27
|
+
memory_graph/test_memory_to_nodes.py
|
|
28
|
+
memory_graph/test_sequence.py
|
|
29
|
+
memory_graph/test_slicer.py
|
|
30
|
+
memory_graph/test_slices.py
|
|
31
|
+
memory_graph/test_slices_iterator.py
|
|
32
|
+
memory_graph/utils.py
|
|
33
|
+
memory_graph.egg-info/PKG-INFO
|
|
34
|
+
memory_graph.egg-info/SOURCES.txt
|
|
35
|
+
memory_graph.egg-info/dependency_links.txt
|
|
36
|
+
memory_graph.egg-info/requires.txt
|
|
37
|
+
memory_graph.egg-info/top_level.txt
|
|
@@ -11,7 +11,7 @@ long_description_from_readme = (this_directory / "README.md").read_text()
|
|
|
11
11
|
|
|
12
12
|
setup(
|
|
13
13
|
name = 'memory_graph',
|
|
14
|
-
version = '0.3.
|
|
14
|
+
version = '0.3.13',
|
|
15
15
|
description = 'Draws a graph of your data to analyze its structure.',
|
|
16
16
|
long_description = long_description_from_readme,
|
|
17
17
|
long_description_content_type = 'text/markdown',
|
memory_graph-0.3.12/TODO.txt
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
mg.block(fun, print_loc=True), update docs, stack-overflow posts
|
|
3
|
-
|
|
4
|
-
Jupyter Notebook inline render using display()
|
|
5
|
-
|
|
6
|
-
webassembly inline render
|
|
7
|
-
|
|
8
|
-
optional max introspect depth for each type/id
|
|
9
|
-
https://discuss.python.org/t/request-for-feedback-memory-graph-a-python-visualization-tool-for-education/78347
|
|
Binary file
|
|
@@ -1,18 +0,0 @@
|
|
|
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 as mg
|
|
6
|
-
|
|
7
|
-
def add_one(a, b, c):
|
|
8
|
-
a += [1]
|
|
9
|
-
b += (1,)
|
|
10
|
-
c += [1]
|
|
11
|
-
mg.render( mg.get_call_stack(), "add_one.png")
|
|
12
|
-
|
|
13
|
-
a = [4, 3, 2]
|
|
14
|
-
b = (4, 3, 2)
|
|
15
|
-
c = [4, 3, 2]
|
|
16
|
-
|
|
17
|
-
add_one(a, b, c.copy())
|
|
18
|
-
print(f"a:{a} b:{b} c:{c}")
|
|
@@ -1,43 +0,0 @@
|
|
|
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 as mg
|
|
6
|
-
import bintrees
|
|
7
|
-
|
|
8
|
-
# Create an AVL tree
|
|
9
|
-
tree = bintrees.AVLTree()
|
|
10
|
-
tree.insert(10, "ten")
|
|
11
|
-
tree.insert(5, "five")
|
|
12
|
-
tree.insert(20, "twenty")
|
|
13
|
-
tree.insert(15, "fifteen")
|
|
14
|
-
|
|
15
|
-
# mg.render(locals(), 'avltree_fail.png') # id keeps changing
|
|
16
|
-
|
|
17
|
-
mg.config.type_to_color[bintrees.avltree.Node] = "sandybrown"
|
|
18
|
-
mg.config.type_to_node[bintrees.avltree.Node] = lambda data: mg.node_linear.Node_Linear(data, dir(data))
|
|
19
|
-
mg.config.type_to_slicer[bintrees.avltree.Node] = mg.slicer.Slicer()
|
|
20
|
-
mg.render(locals(), 'avltree_dir.png')
|
|
21
|
-
|
|
22
|
-
mg.config.type_to_node[bintrees.avltree.Node] = lambda data: mg.node_base.Node_Base(f"key:{data.key} value:{data.value}")
|
|
23
|
-
mg.render(locals(), 'avltree_base.png')
|
|
24
|
-
|
|
25
|
-
mg.config.type_to_node[bintrees.avltree.Node] = lambda data: mg.node_linear.Node_Linear(data,
|
|
26
|
-
['left:', data.left,
|
|
27
|
-
'key:', data.key,
|
|
28
|
-
'value:', data.value,
|
|
29
|
-
'right:', data.right])
|
|
30
|
-
mg.render(locals(), 'avltree_linear.png')
|
|
31
|
-
|
|
32
|
-
mg.config.type_to_node[bintrees.avltree.Node] = lambda data: mg.node_key_value.Node_Key_Value(data,
|
|
33
|
-
{'left': data.left,
|
|
34
|
-
'key': data.key,
|
|
35
|
-
'value': data.value,
|
|
36
|
-
'right': data.right}.items())
|
|
37
|
-
mg.render(locals(), 'avltree_key_value.png')
|
|
38
|
-
|
|
39
|
-
mg.config.type_to_node[bintrees.avltree.Node] = lambda data: mg.node_table.Node_Table(data,
|
|
40
|
-
[[data.key, data.value],
|
|
41
|
-
[data.left, data.right]]
|
|
42
|
-
)
|
|
43
|
-
mg.render(locals(), 'avltree_table.png')
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,47 +0,0 @@
|
|
|
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 as mg
|
|
6
|
-
import random
|
|
7
|
-
random.seed(0) # use same random numbers each run
|
|
8
|
-
|
|
9
|
-
class Node:
|
|
10
|
-
|
|
11
|
-
def __init__(self, value):
|
|
12
|
-
self.smaller = None
|
|
13
|
-
self.value = value
|
|
14
|
-
self.larger = None
|
|
15
|
-
|
|
16
|
-
class BinTree:
|
|
17
|
-
|
|
18
|
-
def __init__(self):
|
|
19
|
-
self.root = None
|
|
20
|
-
|
|
21
|
-
def add_recursive(self, new_value, node):
|
|
22
|
-
if new_value < node.value:
|
|
23
|
-
if node.smaller is None:
|
|
24
|
-
node.smaller = Node(new_value)
|
|
25
|
-
else:
|
|
26
|
-
self.add_recursive(new_value, node.smaller)
|
|
27
|
-
else:
|
|
28
|
-
if node.larger is None:
|
|
29
|
-
node.larger = Node(new_value)
|
|
30
|
-
else:
|
|
31
|
-
self.add_recursive(new_value, node.larger)
|
|
32
|
-
if new_value == 51:
|
|
33
|
-
mg.render(locals(), f"bin_tree.png")
|
|
34
|
-
exit(0)
|
|
35
|
-
|
|
36
|
-
def add(self, value):
|
|
37
|
-
if self.root is None:
|
|
38
|
-
self.root = Node(value)
|
|
39
|
-
else:
|
|
40
|
-
self.add_recursive(value, self.root)
|
|
41
|
-
|
|
42
|
-
tree = BinTree()
|
|
43
|
-
n = 100
|
|
44
|
-
for i in range(n):
|
|
45
|
-
new_value = random.randrange(n)
|
|
46
|
-
tree.add(new_value)
|
|
47
|
-
|
|
Binary file
|
|
@@ -1,15 +0,0 @@
|
|
|
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 as mg
|
|
6
|
-
import copy
|
|
7
|
-
|
|
8
|
-
a = [ [1, 2], ['x', 'y'] ] # a nested list (a list containing lists)
|
|
9
|
-
|
|
10
|
-
# three different ways to make a "copy" of 'a':
|
|
11
|
-
c1 = a
|
|
12
|
-
c2 = copy.copy(a) # equivalent to: a.copy() a[:] list(a)
|
|
13
|
-
c3 = copy.deepcopy(a)
|
|
14
|
-
|
|
15
|
-
mg.render(locals(), 'copies.png')
|
|
Binary file
|
|
@@ -1,22 +0,0 @@
|
|
|
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 as mg
|
|
6
|
-
import copy
|
|
7
|
-
|
|
8
|
-
class My_Class:
|
|
9
|
-
|
|
10
|
-
def __init__(self):
|
|
11
|
-
self.digits = [1, 2]
|
|
12
|
-
self.letters = ['x', 'y']
|
|
13
|
-
|
|
14
|
-
def copy(self): # custom copy method copies the digits but shares the letters
|
|
15
|
-
c = copy.copy(self)
|
|
16
|
-
c.digits = copy.copy(self.digits)
|
|
17
|
-
return c
|
|
18
|
-
|
|
19
|
-
a = My_Class()
|
|
20
|
-
b = a.copy()
|
|
21
|
-
|
|
22
|
-
mg.render(locals(), 'copy_method.png')
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
#
|
|
3
|
-
# install:
|
|
4
|
-
#
|
|
5
|
-
# sudo apt install imagemagick
|
|
6
|
-
|
|
7
|
-
name="$1"
|
|
8
|
-
files=$(ls -v $name*.png)
|
|
9
|
-
echo "creating gif with:"
|
|
10
|
-
echo "$files"
|
|
11
|
-
|
|
12
|
-
largest_size=$(identify -format "%Wx%H %f\n" $name*.png | sort -nr | head -n1)
|
|
13
|
-
echo "largest_size: $largest_size"
|
|
14
|
-
|
|
15
|
-
echo "resizing images"
|
|
16
|
-
mogrify -resize $largest_size -background white -gravity center -extent $largest_size $files
|
|
17
|
-
echo "creating file: $name.gif"
|
|
18
|
-
convert -delay 150 -loop 0 $files $name.gif
|
|
19
|
-
echo "done"
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# intro
|
|
2
|
-
python many_types.py
|
|
3
|
-
|
|
4
|
-
# debugging
|
|
5
|
-
python debugging.py
|
|
6
|
-
bash create_gif.sh debugging
|
|
7
|
-
|
|
8
|
-
# data model
|
|
9
|
-
python immutable.py
|
|
10
|
-
python mutable.py
|
|
11
|
-
python copies.py
|
|
12
|
-
python copy_method.py
|
|
13
|
-
|
|
14
|
-
# call stack
|
|
15
|
-
python add_one.py
|
|
16
|
-
python factorial.py
|
|
17
|
-
bash create_gif.sh factorial
|
|
18
|
-
python power_set.py
|
|
19
|
-
bash create_gif.sh power_set
|
|
20
|
-
|
|
21
|
-
# datastructures
|
|
22
|
-
python linked_list.py
|
|
23
|
-
python bin_tree.py
|
|
24
|
-
python hash_set.py
|
|
25
|
-
|
|
26
|
-
# configuration
|
|
27
|
-
python highlight.py
|
|
28
|
-
|
|
29
|
-
# extensions
|
|
30
|
-
python extension_numpy.py
|
|
31
|
-
python extension_pandas.py
|
|
32
|
-
|
|
33
|
-
# introspection
|
|
34
|
-
python avltree.py
|
|
Binary file
|
|
Binary file
|
|
@@ -1,19 +0,0 @@
|
|
|
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 as mg
|
|
6
|
-
|
|
7
|
-
image=0
|
|
8
|
-
def get_fac_name():
|
|
9
|
-
global image
|
|
10
|
-
image+=1
|
|
11
|
-
return f"debugging{image:02d}.png"
|
|
12
|
-
|
|
13
|
-
squares = []
|
|
14
|
-
squares_collector = []
|
|
15
|
-
for i in range(1,6):
|
|
16
|
-
squares.append(i**2)
|
|
17
|
-
squares_collector.append(squares.copy())
|
|
18
|
-
mg.render(locals(), get_fac_name())
|
|
19
|
-
mg.render(locals(), get_fac_name())
|
|
Binary file
|
|
@@ -1,14 +0,0 @@
|
|
|
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 as mg
|
|
6
|
-
import numpy as np
|
|
7
|
-
import memory_graph.extension_numpy
|
|
8
|
-
np.random.seed(0) # use same random numbers each run
|
|
9
|
-
|
|
10
|
-
array = np.array([1.1, 2, 3, 4, 5])
|
|
11
|
-
matrix = np.matrix([[i*20+j for j in range(20)] for i in range(20)])
|
|
12
|
-
ndarray = np.random.rand(20,20)
|
|
13
|
-
|
|
14
|
-
mg.render( locals(), "extension_numpy.png")
|
|
Binary file
|