memory-graph 0.3.13__tar.gz → 0.3.14__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.
Files changed (139) hide show
  1. memory_graph-0.3.14/MANIFEST.in +1 -0
  2. {memory_graph-0.3.13/memory_graph.egg-info → memory_graph-0.3.14}/PKG-INFO +40 -39
  3. {memory_graph-0.3.13 → memory_graph-0.3.14}/README.md +38 -37
  4. memory_graph-0.3.14/TODO.txt +9 -0
  5. memory_graph-0.3.14/images/.ipynb_checkpoints/jupyter_example-checkpoint.ipynb +85 -0
  6. memory_graph-0.3.14/images/add_one.png +0 -0
  7. memory_graph-0.3.14/images/add_one.py +18 -0
  8. memory_graph-0.3.14/images/avltree.py +43 -0
  9. memory_graph-0.3.14/images/avltree.py~ +41 -0
  10. memory_graph-0.3.14/images/avltree_base.png +0 -0
  11. memory_graph-0.3.14/images/avltree_dir.png +0 -0
  12. memory_graph-0.3.14/images/avltree_fail.gv +26 -0
  13. memory_graph-0.3.14/images/avltree_fail.png +0 -0
  14. memory_graph-0.3.14/images/avltree_key_value.png +0 -0
  15. memory_graph-0.3.14/images/avltree_linear.png +0 -0
  16. memory_graph-0.3.14/images/avltree_table.png +0 -0
  17. memory_graph-0.3.14/images/bin_tree.png +0 -0
  18. memory_graph-0.3.14/images/bin_tree.py +47 -0
  19. memory_graph-0.3.14/images/copies.png +0 -0
  20. memory_graph-0.3.14/images/copies.py +15 -0
  21. memory_graph-0.3.14/images/copy_method.png +0 -0
  22. memory_graph-0.3.14/images/copy_method.py +22 -0
  23. memory_graph-0.3.14/images/create_gif.sh +19 -0
  24. memory_graph-0.3.14/images/create_images.sh +35 -0
  25. memory_graph-0.3.14/images/debug_vscode.png +0 -0
  26. memory_graph-0.3.14/images/debugging.gif +0 -0
  27. memory_graph-0.3.14/images/debugging.py +19 -0
  28. memory_graph-0.3.14/images/debugging01.png +0 -0
  29. memory_graph-0.3.14/images/debugging02.png +0 -0
  30. memory_graph-0.3.14/images/debugging03.png +0 -0
  31. memory_graph-0.3.14/images/debugging04.png +0 -0
  32. memory_graph-0.3.14/images/debugging05.png +0 -0
  33. memory_graph-0.3.14/images/debugging06.png +0 -0
  34. memory_graph-0.3.14/images/extension_numpy.png +0 -0
  35. memory_graph-0.3.14/images/extension_numpy.py +14 -0
  36. memory_graph-0.3.14/images/extension_pandas.png +0 -0
  37. memory_graph-0.3.14/images/extension_pandas.py +17 -0
  38. memory_graph-0.3.14/images/factorial.gif +0 -0
  39. memory_graph-0.3.14/images/factorial.py +24 -0
  40. memory_graph-0.3.14/images/factorial01.png +0 -0
  41. memory_graph-0.3.14/images/factorial02.png +0 -0
  42. memory_graph-0.3.14/images/factorial03.png +0 -0
  43. memory_graph-0.3.14/images/factorial04.png +0 -0
  44. memory_graph-0.3.14/images/factorial05.png +0 -0
  45. memory_graph-0.3.14/images/factorial06.png +0 -0
  46. memory_graph-0.3.14/images/factorial07.png +0 -0
  47. memory_graph-0.3.14/images/hash_set.png +0 -0
  48. memory_graph-0.3.14/images/hash_set.py +39 -0
  49. memory_graph-0.3.14/images/highlight.png +0 -0
  50. memory_graph-0.3.14/images/highlight.py +15 -0
  51. memory_graph-0.3.14/images/immutable.py +11 -0
  52. memory_graph-0.3.14/images/immutable1.png +0 -0
  53. memory_graph-0.3.14/images/immutable2.png +0 -0
  54. memory_graph-0.3.14/images/ipython.png +0 -0
  55. memory_graph-0.3.14/images/jupyter_example.png +0 -0
  56. memory_graph-0.3.14/images/linked_list.png +0 -0
  57. memory_graph-0.3.14/images/linked_list.py +39 -0
  58. memory_graph-0.3.14/images/many_types.png +0 -0
  59. memory_graph-0.3.14/images/many_types.py +13 -0
  60. memory_graph-0.3.14/images/memory_graph.gv +20 -0
  61. memory_graph-0.3.14/images/memory_graph.gv.pdf +0 -0
  62. memory_graph-0.3.14/images/mutable.py +11 -0
  63. memory_graph-0.3.14/images/mutable1.png +0 -0
  64. memory_graph-0.3.14/images/mutable2.png +0 -0
  65. memory_graph-0.3.14/images/my_graph.gv +35 -0
  66. memory_graph-0.3.14/images/my_graph.pdf +0 -0
  67. memory_graph-0.3.14/images/not_node_types.py +9 -0
  68. memory_graph-0.3.14/images/not_node_types.py~ +9 -0
  69. memory_graph-0.3.14/images/not_node_types1.png +0 -0
  70. memory_graph-0.3.14/images/not_node_types2.png +0 -0
  71. memory_graph-0.3.14/images/power_set.gif +0 -0
  72. memory_graph-0.3.14/images/power_set.py +28 -0
  73. memory_graph-0.3.14/images/power_set1.png +0 -0
  74. memory_graph-0.3.14/images/power_set10.png +0 -0
  75. memory_graph-0.3.14/images/power_set11.png +0 -0
  76. memory_graph-0.3.14/images/power_set12.png +0 -0
  77. memory_graph-0.3.14/images/power_set13.png +0 -0
  78. memory_graph-0.3.14/images/power_set14.png +0 -0
  79. memory_graph-0.3.14/images/power_set15.png +0 -0
  80. memory_graph-0.3.14/images/power_set16.png +0 -0
  81. memory_graph-0.3.14/images/power_set17.png +0 -0
  82. memory_graph-0.3.14/images/power_set18.png +0 -0
  83. memory_graph-0.3.14/images/power_set19.png +0 -0
  84. memory_graph-0.3.14/images/power_set2.png +0 -0
  85. memory_graph-0.3.14/images/power_set20.png +0 -0
  86. memory_graph-0.3.14/images/power_set21.png +0 -0
  87. memory_graph-0.3.14/images/power_set22.png +0 -0
  88. memory_graph-0.3.14/images/power_set3.png +0 -0
  89. memory_graph-0.3.14/images/power_set4.png +0 -0
  90. memory_graph-0.3.14/images/power_set5.png +0 -0
  91. memory_graph-0.3.14/images/power_set6.png +0 -0
  92. memory_graph-0.3.14/images/power_set7.png +0 -0
  93. memory_graph-0.3.14/images/power_set8.png +0 -0
  94. memory_graph-0.3.14/images/power_set9.png +0 -0
  95. memory_graph-0.3.14/images/pyodide.png +0 -0
  96. memory_graph-0.3.14/images/uva.png +0 -0
  97. memory_graph-0.3.14/install.txt +31 -0
  98. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/__init__.py +20 -10
  99. {memory_graph-0.3.13 → memory_graph-0.3.14/memory_graph.egg-info}/PKG-INFO +40 -39
  100. memory_graph-0.3.14/memory_graph.egg-info/SOURCES.txt +134 -0
  101. {memory_graph-0.3.13 → memory_graph-0.3.14}/setup.py +3 -3
  102. memory_graph-0.3.14/src/auto_memory_graph.py +21 -0
  103. memory_graph-0.3.14/src/jupyter_example.ipynb +85 -0
  104. memory_graph-0.3.14/src/pyodide.html +182 -0
  105. memory_graph-0.3.14/uml/memory_graph.uxf +322 -0
  106. memory_graph-0.3.13/MANIFEST.in +0 -1
  107. memory_graph-0.3.13/memory_graph/t.py +0 -6
  108. memory_graph-0.3.13/memory_graph.egg-info/SOURCES.txt +0 -37
  109. {memory_graph-0.3.13 → memory_graph-0.3.14}/LICENSE.txt +0 -0
  110. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/config.py +0 -0
  111. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/config_default.py +0 -0
  112. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/config_helpers.py +0 -0
  113. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/extension_numpy.py +0 -0
  114. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/extension_pandas.py +0 -0
  115. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/html_table.py +0 -0
  116. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/list_view.py +0 -0
  117. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/memory_to_nodes.py +0 -0
  118. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/node_base.py +0 -0
  119. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/node_key_value.py +0 -0
  120. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/node_linear.py +0 -0
  121. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/node_table.py +0 -0
  122. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/sequence.py +0 -0
  123. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/slicer.py +0 -0
  124. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/slices.py +0 -0
  125. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/slices_iterator.py +0 -0
  126. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/slices_table_iterator.py +0 -0
  127. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/test.py +0 -0
  128. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/test_max_graph_depth.py +0 -0
  129. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/test_memory_graph.py +0 -0
  130. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/test_memory_to_nodes.py +0 -0
  131. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/test_sequence.py +0 -0
  132. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/test_slicer.py +0 -0
  133. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/test_slices.py +0 -0
  134. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/test_slices_iterator.py +0 -0
  135. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph/utils.py +0 -0
  136. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph.egg-info/dependency_links.txt +0 -0
  137. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph.egg-info/requires.txt +0 -0
  138. {memory_graph-0.3.13 → memory_graph-0.3.14}/memory_graph.egg-info/top_level.txt +0 -0
  139. {memory_graph-0.3.13 → memory_graph-0.3.14}/setup.cfg +0 -0
