memory-graph 0.3.5__tar.gz → 0.3.6__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.6}/PKG-INFO +173 -132
  2. {memory_graph-0.3.5 → memory_graph-0.3.6}/README.md +172 -131
  3. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/__init__.py +29 -1
  4. {memory_graph-0.3.5 → memory_graph-0.3.6/memory_graph.egg-info}/PKG-INFO +173 -132
  5. {memory_graph-0.3.5 → memory_graph-0.3.6}/setup.py +1 -1
  6. {memory_graph-0.3.5 → memory_graph-0.3.6}/LICENSE.txt +0 -0
  7. {memory_graph-0.3.5 → memory_graph-0.3.6}/MANIFEST.in +0 -0
  8. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/config.py +0 -0
  9. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/config_default.py +0 -0
  10. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/config_helpers.py +0 -0
  11. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/extension_numpy.py +0 -0
  12. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/extension_pandas.py +0 -0
  13. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/html_table.py +0 -0
  14. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/list_view.py +0 -0
  15. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/memory_to_nodes.py +0 -0
  16. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/node_base.py +0 -0
  17. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/node_key_value.py +0 -0
  18. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/node_linear.py +0 -0
  19. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/node_table.py +0 -0
  20. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/sequence.py +0 -0
  21. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/slicer.py +0 -0
  22. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/slices.py +0 -0
  23. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/slices_iterator.py +0 -0
  24. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/slices_table_iterator.py +0 -0
  25. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/t.py +0 -0
  26. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/test.py +0 -0
  27. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/test_memory_graph.py +0 -0
  28. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/test_memory_to_nodes.py +0 -0
  29. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/test_sequence.py +0 -0
  30. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/test_slicer.py +0 -0
  31. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/test_slices.py +0 -0
  32. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/test_slices_iterator.py +0 -0
  33. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph/utils.py +0 -0
  34. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph.egg-info/SOURCES.txt +0 -0
  35. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph.egg-info/dependency_links.txt +0 -0
  36. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph.egg-info/requires.txt +0 -0
  37. {memory_graph-0.3.5 → memory_graph-0.3.6}/memory_graph.egg-info/top_level.txt +0 -0
  38. {memory_graph-0.3.5 → memory_graph-0.3.6}/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.6
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, block=True)
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
+ By using `block=True` the program blocks until the &lt;Enter&gt; 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:
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,12 +179,12 @@ 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
 
@@ -192,7 +193,7 @@ memory_graph.show(locals())
192
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 the copy() method of My_Class in the code below copies the `digits` but shares the `letters` between the 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
- ```
222
-
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
- ```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.
218
+ ## Call Stack ##
219
+ The function `mg.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
220
 
268
221
  ```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,144 @@ 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.
292
245
 
293
246
 
294
247
  ### Recursion ###
295
248
  The call stack can be used to visualize how recursion works. Here we show each step of how recursively ```factorial(3)``` is computed:
296
249
 
297
250
  ```python
298
- import memory_graph
251
+ import memory_graph as mg
299
252
 
300
253
  def factorial(n):
301
254
  if n==0:
302
255
  return 1
303
- memory_graph.show( memory_graph.get_call_stack(), block=True )
256
+ mg.show( mg.get_call_stack(), block=True )
304
257
  result = n * factorial(n-1)
305
- memory_graph.show( memory_graph.get_call_stack(), block=True )
258
+ mg.show( mg.get_call_stack(), block=True )
306
259
  return result
307
260
 
308
261
  print(factorial(3))
309
262
  ```
263
+
264
+ Execution results in:
265
+
310
266
  ![factorial.gif](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/factorial.gif)
311
267
 
