memory-graph 0.3.5__tar.gz → 0.3.7__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 (38) hide show
  1. {memory_graph-0.3.5/memory_graph.egg-info → memory_graph-0.3.7}/PKG-INFO +161 -137
  2. {memory_graph-0.3.5 → memory_graph-0.3.7}/README.md +160 -136
  3. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/__init__.py +50 -44
  4. {memory_graph-0.3.5 → memory_graph-0.3.7/memory_graph.egg-info}/PKG-INFO +161 -137
  5. {memory_graph-0.3.5 → memory_graph-0.3.7}/setup.py +1 -1
  6. {memory_graph-0.3.5 → memory_graph-0.3.7}/LICENSE.txt +0 -0
  7. {memory_graph-0.3.5 → memory_graph-0.3.7}/MANIFEST.in +0 -0
  8. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/config.py +0 -0
  9. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/config_default.py +0 -0
  10. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/config_helpers.py +0 -0
  11. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/extension_numpy.py +0 -0
  12. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/extension_pandas.py +0 -0
  13. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/html_table.py +0 -0
  14. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/list_view.py +0 -0
  15. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/memory_to_nodes.py +0 -0
  16. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/node_base.py +0 -0
  17. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/node_key_value.py +0 -0
  18. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/node_linear.py +0 -0
  19. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/node_table.py +0 -0
  20. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/sequence.py +0 -0
  21. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/slicer.py +0 -0
  22. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/slices.py +0 -0
  23. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/slices_iterator.py +0 -0
  24. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/slices_table_iterator.py +0 -0
  25. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/t.py +0 -0
  26. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/test.py +0 -0
  27. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/test_memory_graph.py +0 -0
  28. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/test_memory_to_nodes.py +0 -0
  29. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/test_sequence.py +0 -0
  30. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/test_slicer.py +0 -0
  31. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/test_slices.py +0 -0
  32. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/test_slices_iterator.py +0 -0
  33. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph/utils.py +0 -0
  34. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph.egg-info/SOURCES.txt +0 -0
  35. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph.egg-info/dependency_links.txt +0 -0
  36. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph.egg-info/requires.txt +0 -0
  37. {memory_graph-0.3.5 → memory_graph-0.3.7}/memory_graph.egg-info/top_level.txt +0 -0
  38. {memory_graph-0.3.5 → memory_graph-0.3.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: memory_graph
3
- Version: 0.3.5
3
+ Version: 0.3.7
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
@@ -31,7 +31,7 @@ In Python, assigning the list from variable `a` to variable `b` causes both vari
31
31
  <table><tr><td>
32
32
 
33
33
  ```python
34
- import memory_graph
34
+ import memory_graph as mg
35
35
 
36
36
  # create the lists 'a' and 'b'
37
37
  a = [4, 3, 2]
@@ -47,7 +47,7 @@ print('ids:', id(a), id(b))
47
47
  print('identical?:', a is b)
48
48
 
49
49
  # show all local variables in a graph
50
- memory_graph.show( locals() )
50
+ mg.show( locals() )
51
51
  ```
52
52
 
53
53
  </td><td>
@@ -71,7 +71,7 @@ A better way to understand what data is shared is to draw a graph of the data us
71
71
  The [memory_graph](https://pypi.org/project/memory-graph/) package can graph many different data types.
72
72
 
73
73
  ```python
74
- import memory_graph
74
+ import memory_graph as mg
75
75
 
76
76
  class MyClass:
77
77
 
@@ -80,35 +80,35 @@ class MyClass:
80
80
  self.y = y
81
81
 
82
82
  data = [ range(1, 2), (3, 4), {5, 6}, {7:'seven', 8:'eight'}, MyClass(9, 10) ]
83
- memory_graph.show(data, block=True)
83
+ mg.show(data)
84
84
  ```
85
85
  ![many_types.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/many_types.png)
86
86
 
87
- By using `block=True` the program blocks until the ENTER key is pressed so you can view the graph before continuing program execution (and possibly viewing later graphs). 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:
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
88
 
89
89
  ```python
90
- memory_graph.render(data, "my_graph.pdf")
91
- memory_graph.render(data, "my_graph.png")
92
- memory_graph.render(data, "my_graph.gv") # Graphviz DOT file
90
+ mg.render(data, "my_graph.pdf")
91
+ mg.render(data, "my_graph.png")
92
+ mg.render(data, "my_graph.gv") # Graphviz DOT file
93
93
  ```
94
94
 
95
95
  # Chapters #
96
96
 
97
- [1. Python Data Model](#1-python-data-model)
97
+ [Python Data Model](#python-data-model)
98
98
 
99
- [2. Debugging](#2-debugging)
99
+ [Call Stack](#call-stack)
100
100
 
101
- [3. Call Stack](#3-call-stack)
101
+ [Debugging](#Debugging)
102
102
 
103
- [4. Datastructure Examples](#4-datastructure-examples)
103
+ [Datastructure Examples](#datastructure-examples)
104
104
 
105
- [5. Configuration](#5-configuration)
105
+ [Configuration](#configuration)
106
106
 
107
- [6. Extensions](#6-extensions)
107
+ [Extensions](#extensions)
108
108
 
109
- [7. Jupyter Notebook](#7-jupyter-notebook)
109
+ [Jupyter Notebook](#jupyter-notebook)
110
110
 
111
- [8. Troubleshooting](#8-troubleshooting)
111
+ [Troubleshooting](#troubleshooting)
112
112
 
113
113
 
114
114
  ## Author ##
@@ -117,27 +117,30 @@ Bas Terwijn
117
117
  ## Inspiration ##
118
118
  Inspired by [Python Tutor](https://pythontutor.com/).
119
119
 
120
+ ## Supported by ##
121
+ <img src="https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/uva.png" alt="University of Amsterdam" width="600">
122
+
120
123
  ___
121
124
  ___
122
125
 
123
- ## 1. Python Data Model ##
126
+ ## Python Data Model ##
124
127
  The [Python Data Model](https://docs.python.org/3/reference/datamodel.html) makes a distiction between immutable and mutable types:
125
128
 
126
129
  * **immutable**: bool, int, float, complex, str, tuple, bytes, frozenset
127
- * **mutable**: list, dict, set, class, ... (most other types)
130
+ * **mutable**: list, set, dict, classes, ... (most other types)
128
131
 
129
132
 
130
133
  ### Immutable Type ###
131
- In the code below variable `a` and `b` both reference the same tuple value (4, 3, 2). A tuple is an immutable type and therefore when we change variable `a` its value can not be mutated in place, and thus a copy is made and `a` and `b` reference a different value afterwards.
134
+ In the code below variable `a` and `b` both reference the same tuple value (4, 3, 2). A tuple is an immutable type and therefore when we change variable `a` its value **cannot** be mutated in place, and thus a copy is made and `a` and `b` reference a different value afterwards.
132
135
 
133
136
  ```python
134
- import memory_graph
137
+ import memory_graph as mg
135
138
 
136
139
  a = (4, 3, 2)
137
140
  b = a
138
- memory_graph.render(locals(), 'immutable1.png')
141
+ mg.render(locals(), 'immutable1.png')
139
142
  a += (1,)
140
- memory_graph.render(locals(), 'immutable2.png')
143
+ mg.render(locals(), 'immutable2.png')
141
144
  ```
142
145
  | ![mutable1.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/immutable1.png) | ![mutable2.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/immutable2.png) |
143
146
  |:-----------------------------------------------------------:|:-------------------------------------------------------------:|
@@ -148,27 +151,25 @@ memory_graph.render(locals(), 'immutable2.png')
148
151
  With mutable types the result is different. In the code below variable `a` and `b` both reference the same `list` value [4, 3, 2]. A `list` is a mutable type and therefore when we change variable `a` its value **can** be mutated in place and thus `a` and `b` both reference the same new value afterwards. Thus changing `a` also changes `b` and vice versa. Sometimes we want this but other times we don't and then we will have to make a copy so that `b` is independent from `a`.
149
152
 
150
153
  ```python
151
- import memory_graph
154
+ import memory_graph as mg
152
155
 
153
156
  a = [4, 3, 2]
154
157
  b = a
155
- memory_graph.render(locals(), 'mutable1.png')
158
+ mg.render(locals(), 'mutable1.png')
156
159
  a += [1] # equivalent to: a.append(1)
157
- memory_graph.render(locals(), 'mutable2.png')
160
+ mg.render(locals(), 'mutable2.png')
158
161
  ```
159
162
  | ![mutable1.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/mutable1.png) | ![mutable2.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/mutable2.png) |
160
163
  |:-----------------------------------------------------------:|:-------------------------------------------------------------:|
161
164
  | mutable1.png | mutable2.png |
162
165
 
163
-
164
- Python makes this distiction between mutable and immutable types because a value of a mutable type could be large and therefore it would be slow to make a copy each time we change it. On the other hand, a value of a immutable type generally is small and therefore fast to copy.
165
-
166
+ One practical reason why Python makes the distinction between mutable and immutable types is that a value of a mutable type could be large, making it inefficient to copy each time we change it. Immutable values generally don't need to change as much or are smaller, which makes copying less of a concern.
166
167
 
167
168
  ### Copying ###
168
169
  Python offers three different "copy" options that we will demonstrate using a nested list:
169
170
 
170
171
  ```python
171
- import memory_graph
172
+ import memory_graph as mg
172
173
  import copy
173
174
 
174
175
  a = [ [1, 2], ['x', 'y'] ] # a nested list (a list containing lists)
@@ -178,21 +179,21 @@ c1 = a
178
179
  c2 = copy.copy(a) # equivalent to: a.copy() a[:] list(a)
179
180
  c3 = copy.deepcopy(a)
180
181
 
181
- memory_graph.show(locals())
182
+ mg.show(locals())
182
183
  ```
183
184
 
184
- * `c1` is an **assignment**, all the data is shared, nothing is copied
185
- * `c2` is a **shallow copy**, only the data referenced by the first reference is copied and the underlying data is shared
186
- * `c3` is a **deep copy**, all the data is copied, nothing is shared
185
+ * `c1` is an **assignment**, nothing is copied, all the values are shared
186
+ * `c2` is a **shallow copy**, only the value referenced by the first reference is copied, all the underlying values are shared
187
+ * `c3` is a **deep copy**, all the values are copied, nothing is shared
187
188
 
188
189
  ![copies.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/copies.png)
189
190
 
190
191
 
191
192
  ### Custom Copy Method ###
192
- We can write our own custom copy function or method in case the three "copy" options don't do what we want. For example the copy() method of My_Class in the code below copies the `digits` but shares the `letters` between the two objects.
193
+ 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.
193
194
 
194
195
  ```python
195
- import memory_graph
196
+ import memory_graph as mg
196
197
  import copy
197
198
 
198
199
  class My_Class:
@@ -209,70 +210,22 @@ class My_Class:
209
210
  a = My_Class()
210
211
  b = a.copy()
211
212
 
212
- memory_graph.show(locals())
213
+ mg.show(locals())
213
214
  ```
214
215
  ![copy_method.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/copy_method.png)
215
216
 
216
217
 
217
- ## 2. Debugging ##
218
- Often it is useful to graph all the local variables using:
219
- ```python
220
- memory_graph.show(locals(), block=True)
221
- ```
218
+ ## Call Stack ##
219
+ 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`.
222
220
 
223
- So much so that function `d()` is available as alias for this for easier debugging. Additionally it can optionally log the data by printing them. For example:
224
221
  ```python
225
- import memory_graph
226
-
227
- squares = []
228
- squares_collector = []
229
- for i in range(1,6):
230
- squares.append(i**2)
231
- squares_collector.append(squares.copy())
232
- memory_graph.d(log=True)
233
- ```
234
- which after pressing ENTER a number of times results in:
235
-
236
- ![debugging.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/debugging.gif)
237
- ```
238
- squares: [1, 4, 9, 16, 25]
239
- squares_collector: [[1], [1, 4], [1, 4, 9], [1, 4, 9, 16], [1, 4, 9, 16, 25]]
240
- i: 5
241
- ```
242
-
243
- Function `d()` has these default arguments:
244
- ```python
245
- def d(data=None, graph=True, log=False, block=True):
246
- ```
247
- - data: the data that is handled, defaults to `locals()` when not specified
248
- - graph: if True the data is visualized as a graph
249
- - log: if True the data is printed
250
- - block: if True the function blocks until the ENTER key is pressed
251
-
252
- To print to a log file instead of standard output use:
253
- ```python
254
- memory_graph.log_file = open("my_log_file.txt", "w")
255
- ```
256
-
257
- ### Watchpoint in Debugger ###
258
- Alternatively you get an even better debugging experience when you set expression:
259
- ```
260
- memory_graph.render(locals(), "my_debug_graph.pdf")
261
- ```
262
- as a *watchpoint* in a debugger tool and open the "my_debug_graph.pdf" output file. This continuouly shows the graph of all the local variables while debugging and avoids having to add any memory_graph `show()`, `render()`, or `d()` calls to your code.
263
-
264
-
265
- ## 3. Call Stack ##
266
- The function `memory_graph.get_call_stack()` returns the complete call stack, including all local variables for each function in the stack. This allows us to simultaneously visualize the local variables of all the called functions. By doing so, we can identify whether any local variables from different functions in the call stack share data with one another. Here for example we call function ```add_one()``` with arguments ```a, b, c``` that adds 1 to each of its arguments.
267
-
268
- ```python
269
- import memory_graph
222
+ import memory_graph as mg
270
223
 
271
224
  def add_one(a, b, c):
272
225
  a += [1]
273
226
  b += (1,)
274
227
  c += [1]
275
- memory_graph.show(memory_graph.get_call_stack())
228
+ mg.show(mg.get_call_stack())
276
229
 
277
230
  a = [4, 3, 2]
278
231
  b = (4, 3, 2)
@@ -288,55 +241,127 @@ In the printed output only `a` is changed as a result of the function call:
288
241
  a:[4, 3, 2, 1] b:(4, 3, 2) c:[4, 3, 2]
289
242
  ```
290
243
 
291
- 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.
244
+ 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.
245
+
246
+ ### Block ###
247
+ It is often helpful to temporarily block program execution to inspect the graph. For this, you can use the `mg.block()` function:
248
+
249
+ ```python
250
+ mg.block(fun, arg1, arg2, ..., loc=True)
251
+ ```
292
252
 
253
+ This function first executes `fun(arg1, arg2, ...)`, then prints the current source location in the program, and blocks execution until the &lt;Enter&gt; key is pressed. To skip printing the source location, set `loc=False`.
293
254
 
294
255
  ### Recursion ###
295
- The call stack can be used to visualize how recursion works. Here we show each step of how recursively ```factorial(3)``` is computed:
256
+ The call stack is also helpful to visualize how recursion works. Here we use `mg.block()` to show each step of how recursively ```factorial(3)``` is computed:
296
257
 
297
258
  ```python
298
- import memory_graph
259
+ import memory_graph as mg
299
260
 
300
261
  def factorial(n):
301
262
  if n==0:
302
263
  return 1
303
- memory_graph.show( memory_graph.get_call_stack(), block=True )
264
+ mg.block(mg.show, mg.get_call_stack())
304
265
  result = n * factorial(n-1)
305
- memory_graph.show( memory_graph.get_call_stack(), block=True )
266
+ mg.block(mg.show, mg.get_call_stack())
306
267
  return result
307
268
 
308
269
  print(factorial(3))
309
270
  ```
271
+
310
272
  ![factorial.gif](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/factorial.gif)
311
273
 
312
- and the final result is: 1 x 2 x 3 = 6
274
+ and the result is: 1 x 2 x 3 = 6
275
+
276
+ ### Power Set ###
277
+ A more interesting recursive example that shows sharing of data is power_set(). A power set is the set of all subsets of a collection of values.
278
+
279
+ ```python
280
+ import memory_graph as mg
281
+
282
+ def get_subsets(subsets, data, i, subset):
283
+ mg.block(mg.show, mg.get_call_stack())
284
+ if i == len(data):
285
+ subsets.append(subset.copy())
286
+ return
287
+ subset.append(data[i])
288
+ get_subsets(subsets, data, i+1, subset) # do include data[i]
289
+ subset.pop()
290
+ get_subsets(subsets, data, i+1, subset) # don't include data[i]
291
+ mg.block(mg.show, mg.get_call_stack())
292
+
293
+ def power_set(data):
294
+ subsets = []
295
+ get_subsets(subsets, data, 0, [])
296
+ return subsets
297
+
298
+ print( power_set(['a', 'b', 'c']) )
299
+ ```
300
+
301
+ ![power_set.gif](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/power_set.gif)
302
+ ```
303
+ [['a', 'b', 'c'], ['a', 'b'], ['a', 'c'], ['a'], ['b', 'c'], ['b'], ['c'], []]
304
+ ```
313
305
 
314
- ### Call Stack in Watchpoint Context ###
315
- The ```memory_graph.get_call_stack()``` doesn't work well in a watchpoint context in most debuggers because debuggers introduce additional stack frames that cause problems. Use these alternative functions for various debuggers to filter out these problematic stack frames:
306
+
307
+ ## Debugging ##
308
+
309
+ For the best debugging experience with memory_graph set for example expression:
310
+ ```
311
+ mg.render(locals(), "my_graph.pdf")
312
+ ```
313
+ as a *watch* in a debugger tool such as the integrated debugger in Visual Studio Code. Then open the "my_graph.pdf" output file to continuously see all the local variables while debugging. This avoids having to add any memory_graph `show()`, `render()` calls to your code.
314
+
315
+ ### Call Stack in Watch Context ###
316
+ The ```mg.get_call_stack()``` doesn't work well in *watch* context in most debuggers because debuggers introduce additional stack frames that cause problems. Use these alternative functions for various debuggers to filter out these problematic stack frames:
316
317
 
317
318
  | debugger | function to get the call stack |
318
319
  |:---|:---|
319
- | **pdb, pudb** | `memory_graph.get_call_stack_pdb()` |
320
- | **Visual Studio Code** | `memory_graph.get_call_stack_vscode()` |
321
- | **Pycharm** | `memory_graph.get_call_stack_pycharm()` |
320
+ | **pdb, pudb** | `mg.get_call_stack_pdb()` |
321
+ | **Visual Studio Code** | `mg.get_call_stack_vscode()` |
322
+ | **Pycharm** | `mg.get_call_stack_pycharm()` |
322
323
 
323
324
  #### Other Debuggers ####
324
- For other debuggers, invoke this function within the watchpoint context. Then, in the "call_stack.txt" file, identify the slice of functions you wish to include in the call stack, more specifically choise 'after' and 'up_to' what function you want to slice.
325
+ For other debuggers, invoke this function within the *watch* context. Then, in the "call_stack.txt" file, identify the slice of functions you wish to include in the call stack.
325
326
  ```
326
- memory_graph.save_call_stack("call_stack.txt")
327
+ mg.save_call_stack("call_stack.txt")
327
328
  ```
328
- and then call this function to get the desired call stack to show in the graph:
329
+ Choose 'after' and 'up_to' what function you want to slice and then call this function to get the desired call stack:
329
330
  ```
330
- memory_graph.get_call_stack_after_up_to(after_function, up_to_function="<module>")
331
+ mg.get_call_stack_after_up_to(after_function, up_to_function="<module>")
331
332
  ```
332
333
 
334
+ ### Debugging without Debugger Tool ###
335
+
336
+ To simplify debugging without a debugger tool, we offer these blocking alias functions that you can insert into your code at a specific point to visualize a graph:
337
+
338
+ | alias | purpose | function call |
339
+ |:---|:---|:---|
340
+ | `mg.l()` | graph **l**ocal variables | `mg.block(mg.show, locals())` |
341
+ | `mg.s()` | graph the call **s**tack | `mg.block(mg.show, mg.get_call_stack())` |
342
+
343
+ For example, executing this program:
333
344
 
334
- ## 4. Datastructure Examples ##
345
+ ```python
346
+ from memory_graph as mg
347
+
348
+ squares = []
349
+ squares_collector = []
350
+ for i in range(1, 6):
351
+ squares.append(i**2)
352
+ squares_collector.append(squares.copy())
353
+ mg.l() # graph local variables
354
+ ```
355
+ and pressing &lt;Enter&gt; a number of times, results in:
356
+
357
+ ![debugging.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/debugging.gif)
358
+
359
+ ## Datastructure Examples ##
335
360
  Module memory_graph can be very useful in a course about datastructures, some examples:
336
361
 
337
362
  ### Doubly Linked List ###
338
363
  ```python
339
- import memory_graph
364
+ import memory_graph as mg
340
365
  import random
341
366
  random.seed(0) # use same random numbers each run
342
367
 
@@ -362,7 +387,7 @@ class LinkedList:
362
387
  new_node.next = self.head
363
388
  self.head.prev = new_node
364
389
  self.head = new_node
365
- memory_graph.d() # <--- draw graph
390
+ mg.block(mg.show, locals()) # <--- draw graph
366
391
 
367
392
  linked_list = LinkedList()
368
393
  n = 100
@@ -374,7 +399,7 @@ for i in range(n):
374
399
 
375
400
  ### Binary Tree ###
376
401
  ```python
377
- import memory_graph
402
+ import memory_graph as mg
378
403
  import random
379
404
  random.seed(0) # use same random numbers each run
380
405
 
@@ -401,7 +426,7 @@ class BinTree:
401
426
  node.larger = Node(new_value)
402
427
  else:
403
428
  self.add_recursive(new_value, node.larger)
404
- memory_graph.d() # <--- draw graph
429
+ mg.block(mg.show, locals()) # <--- draw graph
405
430
 
406
431
  def add(self, value):
407
432
  if self.root is None:
@@ -419,7 +444,7 @@ for i in range(n):
419
444
 
420
445
  ### Hash Set ###
421
446
  ```python
422
- import memory_graph
447
+ import memory_graph as mg
423
448
  import random
424
449
  random.seed(0) # use same random numbers each run
425
450
 
@@ -434,7 +459,7 @@ class HashSet:
434
459
  self.buckets[index] = []
435
460
  bucket = self.buckets[index]
436
461
  bucket.append(value)
437
- memory_graph.d() # <--- draw graph
462
+ mg.block(mg.show, locals()) # <--- draw graph
438
463
 
439
464
  def contains(self, value):
440
465
  index = hash(value) % len(self.buckets)
@@ -456,44 +481,44 @@ for i in range(n):
456
481
  ![hash_set.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/hash_set.png)
457
482
 
458
483
 
459
- ## 5. Configuration ##
484
+ ## Configuration ##
460
485
  Different aspects of memory_graph can be configured. The default configuration is reset by importing 'memory_graph.config_default'.
461
486
 
462
- - ***memory_graph.config.max_number_nodes*** : int
463
- - The maxium number of Nodes shown in the graph. When the graph gets too big set this to a smaller number. A `★` symbol indictes where the graph is cut short.
487
+ - ***mg.config.max_tree_depth*** : int
488
+ - The maxium depth of the graph. A `★` symbol indictes where the graph is cut short.
464
489
 
465
- - ***memory_graph.config.max_string_length*** : int
490
+ - ***mg.config.max_string_length*** : int
466
491
  - The maximum length of strings shown in the graph. Longer strings will be truncated.
467
492
 
468
- - ***memory_graph.config.not_node_types*** : set
493
+ - ***mg.config.not_node_types*** : set
469
494
  - Holds all types for which no seperate node is drawn but that instead are shown as elements in their parent Node.
470
495
 
471
- - ***memory_graph.config.no_child_references_types*** : set
496
+ - ***mg.config.no_child_references_types*** : set
472
497
  - The set of key_value types that don't draw references to their direct childeren but have their children shown as elements of their node.
473
498
 
474
- - ***memory_graph.config.type_to_node*** : dict
499
+ - ***mg.config.type_to_node*** : dict
475
500
  - Determines how a data types is converted to a Node (sub)class for visualization in the graph.
476
501
 
477
- - ***memory_graph.config.type_to_color*** : dict
502
+ - ***mg.config.type_to_color*** : dict
478
503
  - Maps each type to the [graphviz color](https://graphviz.org/doc/info/colors.html) it gets in the graph.
479
504
 
480
- - ***memory_graph.config.type_to_vertical_orientation*** : dict
505
+ - ***mg.config.type_to_vertical_orientation*** : dict
481
506
  - Maps each type to its orientation. Use 'True' for vertical and 'False' for horizontal. If not specified Node_Linear and Node_Key_Value are vertical unless they have references to children.
482
507
 
483
- - ***memory_graph.config.type_to_slicer*** : dict
508
+ - ***mg.config.type_to_slicer*** : dict
484
509
  - 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.
485
510
 
486
511
  ### Temporary Configuration ###
487
- In addition to the global configuration, a temporary configuration can be set for a single `show()`, `render()`, or `d()` 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:
512
+ In addition to the global configuration, a temporary configuration can be set for a single `show()`, `render()`, `d()`, `ds()` 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:
488
513
 
489
514
  ```python
490
- import memory_graph
491
- from memory_graph.Slicer import Slicer
515
+ import memory_graph as mg
516
+ from memory_graph.slicer import Slicer
492
517
 
493
518
  data = [ list(range(20)) for i in range(1,5)]
494
519
  highlight = data[2]
495
520
 
496
- memory_graph.show( locals(),
521
+ mg.show( locals(),
497
522
  colors = {id(highlight): "red" }, # set color to "red"
498
523
  vertical_orientations = {id(highlight): False }, # set horizontal orientation
499
524
  slicers = {id(highlight): Slicer()} # set no slicing
@@ -501,14 +526,14 @@ memory_graph.show( locals(),
501
526
  ```
502
527
  ![highlight.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/highlight.png)
503
528
 
504
- ## 6. Extensions ##
529
+ ## Extensions ##
505
530
  Different extensions are available for types from other Python packages.
506
531
 
507
532
  ### Numpy ###
508
- Numpy types `arrray` and `matrix` and `ndarray` can be graphed with the "memory_graph.extension_numpy" extension:
533
+ Numpy types `arrray` and `matrix` and `ndarray` can be graphed with "memory_graph.extension_numpy":
509
534
 
510
535
  ```python
511
- import memory_graph
536
+ import memory_graph as mg
512
537
  import numpy as np
513
538
  import memory_graph.extension_numpy
514
539
  np.random.seed(0) # use same random numbers each run
@@ -516,15 +541,15 @@ np.random.seed(0) # use same random numbers each run
516
541
  array = np.array([1.1, 2, 3, 4, 5])
517
542
  matrix = np.matrix([[i*20+j for j in range(20)] for i in range(20)])
518
543
  ndarray = np.random.rand(20,20)
519
- memory_graph.d()
544
+ mg.show(locals())
520
545
  ```
521
546
  ![extension_numpy.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/extension_numpy.png)
522
547
 
523
548
  ### Pandas ###
524
- Pandas types `Series` and `DataFrame` can be graphed with the "memory_graph.extension_pandas" extension:
549
+ Pandas types `Series` and `DataFrame` can be graphed with "memory_graph.extension_pandas":
525
550
 
526
551
  ```python
527
- import memory_graph
552
+ import memory_graph as mg
528
553
  import pandas as pd
529
554
  import memory_graph.extension_pandas
530
555
 
@@ -535,21 +560,20 @@ dataframe2 = pd.DataFrame({ 'Name' : [ 'Tom', 'Anna', 'Steve', 'Lisa'],
535
560
  'Age' : [ 28, 34, 29, 42],
536
561
  'Length' : [ 1.70, 1.66, 1.82, 1.73] },
537
562
  index=['one', 'two', 'three', 'four']) # with row names
538
- memory_graph.d()
563
+ mg.show(locals())
539
564
  ```
540
565
  ![extension_pandas.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/extension_pandas.png)
541
566
 
542
- ## 7. Jupyter Notebook ##
567
+ ## Jupyter Notebook ##
543
568
 
544
- In Jupyter Notebook `locals()` has additional variables that cause problems in the graph, use `memory_graph.locals_jupyter()` to get the local variables with these problematic variables filtered out. Use `memory_graph.get_call_stack_jupyter()` to get the whole call stack with these variables filtered out.
569
+ In Jupyter Notebook `locals()` has additional variables that cause problems in the graph, use `mg.locals_jupyter()` to get the local variables with these problematic variables filtered out. Use `mg.get_call_stack_jupyter()` to get the whole call stack with these variables filtered out.
545
570
 
546
571
  See for example [jupyter_example.ipynb](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/jupyter_example.ipynb).
547
572
  ![jupyter_example.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/jupyter_example.png)
548
573
 
549
574
 
550
- ## 8. Troubleshooting ##
575
+ ## Troubleshooting ##
551
576
 
552
- - 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) for example) and set it as the default PDF reader. Another solution is to `render()` the graph to a different output format and open it manually.
577
+ - 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.
553
578
 
554
579
  - 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.
555
-