@@ -0,0 +1 @@
1
+ recursive-include images/ *
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: memory_graph
3
- Version: 0.3.13
4
- Summary: Draws a graph of your data to analyze its structure.
3
+ Version: 0.3.14
4
+ Summary: Generate intuitive graphs of your Python data, perfect for debugging and understanding complex relationships.
5
5
  Home-page: https://github.com/bterwijn/memory_graph
6
6
  Author: Bas Terwijn
7
7
  Author-email: bterwijn@gmail.com
@@ -24,9 +24,35 @@ pip install --upgrade memory_graph
24
24
  ```
25
25
  Additionally [Graphviz](https://graphviz.org/download/) needs to be installed.
26
26
 
27
- # Sharing Data #
27
+ # Memory Graph #
28
+ For program understanding and debugging, the [memory_graph](https://pypi.org/project/memory-graph/) package can visualize your data, supporting many different data types, including but not limited to:
29
+
30
+ ```python
31
+ import memory_graph as mg
32
+
33
+ class MyClass:
34
+
35
+ def __init__(self, x, y):
36
+ self.x = x
37
+ self.y = y
38
+
39
+ data = [ range(1, 2), (3, 4), {5, 6}, {7:'seven', 8:'eight'}, MyClass(9, 10) ]
40
+ mg.show(data)
41
+ ```
42
+ ![many_types.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/many_types.png)
28
43
 
29
- In Python, assigning the list from variable `a` to variable `b` causes both variables to reference the same list object and therefore share the data. Consequently, any change applied through one variable will impact the other. This behavior can lead to elusive bugs if a programmer incorrectly assumes that list `a` and `b` are independent.
44
+ Instead of showing the graph on screen you can also render it to an output file of your choosing (see [Graphviz Output Formats](https://graphviz.org/docs/outputs/)) using for example:
45
+
46
+ ```python
47
+ mg.render(data, "my_graph.pdf")
48
+ mg.render(data, "my_graph.svg")
49
+ mg.render(data, "my_graph.png")
50
+ mg.render(data, "my_graph.gv") # Graphviz DOT file
51
+ mg.render(data) # renders to 'mg.render_filename' with default value: 'memory_graph.pdf'
52
+ ```
53
+
54
+ # Sharing Values #
55
+ In Python, assigning the list from variable `a` to variable `b` causes both variables to reference the same list value and thus share it. Consequently, any change applied through one variable will impact the other. This behavior can lead to elusive bugs if a programmer incorrectly assumes that list `a` and `b` are independent.
30
56
 
31
57
  <table><tr><td>
32
58
 
@@ -67,33 +93,6 @@ identical?: True
67
93
  ```