312
- and the final result is: 1 x 2 x 3 = 6
268
+ and the result is: 1 x 2 x 3 = 6
269
+
270
+ ### Power Set ###
271
+ A more insteresting recursive example that shows sharing of data is power_set(). A power set is the set of all subsets of a collection of values.
272
+
273
+ ```python
274
+ import memory_graph as mg
275
+
276
+ def get_subsets(subsets, data, i, subset):
277
+ mg.show(mg.get_call_stack(), block=True)
278
+ if i == len(data):
279
+ subsets.append(subset.copy())
280
+ return
281
+ subset.append(data[i])
282
+ get_subsets(subsets, data, i+1, subset) # do include data[i]
283
+ subset.pop()
284
+ get_subsets(subsets, data, i+1, subset) # don't include data[i]
285
+ mg.show(mg.get_call_stack(), block=True)
286
+
287
+ def power_set(data):
288
+ subsets = []
289
+ get_subsets(subsets, data, 0, [])
290
+ return subsets
313
291
 
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:
292
+ print( power_set(['a', 'b', 'c']) )
293
+ ```
294
+
295
+ Execution results in:
296
+
297
+ ![power_set.gif](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/power_set.gif)
298
+ ```
299
+ [['a', 'b', 'c'], ['a', 'b'], ['a', 'c'], ['a'], ['b', 'c'], ['b'], ['c'], []]
300
+ ```
301
+
302
+
303
+ ## Debugging ##
304
+
305
+ For the best debugging experience with memory_graph set for example expression:
306
+ ```
307
+ mg.render(locals(), "my_graph.pdf")
308
+ ```
309
+ 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.
310
+
311
+ ### Call Stack in Watch Context ###
312
+ 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
313
 
317
314
  | debugger | function to get the call stack |
318
315
  |:---|:---|
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()` |
316
+ | **pdb, pudb** | `mg.get_call_stack_pdb()` |
317
+ | **Visual Studio Code** | `mg.get_call_stack_vscode()` |
318
+ | **Pycharm** | `mg.get_call_stack_pycharm()` |
322
319
 
323
320
  #### 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.
321
+ 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.
322
+ ```
323
+ mg.save_call_stack("call_stack.txt")
324
+ ```
325
+ Choose 'after' and 'up_to' what function you want to slice and then call this function to get the desired call stack:
326
+ ```
327
+ mg.get_call_stack_after_up_to(after_function, up_to_function="<module>")
328
+ ```
329
+
330
+ ### Debugging without Debugger Tool ###
331
+
332
+ To make debugging without a debugger tool easier we provide these alias functions that you can add to your code where you want to view a graph:
333
+
334
+ | alias | function|
335
+ |:---|:---|
336
+ | `d()` | `mg.show(locals(), block=True)` |
337
+ | `ds()` | `mg.show(mg.get_call_stack(), block=True)` |
338
+
339
+ These functions have the following default arguments:
340
+ ```python
341
+ def d(data=None, graph=True, log=False, block=True):
325
342
  ```
326
- memory_graph.save_call_stack("call_stack.txt")
343
+ - data: defaults to locals() and mg.get_call_stack() respectively
344
+ - graph: if True the data is visualized as a graph
345
+ - log: if True the data is printed
346
+ - block: if True the function blocks until the &lt;Enter&gt; key is pressed
347
+
348
+ To print to a log file instead of standard output use:
349
+ ```python
350
+ mg.log_file = open("my_log_file.txt", "w")
327
351
  ```
328
- and then call this function to get the desired call stack to show in the graph:
352
+
353
+ For example, executing this program:
354
+
355
+ ```python
356
+ import memory_graph as mg
357
+ from memory_graph import d, ds
358
+
359
+ squares = []
360
+ squares_collector = []
361
+ for i in range(1, 6):
362
+ squares.append(i**2)
363
+ squares_collector.append(squares.copy())
364
+ d(log=True)
365
+ ```
366
+ and pressing &lt;Enter&gt; a number of times, produces:
367
+
368
+ ![debugging.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/debugging.gif)
329
369
  ```
330
- memory_graph.get_call_stack_after_up_to(after_function, up_to_function="<module>")
370
+ squares: [1, 4, 9, 16, 25]
371
+ squares_collector: [[1], [1, 4], [1, 4, 9], [1, 4, 9, 16], [1, 4, 9, 16, 25]]
372
+ i: 5
331
373
  ```
332
374
 
333
375
 
334
- ## 4. Datastructure Examples ##
376
+ ## Datastructure Examples ##
335
377
  Module memory_graph can be very useful in a course about datastructures, some examples:
336
378
 
337
379
  ### Doubly Linked List ###
338
380
  ```python
339
- import memory_graph
381
+ import memory_graph as mg
340
382
  import random
341
383
  random.seed(0) # use same random numbers each run
342
384
 
@@ -362,7 +404,7 @@ class LinkedList:
362
404
  new_node.next = self.head
363
405
  self.head.prev = new_node
364
406
  self.head = new_node
365
- memory_graph.d() # <--- draw graph
407
+ mg.show(locals(), block=True) # <--- draw graph
366
408
 
367
409
  linked_list = LinkedList()
368
410
  n = 100
@@ -374,7 +416,7 @@ for i in range(n):
374
416
 
375
417
  ### Binary Tree ###
376
418
  ```python
377
- import memory_graph
419
+ import memory_graph as mg
378
420
  import random
379
421
  random.seed(0) # use same random numbers each run
380
422
 
@@ -401,7 +443,7 @@ class BinTree:
401
443
  node.larger = Node(new_value)
402
444
  else:
403
445
  self.add_recursive(new_value, node.larger)
404
- memory_graph.d() # <--- draw graph
446
+ mg.show(locals(), block=True) # <--- draw graph
405
447
 
406
448
  def add(self, value):
407
449
  if self.root is None:
@@ -419,7 +461,7 @@ for i in range(n):
419
461
 
420
462
  ### Hash Set ###
421
463
  ```python
422
- import memory_graph
464
+ import memory_graph as mg
423
465
  import random
424
466
  random.seed(0) # use same random numbers each run
425
467
 
@@ -434,7 +476,7 @@ class HashSet:
434
476
  self.buckets[index] = []
435
477
  bucket = self.buckets[index]
436
478
  bucket.append(value)
437
- memory_graph.d() # <--- draw graph
479
+ mg.show(locals(), block=True) # <--- draw graph
438
480
 
439
481
  def contains(self, value):
440
482
  index = hash(value) % len(self.buckets)
@@ -456,44 +498,44 @@ for i in range(n):
456
498
  ![hash_set.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/hash_set.png)
457
499
 
458
500
 
459
- ## 5. Configuration ##
460
- Different aspects of memory_graph can be configured. The default configuration is reset by importing 'memory_graph.config_default'.
501
+ ## Configuration ##
502
+ Different aspects of memory_graph can be configured. The default configuration is reset by importing 'mg.config_default'.
461
503
 
462
- - ***memory_graph.config.max_number_nodes*** : int
504
+ - ***mg.config.max_number_nodes*** : int
463
505
  - 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.
464
506
 
465
- - ***memory_graph.config.max_string_length*** : int
507
+ - ***mg.config.max_string_length*** : int
466
508
  - The maximum length of strings shown in the graph. Longer strings will be truncated.
467
509
 
468
- - ***memory_graph.config.not_node_types*** : set
510
+ - ***mg.config.not_node_types*** : set
469
511
  - Holds all types for which no seperate node is drawn but that instead are shown as elements in their parent Node.
470
512
 
471
- - ***memory_graph.config.no_child_references_types*** : set
513
+ - ***mg.config.no_child_references_types*** : set
472
514
  - 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
515
 
474
- - ***memory_graph.config.type_to_node*** : dict
516
+ - ***mg.config.type_to_node*** : dict
475
517
  - Determines how a data types is converted to a Node (sub)class for visualization in the graph.
476
518
 