68
94
  A better way to understand what data is shared is to draw a graph of the data using the [memory_graph](https://pypi.org/project/memory-graph/) package.
69
95
 
70
- # Memory Graph #
71
- The [memory_graph](https://pypi.org/project/memory-graph/) package can graph many different data types, not limited to:
72
-
73
- ```python
74
- import memory_graph as mg
75
-
76
- class MyClass:
77
-
78
- def __init__(self, x, y):
79
- self.x = x
80
- self.y = y
81
-
82
- data = [ range(1, 2), (3, 4), {5, 6}, {7:'seven', 8:'eight'}, MyClass(9, 10) ]
83
- mg.show(data)
84
- ```
85
- ![many_types.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/many_types.png)
86
-
87
- Instead of showing the graph you can also render it to an output file of your choosing (see [Graphviz Output Formats](https://graphviz.org/docs/outputs/)) using for example:
88
-
89
- ```python
90
- mg.render(data, "my_graph.pdf")
91
- mg.render(data, "my_graph.svg")
92
- mg.render(data, "my_graph.png")
93
- mg.render(data, "my_graph.gv") # Graphviz DOT file
94
- mg.render(data) # renders to 'mg.render_filename' with default value: 'memory_graph.pdf'
95
- ```
96
-
97
96
  # Chapters #
98
97
 
99
98
  [Python Data Model](#python-data-model)
@@ -197,7 +196,7 @@ mg.show(locals())
197
196
  ![copies.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/copies.png)
198
197
 
199
198
 
200
- ### Custom Copy Method ###
199
+ ### Custom Copy ###
201
200
  We can write our own custom copy function or method in case the three "copy" options don't do what we want. For example, in the code below the copy() method of My_Class copies the `digits` but shares the `letters` between two objects.
202
201
 
203
202
  ```python
@@ -224,7 +223,7 @@ mg.show(locals())
224
223
 
225
224
 
226
225
  ## Call Stack ##
227
- The `mg.get_call_stack()` function retrieves the entire call stack, including the local variables for each function on the stack. This enables us to visualize the local variables across all active functions simultaneously. Then by examining the graph, we can determine whether any local variables from different functions on the call stack share data. For instance, consider the function `add_one()` which adds the value `1` to each of its parameters `a`, `b`, and `c`.
226
+ The `mg.get_call_stack()` function retrieves the entire call stack, including the local variables for each function on the stack. This enables us to visualize the local variables across all active functions simultaneously. By examining the graph, we can determine whether any local variables from different functions share data. For instance, consider the function `add_one()` which adds the value `1` to each of its parameters `a`, `b`, and `c`.
228
227
 
229
228
  ```python
230
229
  import memory_graph as mg
@@ -252,7 +251,7 @@ a:[4, 3, 2, 1] b:(4, 3, 2) c:[4, 3, 2]
252
251
  This is because `b` is of immutable type 'tuple' so its value gets copied automatically when it is changed. And because the function is called with a copy of `c`, its original value is not changed by the function. The value of variable `a` is the only value of mutable type that is shared between the root stack frame **'0: \<module>'** and the **'1: add_one'** stack frame of the function so only that variable is affected as a result of the function call. The other changes remain confined to the local variables of the ```add_one()``` function.
253
252
 
254
253
  ### Block ###
255
- It is often helpful to temporarily block program execution to inspect the graph. For this, you can use the `mg.block()` function:
254
+ It is often helpful to temporarily block program execution to inspect the graph. For this we can use the `mg.block()` function:
256
255
 
257
256
  ```python
258
257
  mg.block(fun, arg1, arg2, ...)
@@ -264,7 +263,7 @@ This function:
264
263
  * then blocks execution until the &lt;Enter&gt; key is pressed
265
264
  * finally returns the value of the `fun()` call
266
265
 
267
- to change its behavior:
266
+ To change its behavior:
268
267
  * Set `mg.block_prints_location = False` to skip printing the source location.
269
268
  * Set `mg.press_enter_message = None` to skip printing "Press &lt;Enter&gt; to continue...".
270
269
 
@@ -319,7 +318,6 @@ print( power_set(['a', 'b', 'c']) )
319
318
  [['a', 'b', 'c'], ['a', 'b'], ['a', 'c'], ['a'], ['b', 'c'], ['b'], ['c'], []]
320
319
  ```
321
320
 
322
-
323
321
  ## Debugging ##
324
322
 
325
323
  For the best debugging experience with memory_graph set for example expression:
@@ -463,7 +461,7 @@ class BinTree:
463
461
  tree = BinTree()
464
462
  n = 100
465
463
  for i in range(n):
466
- new_value = random.randrange(100)
464
+ new_value = random.randrange(n)
467
465
  tree.add(new_value)
468
466
  ```
469
467
  ![bin_tree.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/bin_tree.png)
@@ -551,9 +549,9 @@ mg.render(locals(), 'not_node_types2.png')
551
549
  ```
552
550
  | ![not_node_types1](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/not_node_types1.png) | ![not_node_types2](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/not_node_types2.png) |
553
551
  |:-----------------------------------------------------------:|:-------------------------------------------------------------:|
554
- | not_node_types1.png, simplified | not_node_types2.png, technically correct |
552
+ | not_node_types1.png simplified | not_node_types2.png technically correct |
555
553
 
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.
554
+ 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
555
 
558
556
  ### Temporary Configuration ###
559
557
  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:
@@ -779,3 +777,6 @@ We can also run memory_graph in the browser: <a href="https://bterwijn.github.io
779
777
  - Adobe Acrobat Reader [doesn't refresh a PDF file](https://superuser.com/questions/337011/windows-pdf-viewer-that-auto-refreshes-pdf-when-compiling-with-pdflatex) when it changes on disk and blocks updates which results in an `Could not open 'somefile.pdf' for writing : Permission denied` error. One solution is to install a PDF reader that does refresh ([Evince](https://www.fosshub.com/Evince.html), [Okular](https://okular.kde.org/), [SumatraPDF](https://www.sumatrapdfreader.org/), ...) and set it as the default PDF reader. Another solution is to `render()` the graph to a different output format and to open it manually.
780
778
 
781
779
  - When graph edges overlap it can be hard to distinguish them. Using an interactive graphviz viewer, such as [xdot](https://github.com/jrfonseca/xdot.py), on a '*.gv' DOT output file will help.
780
+
781
+ ### Invocation_Tree Package ###
782
+ The [memory_graph](https://pypi.org/project/memory-graph/) package visualizes your data. If instead you want to visualize function calls, check out the [invocation_tree](https://pypi.org/project/invocation-tree/) package.
@@ -5,9 +5,35 @@ pip install --upgrade memory_graph
5
5
  ```
6
6
  Additionally [Graphviz](https://graphviz.org/download/) needs to be installed.
7
7
 
8
- # Sharing Data #
8
+ # Memory Graph #
9
+ For program understanding and debugging, the [memory_graph](https://pypi.org/project/memory-graph/) package can visualize your data, supporting many different data types, including but not limited to:
10
+
11
+ ```python
12
+ import memory_graph as mg
13
+
14
+ class MyClass:
15
+
16
+ def __init__(self, x, y):
17
+ self.x = x
18
+ self.y = y
19
+
20
+ data = [ range(1, 2), (3, 4), {5, 6}, {7:'seven', 8:'eight'}, MyClass(9, 10) ]
21
+ mg.show(data)
22
+ ```
23
+ ![many_types.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/many_types.png)
9
24
 
10
- In Python, assigning the list from variable `a` to variable `b` causes both variables to reference the same list object and therefore share the data. Consequently, any change applied through one variable will impact the other. This behavior can lead to elusive bugs if a programmer incorrectly assumes that list `a` and `b` are independent.
25
+ Instead of showing the graph on screen you can also render it to an output file of your choosing (see [Graphviz Output Formats](https://graphviz.org/docs/outputs/)) using for example:
26
+
27
+ ```python
28
+ mg.render(data, "my_graph.pdf")
29
+ mg.render(data, "my_graph.svg")
30
+ mg.render(data, "my_graph.png")
31
+ mg.render(data, "my_graph.gv") # Graphviz DOT file
32
+ mg.render(data) # renders to 'mg.render_filename' with default value: 'memory_graph.pdf'
33
+ ```
34
+
35
+ # Sharing Values #
36
+ In Python, assigning the list from variable `a` to variable `b` causes both variables to reference the same list value and thus share it. Consequently, any change applied through one variable will impact the other. This behavior can lead to elusive bugs if a programmer incorrectly assumes that list `a` and `b` are independent.
11
37
 
12
38
  <table><tr><td>
13
39
 
@@ -48,33 +74,6 @@ identical?: True
48
74
  ```
49
75
  A better way to understand what data is shared is to draw a graph of the data using the [memory_graph](https://pypi.org/project/memory-graph/) package.
50
76
 
51
- # Memory Graph #
52
- The [memory_graph](https://pypi.org/project/memory-graph/) package can graph many different data types, not limited to:
53
-
54
- ```python
55
- import memory_graph as mg
56
-
57
- class MyClass:
58
-
59
- def __init__(self, x, y):
60
- self.x = x
61
- self.y = y
62
-
63
- data = [ range(1, 2), (3, 4), {5, 6}, {7:'seven', 8:'eight'}, MyClass(9, 10) ]
64
- mg.show(data)
65
- ```
66
- ![many_types.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/many_types.png)
67
-
68
- Instead of showing the graph you can also render it to an output file of your choosing (see [Graphviz Output Formats](https://graphviz.org/docs/outputs/)) using for example:
69
-
70
- ```python
71
- mg.render(data, "my_graph.pdf")
72
- mg.render(data, "my_graph.svg")
73
- mg.render(data, "my_graph.png")
74
- mg.render(data, "my_graph.gv") # Graphviz DOT file
75
- mg.render(data) # renders to 'mg.render_filename' with default value: 'memory_graph.pdf'
76
- ```
77
-
78
77
  # Chapters #
79
78
 
80
79
  [Python Data Model](#python-data-model)
@@ -178,7 +177,7 @@ mg.show(locals())
178
177
  ![copies.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/copies.png)
179
178
 
180
179
 
181
- ### Custom Copy Method ###
180
+ ### Custom Copy ###
182
181
  We can write our own custom copy function or method in case the three "copy" options don't do what we want. For example, in the code below the copy() method of My_Class copies the `digits` but shares the `letters` between two objects.
183
182
 
184
183
  ```python
@@ -205,7 +204,7 @@ mg.show(locals())
205
204
 
206
205
 
207
206
  ## Call Stack ##
208
- The `mg.get_call_stack()` function retrieves the entire call stack, including the local variables for each function on the stack. This enables us to visualize the local variables across all active functions simultaneously. Then by examining the graph, we can determine whether any local variables from different functions on the call stack share data. For instance, consider the function `add_one()` which adds the value `1` to each of its parameters `a`, `b`, and `c`.
207
+ The `mg.get_call_stack()` function retrieves the entire call stack, including the local variables for each function on the stack. This enables us to visualize the local variables across all active functions simultaneously. By examining the graph, we can determine whether any local variables from different functions share data. For instance, consider the function `add_one()` which adds the value `1` to each of its parameters `a`, `b`, and `c`.
209
208
 
210
209
  ```python
211
210
  import memory_graph as mg
@@ -233,7 +232,7 @@ a:[4, 3, 2, 1] b:(4, 3, 2) c:[4, 3, 2]
233
232
  This is because `b` is of immutable type 'tuple' so its value gets copied automatically when it is changed. And because the function is called with a copy of `c`, its original value is not changed by the function. The value of variable `a` is the only value of mutable type that is shared between the root stack frame **'0: \<module>'** and the **'1: add_one'** stack frame of the function so only that variable is affected as a result of the function call. The other changes remain confined to the local variables of the ```add_one()``` function.
234
233
 
235
234
  ### Block ###
236
- It is often helpful to temporarily block program execution to inspect the graph. For this, you can use the `mg.block()` function:
235
+ It is often helpful to temporarily block program execution to inspect the graph. For this we can use the `mg.block()` function:
237
236
 
238
237
  ```python
239
238
  mg.block(fun, arg1, arg2, ...)
@@ -245,7 +244,7 @@ This function:
245
244
  * then blocks execution until the &lt;Enter&gt; key is pressed
246
245
  * finally returns the value of the `fun()` call
247
246
 
248
- to change its behavior:
247
+ To change its behavior:
249
248
  * Set `mg.block_prints_location = False` to skip printing the source location.
250
249
  * Set `mg.press_enter_message = None` to skip printing "Press &lt;Enter&gt; to continue...".
251
250
 
@@ -300,7 +299,6 @@ print( power_set(['a', 'b', 'c']) )
300
299
  [['a', 'b', 'c'], ['a', 'b'], ['a', 'c'], ['a'], ['b', 'c'], ['b'], ['c'], []]
301
300
  ```
302
301
 
303
-
304
302
  ## Debugging ##
305
303
 
306
304
  For the best debugging experience with memory_graph set for example expression:
@@ -444,7 +442,7 @@ class BinTree:
444
442
  tree = BinTree()
445
443
  n = 100
446
444
  for i in range(n):
447
- new_value = random.randrange(100)
445
+ new_value = random.randrange(n)
448
446
  tree.add(new_value)
449
447
  ```
450
448
  ![bin_tree.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/bin_tree.png)
@@ -532,9 +530,9 @@ mg.render(locals(), 'not_node_types2.png')
532
530
  ```
533
531
  | ![not_node_types1](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/not_node_types1.png) | ![not_node_types2](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/not_node_types2.png) |
534
532
  |:-----------------------------------------------------------:|:-------------------------------------------------------------:|
535
- | not_node_types1.png, simplified | not_node_types2.png, technically correct |
533
+ | not_node_types1.png simplified | not_node_types2.png technically correct |
536
534
 
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.
535
+ 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
536
 
539
537
  ### Temporary Configuration ###
540
538
  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:
@@ -760,3 +758,6 @@ We can also run memory_graph in the browser: <a href="https://bterwijn.github.io
760
758
  - Adobe Acrobat Reader [doesn't refresh a PDF file](https://superuser.com/questions/337011/windows-pdf-viewer-that-auto-refreshes-pdf-when-compiling-with-pdflatex) when it changes on disk and blocks updates which results in an `Could not open 'somefile.pdf' for writing : Permission denied` error. One solution is to install a PDF reader that does refresh ([Evince](https://www.fosshub.com/Evince.html), [Okular](https://okular.kde.org/), [SumatraPDF](https://www.sumatrapdfreader.org/), ...) and set it as the default PDF reader. Another solution is to `render()` the graph to a different output format and to open it manually.
761
759
 
762
760
  - When graph edges overlap it can be hard to distinguish them. Using an interactive graphviz viewer, such as [xdot](https://github.com/jrfonseca/xdot.py), on a '*.gv' DOT output file will help.
761
+
762
+ ### Invocation_Tree Package ###
763
+ The [memory_graph](https://pypi.org/project/memory-graph/) package visualizes your data. If instead you want to visualize function calls, check out the [invocation_tree](https://pypi.org/project/invocation-tree/) package.
@@ -0,0 +1,9 @@
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
@@ -0,0 +1,85 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "id": "23f6d43f-dd17-4020-971e-5bb8a5b1e30b",
6
+ "metadata": {},
7
+ "source": [
8
+ "# test: locals_jupyter()\n",
9
+ "Show a graph build with the filtered Jupyter locals using function `mg.locals_jupyter()`. Just adding integers to a list:"
10
+ ]
11
+ },
12
+ {
13
+ "cell_type": "code",
14
+ "execution_count": null,
15
+ "id": "e8913787-bbef-4adb-b027-ac0f28500233",
16
+ "metadata": {},
17
+ "outputs": [],
18
+ "source": [
19
+ "import memory_graph as mg\n",
20
+ "\n",
21
+ "data = []\n",
22
+ "for i in range(5):\n",
23
+ " data.append(i)\n",
24
+ " display(mg.create_graph(mg.locals_jupyter())) # display in jupyter notebook\n",
25
+ " mg.block(mg.show, mg.locals_jupyter()) # display in PDF reader\n",
26
+ " "
27
+ ]
28
+ },
29
+ {
30
+ "cell_type": "markdown",
31
+ "id": "f66d9b8d-0937-4ad0-97b4-a7459e84c4f2",
32
+ "metadata": {},
33
+ "source": [
34
+ "# test: get_call_stack_jupyter()\n",
35
+ "Show a graph build the filterd Jupyter call stack from function `mg.get_call_stack_jupyter()`. Recursively filling a list with all permutation of elements with resampling:"
36
+ ]
37
+ },
38
+ {
39
+ "cell_type": "code",
40
+ "execution_count": null,
41
+ "id": "15d0c443-7cc6-4b4f-a9db-598aaf261364",
42
+ "metadata": {},
43
+ "outputs": [],
44
+ "source": [
45
+ "import memory_graph as mg\n",
46
+ "\n",
47
+ "def get_all_permutations(permutations, elements, data, max_length):\n",
48
+ " if len(data) == max_length: # recursive stop condition\n",
49
+ " permutations.append(data.copy())\n",
50
+ " else:\n",
51
+ " for i in elements:\n",
52
+ " data.append(i)\n",
53
+ " mg.block(mg.show, mg.get_call_stack_jupyter())\n",
54
+ " get_all_permutations(permutations, elements, data, max_length)\n",
55
+ " data.pop()\n",
56
+ " mg.block(mg.show, mg.get_call_stack_jupyter())\n",
57
+ "\n",
58
+ "permutations = []\n",
59
+ "get_all_permutations(permutations, ['L','R'], [], 3)\n",
60
+ "print(permutations)"
61
+ ]
62
+ }
63
+ ],
64
+ "metadata": {
65
+ "kernelspec": {
66
+ "display_name": "Python 3 (ipykernel)",
67
+ "language": "python",
68
+ "name": "python3"
69
+ },
70
+ "language_info": {
71
+ "codemirror_mode": {
72
+ "name": "ipython",
73
+ "version": 3
74
+ },
75
+ "file_extension": ".py",
76
+ "mimetype": "text/x-python",
77
+ "name": "python",
78
+ "nbconvert_exporter": "python",
79
+ "pygments_lexer": "ipython3",
80
+ "version": "3.12.3"
81
+ }
82
+ },
83
+ "nbformat": 4,
84
+ "nbformat_minor": 5
85
+ }
Binary file
@@ -0,0 +1,18 @@
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}")
@@ -0,0 +1,43 @@
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')
@@ -0,0 +1,41 @@
1
+ import memory_graph as mg
2
+ import bintrees
3
+
4
+ # Create an AVL tree
5
+ tree = bintrees.AVLTree()
6
+ tree.insert(10, "ten")
7
+ tree.insert(5, "five")
8
+ tree.insert(20, "twenty")
9
+ tree.insert(15, "fifteen")
10
+
11
+ mg.render(locals(), 'avltree_fail.png')
12
+
13
+ mg.config.type_to_color[bintrees.avltree.Node] = "sandybrown"
14
+ mg.render(locals(), 'avltree_color.png')
15
+
16
+ mg.config.type_to_node[bintrees.avltree.Node] = lambda data: mg.node_linear.Node_Linear(data, dir(data))
17
+ mg.config.type_to_slicer[bintrees.avltree.Node] = mg.slicer.Slicer()
18
+ mg.render(locals(), 'avltree_dir.png')
19
+
20
+ mg.config.type_to_node[bintrees.avltree.Node] = lambda data: mg.node_base.Node_Base(f"key:{data.key} value:{data.value}")
21
+ mg.render(locals(), 'avltree_base.png')
22
+
23
+ mg.config.type_to_node[bintrees.avltree.Node] = lambda data: mg.node_linear.Node_Linear(data,
24
+ ['left', data.left,
25
+ 'key', data.key,
26
+ 'value', data.value,
27
+ 'right', data.right])
28
+ mg.render(locals(), 'avltree_linear.png')
29
+
30
+ mg.config.type_to_node[bintrees.avltree.Node] = lambda data: mg.node_key_value.Node_Key_Value(data,
31
+ {'left': data.left,
32
+ 'key': data.key,
33
+ 'value': data.value,
34
+ 'right': data.right}.items())
35
+ mg.render(locals(), 'avltree_key_value.png')
36
+
37
+ mg.config.type_to_node[bintrees.avltree.Node] = lambda data: mg.node_table.Node_Table(data,
38
+ [[data.key, data.value],
39
+ [data.left, data.right]]
40
+ )
41
+ mg.render(locals(), 'avltree_table.png')
@@ -0,0 +1,26 @@
1
+ digraph memory_graph {
2
+ node [shape=plaintext]
3
+ node130513740275104 [label=<
4
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="0" BGCOLOR="white"><TR><TD PORT="table">
5
+ &lt;bintrees.avltree.Node object at 0x76b3992...
6
+ </TD></TR></TABLE>
7
+ > xlabel="Node"]
8
+ node130513740453920 [label=<
9
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="0" BGCOLOR="seagreen1"><TR><TD PORT="table">
10
+ <TABLE BORDER="0" CELLBORDER="0" CELLSPACING="5" CELLPADDING="0">
11
+ <TR><TD BORDER="1" STYLE="ROUNDED"> _root </TD><TD BORDER="1" STYLE="ROUNDED"> _count </TD></TR>
12
+ <TR><TD BORDER="1" PORT="ref0"> </TD><TD BORDER="1"> 4 </TD></TR>
13
+ </TABLE>
14
+ </TD></TR></TABLE>
15
+ > xlabel=AVLTree]
16
+ node130513740453920:ref0 -> node130513740275104:table [style=solid]
17
+ node130513742307200 [label=<
18
+ <TABLE BORDER="0" CELLBORDER="3" CELLSPACING="0" CELLPADDING="0" BGCOLOR="dodgerblue1"><TR><TD PORT="table">
19
+ <TABLE BORDER="0" CELLBORDER="0" CELLSPACING="5" CELLPADDING="0">
20
+ <TR><TD BORDER="1" STYLE="ROUNDED"> tree </TD></TR>
21
+ <TR><TD BORDER="1" PORT="ref0"> </TD></TR>
22
+ </TABLE>
23
+ </TD></TR></TABLE>
24
+ > xlabel=dict]
25
+ node130513742307200:ref0 -> node130513740453920:table [style=solid]
26
+ }
Binary file
@@ -0,0 +1,47 @@
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
@@ -0,0 +1,15 @@
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')
@@ -0,0 +1,22 @@
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')