477
- - ***memory_graph.config.type_to_color*** : dict
519
+ - ***mg.config.type_to_color*** : dict
478
520
  - Maps each type to the [graphviz color](https://graphviz.org/doc/info/colors.html) it gets in the graph.
479
521
 
480
- - ***memory_graph.config.type_to_vertical_orientation*** : dict
522
+ - ***mg.config.type_to_vertical_orientation*** : dict
481
523
  - 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
524
 
483
- - ***memory_graph.config.type_to_slicer*** : dict
525
+ - ***mg.config.type_to_slicer*** : dict
484
526
  - 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
527
 
486
528
  ### 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:
529
+ 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
530
 
489
531
  ```python
490
- import memory_graph
491
- from memory_graph.Slicer import Slicer
532
+ import memory_graph as mg
533
+ from memory_graph.slicer import Slicer
492
534
 
493
535
  data = [ list(range(20)) for i in range(1,5)]
494
536
  highlight = data[2]
495
537
 
496
- memory_graph.show( locals(),
538
+ mg.show( locals(),
497
539
  colors = {id(highlight): "red" }, # set color to "red"
498
540
  vertical_orientations = {id(highlight): False }, # set horizontal orientation
499
541
  slicers = {id(highlight): Slicer()} # set no slicing
@@ -501,14 +543,14 @@ memory_graph.show( locals(),
501
543
  ```
502
544
  ![highlight.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/highlight.png)
503
545
 
504
- ## 6. Extensions ##
546
+ ## Extensions ##
505
547
  Different extensions are available for types from other Python packages.
506
548
 
507
549
  ### Numpy ###
508
550
  Numpy types `arrray` and `matrix` and `ndarray` can be graphed with the "memory_graph.extension_numpy" extension:
509
551
 
510
552
  ```python
511
- import memory_graph
553
+ import memory_graph as mg
512
554
  import numpy as np
513
555
  import memory_graph.extension_numpy
514
556
  np.random.seed(0) # use same random numbers each run
@@ -516,7 +558,7 @@ np.random.seed(0) # use same random numbers each run
516
558
  array = np.array([1.1, 2, 3, 4, 5])
517
559
  matrix = np.matrix([[i*20+j for j in range(20)] for i in range(20)])
518
560
  ndarray = np.random.rand(20,20)
519
- memory_graph.d()
561
+ mg.show(locals(), block=True)
520
562
  ```
521
563
  ![extension_numpy.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/extension_numpy.png)
522
564
 
@@ -524,7 +566,7 @@ memory_graph.d()
524
566
  Pandas types `Series` and `DataFrame` can be graphed with the "memory_graph.extension_pandas" extension:
525
567
 
526
568
  ```python
527
- import memory_graph
569
+ import memory_graph as mg
528
570
  import pandas as pd
529
571
  import memory_graph.extension_pandas
530
572
 
@@ -535,21 +577,20 @@ dataframe2 = pd.DataFrame({ 'Name' : [ 'Tom', 'Anna', 'Steve', 'Lisa'],
535
577
  'Age' : [ 28, 34, 29, 42],
536
578
  'Length' : [ 1.70, 1.66, 1.82, 1.73] },
537
579
  index=['one', 'two', 'three', 'four']) # with row names
538
- memory_graph.d()
580
+ mg.show(locals(), block=True)
539
581
  ```
540
582
  ![extension_pandas.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/extension_pandas.png)
541
583
 
542
- ## 7. Jupyter Notebook ##
584
+ ## Jupyter Notebook ##
543
585
 
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.
586
+ 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
587
 
546
588
  See for example [jupyter_example.ipynb](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/jupyter_example.ipynb).
547
589
  ![jupyter_example.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/jupyter_example.png)
548
590
 
549
591
 
550
- ## 8. Troubleshooting ##
592
+ ## Troubleshooting ##
551
593
 
552
594
  - 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.
553
595
 
554
596
  - 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
-