memory-graph 0.3.73__tar.gz → 0.3.75__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {memory_graph-0.3.73 → memory_graph-0.3.75}/PKG-INFO +124 -19
- {memory_graph-0.3.73 → memory_graph-0.3.75}/README.md +123 -18
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/__init__.py +1 -1
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/config.py +0 -7
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/config_default.py +9 -5
- memory_graph-0.3.75/memory_graph/config_helpers.py +72 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/html_table.py +11 -15
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/memory_to_nodes.py +6 -10
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/node_base.py +1 -1
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/node_key_value.py +1 -1
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/node_linear.py +1 -1
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/utils.py +37 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph.egg-info/PKG-INFO +124 -19
- {memory_graph-0.3.73 → memory_graph-0.3.75}/pyproject.toml +1 -1
- memory_graph-0.3.73/memory_graph/config_helpers.py +0 -44
- {memory_graph-0.3.73 → memory_graph-0.3.75}/LICENSE.txt +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/call_stack.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/extension_numpy.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/extension_pandas.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/extension_torch.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/list_view.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/node_leaf.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/node_table.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/sequence.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/slicer.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/slices.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/slices_iterator.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/slices_table_iterator.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/test.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/test_max_graph_depth.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/test_memory_graph.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/test_memory_to_nodes.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/test_sequence.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/test_slicer.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/test_slices.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph/test_slices_iterator.py +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph.egg-info/SOURCES.txt +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph.egg-info/dependency_links.txt +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph.egg-info/requires.txt +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/memory_graph.egg-info/top_level.txt +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/setup.cfg +0 -0
- {memory_graph-0.3.73 → memory_graph-0.3.75}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memory_graph
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.75
|
|
4
4
|
Summary: Teaching tool and debugging aid in context of references, mutable data types, and shallow and deep copy.
|
|
5
5
|
Author-email: Bas Terwijn <bterwijn@gmail.com>
|
|
6
6
|
License-Expression: BSD-2-Clause
|
|
@@ -34,7 +34,7 @@ Run a live demo in the 👉 [**Memory Graph Web Debugger**](https://memory-graph
|
|
|
34
34
|
- understand function calls, variable scope, and the **complete program state** through call stack visualization
|
|
35
35
|
|
|
36
36
|
An example Binary Tree data structure:
|
|
37
|
-

|
|
38
38
|
Or see it in the [Memory Grah Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/bin_tree.py×tep=0.2&play).
|
|
39
39
|
|
|
40
40
|
# Videos #
|
|
@@ -433,12 +433,12 @@ import memory_graph as mg
|
|
|
433
433
|
mg.config.type_to_horizontal[list] = True # horizontal lists
|
|
434
434
|
|
|
435
435
|
def binary(value: int) -> list[int]:
|
|
436
|
-
mg.block(mg.show
|
|
436
|
+
mg.block(mg.show, mg.stack())
|
|
437
437
|
if value == 0:
|
|
438
438
|
return []
|
|
439
439
|
quotient, remainder = divmod(value, 2)
|
|
440
440
|
result = binary(quotient) + [remainder]
|
|
441
|
-
mg.block(mg.show
|
|
441
|
+
mg.block(mg.show, mg.stack())
|
|
442
442
|
return result
|
|
443
443
|
|
|
444
444
|
print( binary(100) )
|
|
@@ -586,16 +586,16 @@ except Exception as e:
|
|
|
586
586
|
```
|
|
587
587
|
$ python exception_example.py
|
|
588
588
|
Traceback (most recent call last):
|
|
589
|
-
File "
|
|
589
|
+
File "exception_example.py", line 18, in <module>
|
|
590
590
|
raise e # raise to print traceback
|
|
591
591
|
^^^^^^^
|
|
592
|
-
File "
|
|
592
|
+
File "exception_example.py", line 15, in <module>
|
|
593
593
|
fun1()
|
|
594
|
-
File "
|
|
594
|
+
File "exception_example.py", line 12, in fun1
|
|
595
595
|
fun2()
|
|
596
|
-
File "
|
|
596
|
+
File "exception_example.py", line 9, in fun2
|
|
597
597
|
fun3()
|
|
598
|
-
File "
|
|
598
|
+
File "exception_example.py", line 6, in fun3
|
|
599
599
|
d[i] = i # throws IndexError when i = 3
|
|
600
600
|
~^^^
|
|
601
601
|
IndexError: list assignment index out of range
|
|
@@ -772,7 +772,6 @@ This example shows the flow of control when using inheritance with:
|
|
|
772
772
|
- class variable `message_count`
|
|
773
773
|
|
|
774
774
|
```python
|
|
775
|
-
from datetime import datetime
|
|
776
775
|
|
|
777
776
|
class Notification_Service:
|
|
778
777
|
message_count = 0
|
|
@@ -810,14 +809,14 @@ class SMS_Notification(Notification_Service):
|
|
|
810
809
|
email = Email_Notification(3, 'support@company.com')
|
|
811
810
|
email.send('customer@email.com', 'Your report is ready')
|
|
812
811
|
email.send('customer@email.com', 'Update to Privacy Policy')
|
|
813
|
-
sms =
|
|
812
|
+
sms = SMS_Notification(3, '0123456789')
|
|
814
813
|
sms.send('001122334455', 'Update to Privacy Policy')
|
|
815
814
|
```
|
|
816
|
-
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/inheritance.py&breakpoints=
|
|
815
|
+
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/inheritance.py&breakpoints=35&continues=1&play).
|
|
817
816
|
|
|
818
817
|
## Decorator ##
|
|
819
818
|
|
|
820
|
-
This example shows the flow of control when using a decorator.
|
|
819
|
+
This example shows the flow of control when using a decorator. A decorator wraps a function and is active before and after the function is called.
|
|
821
820
|
|
|
822
821
|
```python
|
|
823
822
|
def log_call(function):
|
|
@@ -851,29 +850,135 @@ This example shows the flow of control when using exception handling.
|
|
|
851
850
|
def fun2():
|
|
852
851
|
try:
|
|
853
852
|
d = [0] * 3
|
|
854
|
-
for i in range(
|
|
853
|
+
for i in range(6):
|
|
855
854
|
try:
|
|
856
|
-
|
|
857
|
-
|
|
855
|
+
print(f'{i=}')
|
|
856
|
+
d[i] = i # raises IndexError when i>=3
|
|
857
|
+
except ZeroDivisionError as e:
|
|
858
858
|
print(type(e), e)
|
|
859
859
|
except AssertionError as e:
|
|
860
860
|
print(type(e), e)
|
|
861
|
-
|
|
861
|
+
print('fun2() returns')
|
|
862
|
+
|
|
862
863
|
def fun1():
|
|
863
864
|
try:
|
|
864
865
|
return fun2()
|
|
865
866
|
except NameError as e:
|
|
866
867
|
print(type(e), e)
|
|
867
|
-
|
|
868
|
+
print('fun1() returns')
|
|
869
|
+
|
|
868
870
|
try:
|
|
869
871
|
fun1()
|
|
870
872
|
except LookupError as e:
|
|
871
873
|
print(type(e), e)
|
|
874
|
+
print('program ended cleanly')
|
|
872
875
|
```
|
|
873
|
-
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/exceptions.py&breakpoints=
|
|
876
|
+
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/exceptions.py&breakpoints=22&continues=1&play). In the program an `IndexError` exception is raised which propagates up the call stack until it reaches an except clause that matches its type where it is handled. Here, it is handled by the `LookupError` except clause because `IndexError` is a subclass of `LookupError`. Exceptions that are not handled terminate the execution of a program while its traceback is shown for analyses.
|
|
874
877
|
|
|
875
878
|

|
|
876
879
|
|
|
880
|
+
## Lazy Evaluation ##
|
|
881
|
+
|
|
882
|
+
In the following Eager and Lazy ealuation examples, we use this `pr()` function to print in what order things are created and used.
|
|
883
|
+
|
|
884
|
+
```python
|
|
885
|
+
def pr(tag, v):
|
|
886
|
+
print(tag, v)
|
|
887
|
+
return v
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
With eager evaluation, the function creates all three elements up front and stores them in a list before iteration begins. With lazy evaluation, the function returns a generator that creates each element only when it is needed.
|
|
891
|
+
|
|
892
|
+
<table>
|
|
893
|
+
<tr> <td width="50%" valign="top"><strong>Eager</strong></td> <td width="50%" valign="top"><strong>Lazy</strong></td></tr>
|
|
894
|
+
<tr><td width="50%" valign="top">
|
|
895
|
+
|
|
896
|
+
```python
|
|
897
|
+
def fun():
|
|
898
|
+
result = []
|
|
899
|
+
for i in range(3):
|
|
900
|
+
result.append(pr('create:', i))
|
|
901
|
+
return result
|
|
902
|
+
|
|
903
|
+
for i in fun():
|
|
904
|
+
pr('use:', i)
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
Run in [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/eager_return.py&play)
|
|
908
|
+
|
|
909
|
+
</td><td width="50%" valign="top">
|
|
910
|
+
|
|
911
|
+
```python
|
|
912
|
+
def fun():
|
|
913
|
+
for i in range(3):
|
|
914
|
+
yield pr('create:', i)
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
|
|
918
|
+
for i in fun():
|
|
919
|
+
pr('use:', i)
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
Run in [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/lazy_yield.py&play)
|
|
923
|
+
|
|
924
|
+
</td></tr>
|
|
925
|
+
<tr><td width="50%" valign="top">
|
|
926
|
+
|
|
927
|
+
```python
|
|
928
|
+
def fun():
|
|
929
|
+
return [
|
|
930
|
+
pr('create:', i)
|
|
931
|
+
for i in range(3)
|
|
932
|
+
]
|
|
933
|
+
|
|
934
|
+
for i in fun():
|
|
935
|
+
pr('use:', i)
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
Run in [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/eager_compri.py&play)
|
|
939
|
+
|
|
940
|
+
</td><td width="50%" valign="top">
|
|
941
|
+
|
|
942
|
+
```python
|
|
943
|
+
def fun():
|
|
944
|
+
return (
|
|
945
|
+
pr('create:', i)
|
|
946
|
+
for i in range(3)
|
|
947
|
+
)
|
|
948
|
+
|
|
949
|
+
for i in fun():
|
|
950
|
+
pr('use:', i)
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
Run in [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/lazy_express.py&play)
|
|
954
|
+
|
|
955
|
+
</td></tr>
|
|
956
|
+
<tr><td width="50%" valign="top">
|
|
957
|
+
|
|
958
|
+
```text
|
|
959
|
+
create: 0
|
|
960
|
+
create: 1
|
|
961
|
+
create: 2
|
|
962
|
+
use: 0
|
|
963
|
+
use: 1
|
|
964
|
+
use: 2
|
|
965
|
+
```
|
|
966
|
+
|
|
967
|
+
</td><td width="50%" valign="top">
|
|
968
|
+
|
|
969
|
+
```text
|
|
970
|
+
create: 0
|
|
971
|
+
use: 0
|
|
972
|
+
create: 1
|
|
973
|
+
use: 1
|
|
974
|
+
create: 2
|
|
975
|
+
use: 2
|
|
976
|
+
```
|
|
977
|
+
|
|
978
|
+
</td></tr>
|
|
979
|
+
</table>
|
|
980
|
+
|
|
981
|
+
|
|
877
982
|
# Configuration #
|
|
878
983
|
Different aspects of memory_graph can be configured. The default configuration can be reset by calling 'mg.config_default.reset()'. The Memory Graph Web Debugger gives examples of the [most important configurations](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/config.py&play).
|
|
879
984
|
|
|
@@ -14,7 +14,7 @@ Run a live demo in the 👉 [**Memory Graph Web Debugger**](https://memory-graph
|
|
|
14
14
|
- understand function calls, variable scope, and the **complete program state** through call stack visualization
|
|
15
15
|
|
|
16
16
|
An example Binary Tree data structure:
|
|
17
|
-

|
|
18
18
|
Or see it in the [Memory Grah Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/bin_tree.py×tep=0.2&play).
|
|
19
19
|
|
|
20
20
|
# Videos #
|
|
@@ -413,12 +413,12 @@ import memory_graph as mg
|
|
|
413
413
|
mg.config.type_to_horizontal[list] = True # horizontal lists
|
|
414
414
|
|
|
415
415
|
def binary(value: int) -> list[int]:
|
|
416
|
-
mg.block(mg.show
|
|
416
|
+
mg.block(mg.show, mg.stack())
|
|
417
417
|
if value == 0:
|
|
418
418
|
return []
|
|
419
419
|
quotient, remainder = divmod(value, 2)
|
|
420
420
|
result = binary(quotient) + [remainder]
|
|
421
|
-
mg.block(mg.show
|
|
421
|
+
mg.block(mg.show, mg.stack())
|
|
422
422
|
return result
|
|
423
423
|
|
|
424
424
|
print( binary(100) )
|
|
@@ -566,16 +566,16 @@ except Exception as e:
|
|
|
566
566
|
```
|
|
567
567
|
$ python exception_example.py
|
|
568
568
|
Traceback (most recent call last):
|
|
569
|
-
File "
|
|
569
|
+
File "exception_example.py", line 18, in <module>
|
|
570
570
|
raise e # raise to print traceback
|
|
571
571
|
^^^^^^^
|
|
572
|
-
File "
|
|
572
|
+
File "exception_example.py", line 15, in <module>
|
|
573
573
|
fun1()
|
|
574
|
-
File "
|
|
574
|
+
File "exception_example.py", line 12, in fun1
|
|
575
575
|
fun2()
|
|
576
|
-
File "
|
|
576
|
+
File "exception_example.py", line 9, in fun2
|
|
577
577
|
fun3()
|
|
578
|
-
File "
|
|
578
|
+
File "exception_example.py", line 6, in fun3
|
|
579
579
|
d[i] = i # throws IndexError when i = 3
|
|
580
580
|
~^^^
|
|
581
581
|
IndexError: list assignment index out of range
|
|
@@ -752,7 +752,6 @@ This example shows the flow of control when using inheritance with:
|
|
|
752
752
|
- class variable `message_count`
|
|
753
753
|
|
|
754
754
|
```python
|
|
755
|
-
from datetime import datetime
|
|
756
755
|
|
|
757
756
|
class Notification_Service:
|
|
758
757
|
message_count = 0
|
|
@@ -790,14 +789,14 @@ class SMS_Notification(Notification_Service):
|
|
|
790
789
|
email = Email_Notification(3, 'support@company.com')
|
|
791
790
|
email.send('customer@email.com', 'Your report is ready')
|
|
792
791
|
email.send('customer@email.com', 'Update to Privacy Policy')
|
|
793
|
-
sms =
|
|
792
|
+
sms = SMS_Notification(3, '0123456789')
|
|
794
793
|
sms.send('001122334455', 'Update to Privacy Policy')
|
|
795
794
|
```
|
|
796
|
-
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/inheritance.py&breakpoints=
|
|
795
|
+
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/inheritance.py&breakpoints=35&continues=1&play).
|
|
797
796
|
|
|
798
797
|
## Decorator ##
|
|
799
798
|
|
|
800
|
-
This example shows the flow of control when using a decorator.
|
|
799
|
+
This example shows the flow of control when using a decorator. A decorator wraps a function and is active before and after the function is called.
|
|
801
800
|
|
|
802
801
|
```python
|
|
803
802
|
def log_call(function):
|
|
@@ -831,29 +830,135 @@ This example shows the flow of control when using exception handling.
|
|
|
831
830
|
def fun2():
|
|
832
831
|
try:
|
|
833
832
|
d = [0] * 3
|
|
834
|
-
for i in range(
|
|
833
|
+
for i in range(6):
|
|
835
834
|
try:
|
|
836
|
-
|
|
837
|
-
|
|
835
|
+
print(f'{i=}')
|
|
836
|
+
d[i] = i # raises IndexError when i>=3
|
|
837
|
+
except ZeroDivisionError as e:
|
|
838
838
|
print(type(e), e)
|
|
839
839
|
except AssertionError as e:
|
|
840
840
|
print(type(e), e)
|
|
841
|
-
|
|
841
|
+
print('fun2() returns')
|
|
842
|
+
|
|
842
843
|
def fun1():
|
|
843
844
|
try:
|
|
844
845
|
return fun2()
|
|
845
846
|
except NameError as e:
|
|
846
847
|
print(type(e), e)
|
|
847
|
-
|
|
848
|
+
print('fun1() returns')
|
|
849
|
+
|
|
848
850
|
try:
|
|
849
851
|
fun1()
|
|
850
852
|
except LookupError as e:
|
|
851
853
|
print(type(e), e)
|
|
854
|
+
print('program ended cleanly')
|
|
852
855
|
```
|
|
853
|
-
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/exceptions.py&breakpoints=
|
|
856
|
+
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/exceptions.py&breakpoints=22&continues=1&play). In the program an `IndexError` exception is raised which propagates up the call stack until it reaches an except clause that matches its type where it is handled. Here, it is handled by the `LookupError` except clause because `IndexError` is a subclass of `LookupError`. Exceptions that are not handled terminate the execution of a program while its traceback is shown for analyses.
|
|
854
857
|
|
|
855
858
|

|
|
856
859
|
|
|
860
|
+
## Lazy Evaluation ##
|
|
861
|
+
|
|
862
|
+
In the following Eager and Lazy ealuation examples, we use this `pr()` function to print in what order things are created and used.
|
|
863
|
+
|
|
864
|
+
```python
|
|
865
|
+
def pr(tag, v):
|
|
866
|
+
print(tag, v)
|
|
867
|
+
return v
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
With eager evaluation, the function creates all three elements up front and stores them in a list before iteration begins. With lazy evaluation, the function returns a generator that creates each element only when it is needed.
|
|
871
|
+
|
|
872
|
+
<table>
|
|
873
|
+
<tr> <td width="50%" valign="top"><strong>Eager</strong></td> <td width="50%" valign="top"><strong>Lazy</strong></td></tr>
|
|
874
|
+
<tr><td width="50%" valign="top">
|
|
875
|
+
|
|
876
|
+
```python
|
|
877
|
+
def fun():
|
|
878
|
+
result = []
|
|
879
|
+
for i in range(3):
|
|
880
|
+
result.append(pr('create:', i))
|
|
881
|
+
return result
|
|
882
|
+
|
|
883
|
+
for i in fun():
|
|
884
|
+
pr('use:', i)
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
Run in [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/eager_return.py&play)
|
|
888
|
+
|
|
889
|
+
</td><td width="50%" valign="top">
|
|
890
|
+
|
|
891
|
+
```python
|
|
892
|
+
def fun():
|
|
893
|
+
for i in range(3):
|
|
894
|
+
yield pr('create:', i)
|
|
895
|
+
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
for i in fun():
|
|
899
|
+
pr('use:', i)
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
Run in [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/lazy_yield.py&play)
|
|
903
|
+
|
|
904
|
+
</td></tr>
|
|
905
|
+
<tr><td width="50%" valign="top">
|
|
906
|
+
|
|
907
|
+
```python
|
|
908
|
+
def fun():
|
|
909
|
+
return [
|
|
910
|
+
pr('create:', i)
|
|
911
|
+
for i in range(3)
|
|
912
|
+
]
|
|
913
|
+
|
|
914
|
+
for i in fun():
|
|
915
|
+
pr('use:', i)
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
Run in [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/eager_compri.py&play)
|
|
919
|
+
|
|
920
|
+
</td><td width="50%" valign="top">
|
|
921
|
+
|
|
922
|
+
```python
|
|
923
|
+
def fun():
|
|
924
|
+
return (
|
|
925
|
+
pr('create:', i)
|
|
926
|
+
for i in range(3)
|
|
927
|
+
)
|
|
928
|
+
|
|
929
|
+
for i in fun():
|
|
930
|
+
pr('use:', i)
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
Run in [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/lazy_express.py&play)
|
|
934
|
+
|
|
935
|
+
</td></tr>
|
|
936
|
+
<tr><td width="50%" valign="top">
|
|
937
|
+
|
|
938
|
+
```text
|
|
939
|
+
create: 0
|
|
940
|
+
create: 1
|
|
941
|
+
create: 2
|
|
942
|
+
use: 0
|
|
943
|
+
use: 1
|
|
944
|
+
use: 2
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
</td><td width="50%" valign="top">
|
|
948
|
+
|
|
949
|
+
```text
|
|
950
|
+
create: 0
|
|
951
|
+
use: 0
|
|
952
|
+
create: 1
|
|
953
|
+
use: 1
|
|
954
|
+
create: 2
|
|
955
|
+
use: 2
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
</td></tr>
|
|
959
|
+
</table>
|
|
960
|
+
|
|
961
|
+
|
|
857
962
|
# Configuration #
|
|
858
963
|
Different aspects of memory_graph can be configured. The default configuration can be reset by calling 'mg.config_default.reset()'. The Memory Graph Web Debugger gives examples of the [most important configurations](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/config.py&play).
|
|
859
964
|
|
|
@@ -27,13 +27,6 @@ no_index_types = {}
|
|
|
27
27
|
|
|
28
28
|
type_to_string = { }
|
|
29
29
|
|
|
30
|
-
def to_string(data):
|
|
31
|
-
""" Convert data to string. """
|
|
32
|
-
data_type = type(data)
|
|
33
|
-
if data_type in type_to_string:
|
|
34
|
-
return type_to_string[data_type](data)
|
|
35
|
-
return str(data)
|
|
36
|
-
|
|
37
30
|
type_to_node = { }
|
|
38
31
|
|
|
39
32
|
type_to_color = { }
|
|
@@ -60,11 +60,12 @@ def reset():
|
|
|
60
60
|
|
|
61
61
|
""" Types that need a special conversion """
|
|
62
62
|
config.type_to_string = {
|
|
63
|
-
types.FunctionType: lambda data: data.__qualname__,
|
|
64
|
-
types.MethodType: lambda data: data.__qualname__,
|
|
65
|
-
classmethod: lambda data: data.__qualname__,
|
|
66
|
-
staticmethod: lambda data: data.__qualname__,
|
|
67
|
-
type(len): lambda data: data.__qualname__,
|
|
63
|
+
types.FunctionType: lambda data: utils.limit_string(data.__qualname__),
|
|
64
|
+
types.MethodType: lambda data: utils.limit_string(data.__qualname__),
|
|
65
|
+
classmethod: lambda data: utils.limit_string(data.__qualname__),
|
|
66
|
+
staticmethod: lambda data: utils.limit_string(data.__qualname__),
|
|
67
|
+
type(len): lambda data: utils.limit_string(data.__qualname__),
|
|
68
|
+
BaseException: lambda data: utils.exception_to_string(data),
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
""" Conversion from type to Node objects. """
|
|
@@ -78,6 +79,7 @@ def reset():
|
|
|
78
79
|
if dict in config.embedding_types else
|
|
79
80
|
Node_Linear(data, utils.filter_dict(data) )
|
|
80
81
|
),
|
|
82
|
+
BaseException: lambda data: Node_Leaf(data, data),
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
""" Colors of different types in the graph. """
|
|
@@ -103,6 +105,8 @@ def reset():
|
|
|
103
105
|
dict : "#60a5ff",
|
|
104
106
|
types.MappingProxyType : "dodgerblue2", # not used
|
|
105
107
|
range : "cornsilk2",
|
|
108
|
+
# ================= exception
|
|
109
|
+
BaseException : "#ff5555",
|
|
106
110
|
}
|
|
107
111
|
|
|
108
112
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# This file is part of memory_graph.
|
|
2
|
+
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
|
|
5
|
+
""" This module provides helper functions to access the configuration of the memory graph. """
|
|
6
|
+
import memory_graph.utils as utils
|
|
7
|
+
from memory_graph.slicer import Slicer
|
|
8
|
+
import memory_graph.config as config
|
|
9
|
+
import memory_graph.utils as utils
|
|
10
|
+
|
|
11
|
+
def get_property(data_id, data_types, node_type, dictionary, default):
|
|
12
|
+
if data_id in dictionary:
|
|
13
|
+
return dictionary[data_id]
|
|
14
|
+
for data_type in data_types:
|
|
15
|
+
if data_type in dictionary:
|
|
16
|
+
return dictionary[data_type]
|
|
17
|
+
if node_type in dictionary:
|
|
18
|
+
return dictionary[node_type]
|
|
19
|
+
return default
|
|
20
|
+
|
|
21
|
+
def get_data_to_node(data, default=None):
|
|
22
|
+
return get_property(id(data),
|
|
23
|
+
utils.get_all_types(data),
|
|
24
|
+
None,
|
|
25
|
+
config.type_to_node,
|
|
26
|
+
default )
|
|
27
|
+
|
|
28
|
+
def default_to_string(data):
|
|
29
|
+
""" Convert data to string. """
|
|
30
|
+
try:
|
|
31
|
+
if isinstance(data, str):
|
|
32
|
+
s = data
|
|
33
|
+
else:
|
|
34
|
+
s = str(data)
|
|
35
|
+
return utils.limit_string(s)
|
|
36
|
+
except Exception as e:
|
|
37
|
+
return f'no stringification, {type(e).__name__}: {e}'
|
|
38
|
+
|
|
39
|
+
def get_to_string(data, default=lambda s: default_to_string(s)):
|
|
40
|
+
return get_property(id(data),
|
|
41
|
+
utils.get_all_types(data),
|
|
42
|
+
None,
|
|
43
|
+
config.type_to_string,
|
|
44
|
+
default )
|
|
45
|
+
|
|
46
|
+
def get_node_color(node, default='white'):
|
|
47
|
+
return get_property(node.get_id(),
|
|
48
|
+
utils.get_all_types(node.get_data()),
|
|
49
|
+
type(node),
|
|
50
|
+
config.type_to_color,
|
|
51
|
+
default)
|
|
52
|
+
|
|
53
|
+
def get_node_vertical(node, default):
|
|
54
|
+
horizontal = get_property(node.get_id(),
|
|
55
|
+
utils.get_all_types(node.get_data()),
|
|
56
|
+
type(node),
|
|
57
|
+
config.type_to_horizontal,
|
|
58
|
+
None)
|
|
59
|
+
if isinstance(horizontal, bool):
|
|
60
|
+
return not horizontal
|
|
61
|
+
return get_property(node.get_id(),
|
|
62
|
+
utils.get_all_types(node.get_data()),
|
|
63
|
+
type(node),
|
|
64
|
+
config.type_to_vertical,
|
|
65
|
+
default)
|
|
66
|
+
|
|
67
|
+
def get_node_slicer(node, data, default=Slicer(3,2,3)):
|
|
68
|
+
return get_property(id(data),
|
|
69
|
+
utils.get_all_types(node.get_data()),
|
|
70
|
+
type(node),
|
|
71
|
+
config.type_to_slicer,
|
|
72
|
+
default)
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
from memory_graph.node_base import Node_Base
|
|
6
6
|
import memory_graph.node_base
|
|
7
7
|
import memory_graph.config as config
|
|
8
|
+
import memory_graph.config_helpers as config_helpers
|
|
8
9
|
import html
|
|
9
10
|
|
|
10
11
|
def html_table_frame(s, border, color, spacing=5):
|
|
@@ -12,11 +13,11 @@ def html_table_frame(s, border, color, spacing=5):
|
|
|
12
13
|
return (f'<\n<TABLE BORDER="{border}" CELLBORDER="1" CELLSPACING="{spacing}" CELLPADDING="0" BGCOLOR="{color}" PORT="table">\n <TR>' +
|
|
13
14
|
s + '</TR>\n</TABLE>\n>')
|
|
14
15
|
|
|
15
|
-
def format_string(
|
|
16
|
-
""" Helper function to format
|
|
17
|
-
|
|
18
|
-
s =
|
|
19
|
-
return
|
|
16
|
+
def format_string(value):
|
|
17
|
+
""" Helper function to format 'value' to be shown in the graph. We escape html characters and convert newlines to <BR/> tags. """
|
|
18
|
+
to_string = config_helpers.get_to_string(value)
|
|
19
|
+
s = html.escape(to_string(value))
|
|
20
|
+
return s.replace('\n', ' <BR/> ')
|
|
20
21
|
|
|
21
22
|
class HTML_Table:
|
|
22
23
|
"""
|
|
@@ -54,11 +55,6 @@ class HTML_Table:
|
|
|
54
55
|
self.html += '</TR>\n <TR>'
|
|
55
56
|
self.add_new_line_flag = False
|
|
56
57
|
|
|
57
|
-
def add_string(self, s, border=0):
|
|
58
|
-
""" Add a string s to the table. """
|
|
59
|
-
self.html += f'<TD BORDER="{border}">'+format_string(s)+'</TD>'
|
|
60
|
-
self.is_empty = False
|
|
61
|
-
|
|
62
58
|
def add_index(self, s):
|
|
63
59
|
""" Add an index s to the table. """
|
|
64
60
|
self.check_add_new_line()
|
|
@@ -67,7 +63,6 @@ class HTML_Table:
|
|
|
67
63
|
|
|
68
64
|
def add_entry(self, node, nodes, child, id_to_slices, rounded=False, border=1, dashed=False, embed=False):
|
|
69
65
|
""" Add child to the table either as reference if it is a Node_Base or as a value otherwise. """
|
|
70
|
-
#print('child:', child)
|
|
71
66
|
child_id = id(child)
|
|
72
67
|
if not embed and child_id in nodes:
|
|
73
68
|
child = nodes[child_id]
|
|
@@ -78,11 +73,12 @@ class HTML_Table:
|
|
|
78
73
|
else:
|
|
79
74
|
self.add_value(child, rounded, border)
|
|
80
75
|
|
|
81
|
-
def add_value(self,
|
|
82
|
-
""" Helper function to add
|
|
76
|
+
def add_value(self, value, rounded=False, border=1):
|
|
77
|
+
""" Helper function to add 'value' to the table. """
|
|
83
78
|
self.check_add_new_line()
|
|
84
79
|
r = ' STYLE="ROUNDED"' if rounded else ''
|
|
85
|
-
self.html += f'<TD BORDER="{border}"{r}> {format_string(
|
|
80
|
+
self.html += f'<TD BORDER="{border}"{r}> {format_string(value)} </TD>'
|
|
81
|
+
self.is_empty = False
|
|
86
82
|
self.col_count += 1
|
|
87
83
|
|
|
88
84
|
def add_reference(self, node, child, rounded=False, border=1, dashed=False):
|
|
@@ -106,7 +102,7 @@ class HTML_Table:
|
|
|
106
102
|
""" Construct the HTML table string with the 'border' and 'color' settings. """
|
|
107
103
|
if self.col_count == 0 and self.row_count == 0:
|
|
108
104
|
if self.is_empty:
|
|
109
|
-
self.
|
|
105
|
+
self.add_value('', border=0)
|
|
110
106
|
return html_table_frame(self.html, border, color, spacing=0)
|
|
111
107
|
return html_table_frame(self.html, border, color)
|
|
112
108
|
|
|
@@ -20,15 +20,11 @@ def read_nodes(data):
|
|
|
20
20
|
- the id of 'data' as root node.
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
-
def data_to_node(
|
|
23
|
+
def data_to_node(data):
|
|
24
24
|
""" Returns the Node for 'data' based on it's id or type. """
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return config.type_to_node[data_type](data)
|
|
29
|
-
elif isinstance(data, type) and type in config.type_to_node:
|
|
30
|
-
# Handle classes with custom metaclasses (for example abc.ABCMeta).
|
|
31
|
-
return config.type_to_node[type](data)
|
|
25
|
+
to_node = config_helpers.get_data_to_node(data)
|
|
26
|
+
if (to_node):
|
|
27
|
+
return to_node(data)
|
|
32
28
|
elif utils.has_dict_attributes(data): # for user defined classes
|
|
33
29
|
return Node_Key_Value(data, utils.filter_dict(utils.get_dict_attributes(data)) )
|
|
34
30
|
elif utils.is_finite_iterable(data): # for lists, tuples, sets, ...
|
|
@@ -46,7 +42,7 @@ def read_nodes(data):
|
|
|
46
42
|
if data_id in nodes:
|
|
47
43
|
node = nodes[data_id]
|
|
48
44
|
else:
|
|
49
|
-
node = data_to_node(
|
|
45
|
+
node = data_to_node(data)
|
|
50
46
|
if isinstance(node, Node_Key_Value):
|
|
51
47
|
nodes_key_value.append(data_id)
|
|
52
48
|
nodes[data_id] = node
|
|
@@ -219,7 +215,7 @@ def build_graph(graphviz_graph, nodes, root_id, id_to_slices):
|
|
|
219
215
|
""" Adds 'node' to 'graphviz_graph' with its children and edges. """
|
|
220
216
|
html_table = node.get_html_table(nodes, slices, id_to_slices)
|
|
221
217
|
edges = html_table.get_edges()
|
|
222
|
-
color = config_helpers.
|
|
218
|
+
color = config_helpers.get_node_color(node)
|
|
223
219
|
border = 3 if node.is_root() else 1
|
|
224
220
|
if config.type_labels:
|
|
225
221
|
graphviz_graph.node(node.get_name(),
|
|
@@ -46,7 +46,7 @@ class Node_Key_Value(Node_Base):
|
|
|
46
46
|
"""
|
|
47
47
|
Return if the node is vertical or horizontal based on the orientation of the children.
|
|
48
48
|
"""
|
|
49
|
-
vertical = config_helpers.
|
|
49
|
+
vertical = config_helpers.get_node_vertical(self, None)
|
|
50
50
|
if vertical is None:
|
|
51
51
|
vertical = not self.has_references(nodes, slices, id_to_slices)
|
|
52
52
|
return vertical
|
|
@@ -37,7 +37,7 @@ class Node_Linear(Node_Base):
|
|
|
37
37
|
"""
|
|
38
38
|
Return if the node is vertical or horizontal based on the orientation of the children.
|
|
39
39
|
"""
|
|
40
|
-
vertical = config_helpers.
|
|
40
|
+
vertical = config_helpers.get_node_vertical(self, None)
|
|
41
41
|
if vertical is None:
|
|
42
42
|
vertical = not self.has_references(nodes, slices)
|
|
43
43
|
return vertical
|
|
@@ -5,6 +5,42 @@
|
|
|
5
5
|
import math
|
|
6
6
|
import types
|
|
7
7
|
import functools
|
|
8
|
+
import traceback
|
|
9
|
+
import re
|
|
10
|
+
|
|
11
|
+
import memory_graph.config as config
|
|
12
|
+
|
|
13
|
+
def limit_string(s):
|
|
14
|
+
""" Helper function to limit the length of a string s to the 'max_string_length' in the config. """
|
|
15
|
+
if len(s) > config.max_string_length:
|
|
16
|
+
return s[:config.max_string_length] + '...'
|
|
17
|
+
return s
|
|
18
|
+
|
|
19
|
+
def exception_to_string(e):
|
|
20
|
+
""" Helper function to convert the traceback of an exception to a string. """
|
|
21
|
+
return ''.join(traceback.format_exception(type(e), e, e.__traceback__)).strip()
|
|
22
|
+
|
|
23
|
+
def exception_to_string_no_path(e):
|
|
24
|
+
""" Helper function to convert the traceback of an exception to a string without file paths. """
|
|
25
|
+
s = exception_to_string(e)
|
|
26
|
+
# Convert traceback file paths like File "/a/b/c.py" to File "c.py".
|
|
27
|
+
return re.sub(
|
|
28
|
+
r'(^\s*File ")(.*?)(")',
|
|
29
|
+
lambda m: f'{m.group(1)}{m.group(2).replace("\\", "/").rsplit("/", 1)[-1]}{m.group(3)}',
|
|
30
|
+
s,
|
|
31
|
+
flags=re.MULTILINE,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def exception_to_string_short(e):
|
|
35
|
+
""" Helper function to convert an exception to a short string. """
|
|
36
|
+
return f'{type(e).__name__}: {e}'
|
|
37
|
+
|
|
38
|
+
def get_all_types(obj):
|
|
39
|
+
cls = type(obj)
|
|
40
|
+
if hasattr(cls, '__mro__'):
|
|
41
|
+
return cls.__mro__
|
|
42
|
+
else:
|
|
43
|
+
return [cls]
|
|
8
44
|
|
|
9
45
|
def has_dict_attributes(value):
|
|
10
46
|
""" Returns 'True' if 'value' has a '__dict__' attribute. """
|
|
@@ -15,6 +51,7 @@ def get_dict_attributes(value):
|
|
|
15
51
|
return getattr(value,"__dict__")
|
|
16
52
|
|
|
17
53
|
def is_not_state(obj):
|
|
54
|
+
""" Returns 'True' if 'obj' is not considered state, e.g. a function or method. """
|
|
18
55
|
if isinstance(obj, (types.FunctionType, types.MethodType,
|
|
19
56
|
types.BuiltinFunctionType, types.BuiltinMethodType,
|
|
20
57
|
classmethod, staticmethod,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memory_graph
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.75
|
|
4
4
|
Summary: Teaching tool and debugging aid in context of references, mutable data types, and shallow and deep copy.
|
|
5
5
|
Author-email: Bas Terwijn <bterwijn@gmail.com>
|
|
6
6
|
License-Expression: BSD-2-Clause
|
|
@@ -34,7 +34,7 @@ Run a live demo in the 👉 [**Memory Graph Web Debugger**](https://memory-graph
|
|
|
34
34
|
- understand function calls, variable scope, and the **complete program state** through call stack visualization
|
|
35
35
|
|
|
36
36
|
An example Binary Tree data structure:
|
|
37
|
-

|
|
38
38
|
Or see it in the [Memory Grah Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/bin_tree.py×tep=0.2&play).
|
|
39
39
|
|
|
40
40
|
# Videos #
|
|
@@ -433,12 +433,12 @@ import memory_graph as mg
|
|
|
433
433
|
mg.config.type_to_horizontal[list] = True # horizontal lists
|
|
434
434
|
|
|
435
435
|
def binary(value: int) -> list[int]:
|
|
436
|
-
mg.block(mg.show
|
|
436
|
+
mg.block(mg.show, mg.stack())
|
|
437
437
|
if value == 0:
|
|
438
438
|
return []
|
|
439
439
|
quotient, remainder = divmod(value, 2)
|
|
440
440
|
result = binary(quotient) + [remainder]
|
|
441
|
-
mg.block(mg.show
|
|
441
|
+
mg.block(mg.show, mg.stack())
|
|
442
442
|
return result
|
|
443
443
|
|
|
444
444
|
print( binary(100) )
|
|
@@ -586,16 +586,16 @@ except Exception as e:
|
|
|
586
586
|
```
|
|
587
587
|
$ python exception_example.py
|
|
588
588
|
Traceback (most recent call last):
|
|
589
|
-
File "
|
|
589
|
+
File "exception_example.py", line 18, in <module>
|
|
590
590
|
raise e # raise to print traceback
|
|
591
591
|
^^^^^^^
|
|
592
|
-
File "
|
|
592
|
+
File "exception_example.py", line 15, in <module>
|
|
593
593
|
fun1()
|
|
594
|
-
File "
|
|
594
|
+
File "exception_example.py", line 12, in fun1
|
|
595
595
|
fun2()
|
|
596
|
-
File "
|
|
596
|
+
File "exception_example.py", line 9, in fun2
|
|
597
597
|
fun3()
|
|
598
|
-
File "
|
|
598
|
+
File "exception_example.py", line 6, in fun3
|
|
599
599
|
d[i] = i # throws IndexError when i = 3
|
|
600
600
|
~^^^
|
|
601
601
|
IndexError: list assignment index out of range
|
|
@@ -772,7 +772,6 @@ This example shows the flow of control when using inheritance with:
|
|
|
772
772
|
- class variable `message_count`
|
|
773
773
|
|
|
774
774
|
```python
|
|
775
|
-
from datetime import datetime
|
|
776
775
|
|
|
777
776
|
class Notification_Service:
|
|
778
777
|
message_count = 0
|
|
@@ -810,14 +809,14 @@ class SMS_Notification(Notification_Service):
|
|
|
810
809
|
email = Email_Notification(3, 'support@company.com')
|
|
811
810
|
email.send('customer@email.com', 'Your report is ready')
|
|
812
811
|
email.send('customer@email.com', 'Update to Privacy Policy')
|
|
813
|
-
sms =
|
|
812
|
+
sms = SMS_Notification(3, '0123456789')
|
|
814
813
|
sms.send('001122334455', 'Update to Privacy Policy')
|
|
815
814
|
```
|
|
816
|
-
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/inheritance.py&breakpoints=
|
|
815
|
+
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/inheritance.py&breakpoints=35&continues=1&play).
|
|
817
816
|
|
|
818
817
|
## Decorator ##
|
|
819
818
|
|
|
820
|
-
This example shows the flow of control when using a decorator.
|
|
819
|
+
This example shows the flow of control when using a decorator. A decorator wraps a function and is active before and after the function is called.
|
|
821
820
|
|
|
822
821
|
```python
|
|
823
822
|
def log_call(function):
|
|
@@ -851,29 +850,135 @@ This example shows the flow of control when using exception handling.
|
|
|
851
850
|
def fun2():
|
|
852
851
|
try:
|
|
853
852
|
d = [0] * 3
|
|
854
|
-
for i in range(
|
|
853
|
+
for i in range(6):
|
|
855
854
|
try:
|
|
856
|
-
|
|
857
|
-
|
|
855
|
+
print(f'{i=}')
|
|
856
|
+
d[i] = i # raises IndexError when i>=3
|
|
857
|
+
except ZeroDivisionError as e:
|
|
858
858
|
print(type(e), e)
|
|
859
859
|
except AssertionError as e:
|
|
860
860
|
print(type(e), e)
|
|
861
|
-
|
|
861
|
+
print('fun2() returns')
|
|
862
|
+
|
|
862
863
|
def fun1():
|
|
863
864
|
try:
|
|
864
865
|
return fun2()
|
|
865
866
|
except NameError as e:
|
|
866
867
|
print(type(e), e)
|
|
867
|
-
|
|
868
|
+
print('fun1() returns')
|
|
869
|
+
|
|
868
870
|
try:
|
|
869
871
|
fun1()
|
|
870
872
|
except LookupError as e:
|
|
871
873
|
print(type(e), e)
|
|
874
|
+
print('program ended cleanly')
|
|
872
875
|
```
|
|
873
|
-
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/exceptions.py&breakpoints=
|
|
876
|
+
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/exceptions.py&breakpoints=22&continues=1&play). In the program an `IndexError` exception is raised which propagates up the call stack until it reaches an except clause that matches its type where it is handled. Here, it is handled by the `LookupError` except clause because `IndexError` is a subclass of `LookupError`. Exceptions that are not handled terminate the execution of a program while its traceback is shown for analyses.
|
|
874
877
|
|
|
875
878
|

|
|
876
879
|
|
|
880
|
+
## Lazy Evaluation ##
|
|
881
|
+
|
|
882
|
+
In the following Eager and Lazy ealuation examples, we use this `pr()` function to print in what order things are created and used.
|
|
883
|
+
|
|
884
|
+
```python
|
|
885
|
+
def pr(tag, v):
|
|
886
|
+
print(tag, v)
|
|
887
|
+
return v
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
With eager evaluation, the function creates all three elements up front and stores them in a list before iteration begins. With lazy evaluation, the function returns a generator that creates each element only when it is needed.
|
|
891
|
+
|
|
892
|
+
<table>
|
|
893
|
+
<tr> <td width="50%" valign="top"><strong>Eager</strong></td> <td width="50%" valign="top"><strong>Lazy</strong></td></tr>
|
|
894
|
+
<tr><td width="50%" valign="top">
|
|
895
|
+
|
|
896
|
+
```python
|
|
897
|
+
def fun():
|
|
898
|
+
result = []
|
|
899
|
+
for i in range(3):
|
|
900
|
+
result.append(pr('create:', i))
|
|
901
|
+
return result
|
|
902
|
+
|
|
903
|
+
for i in fun():
|
|
904
|
+
pr('use:', i)
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
Run in [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/eager_return.py&play)
|
|
908
|
+
|
|
909
|
+
</td><td width="50%" valign="top">
|
|
910
|
+
|
|
911
|
+
```python
|
|
912
|
+
def fun():
|
|
913
|
+
for i in range(3):
|
|
914
|
+
yield pr('create:', i)
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
|
|
918
|
+
for i in fun():
|
|
919
|
+
pr('use:', i)
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
Run in [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/lazy_yield.py&play)
|
|
923
|
+
|
|
924
|
+
</td></tr>
|
|
925
|
+
<tr><td width="50%" valign="top">
|
|
926
|
+
|
|
927
|
+
```python
|
|
928
|
+
def fun():
|
|
929
|
+
return [
|
|
930
|
+
pr('create:', i)
|
|
931
|
+
for i in range(3)
|
|
932
|
+
]
|
|
933
|
+
|
|
934
|
+
for i in fun():
|
|
935
|
+
pr('use:', i)
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
Run in [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/eager_compri.py&play)
|
|
939
|
+
|
|
940
|
+
</td><td width="50%" valign="top">
|
|
941
|
+
|
|
942
|
+
```python
|
|
943
|
+
def fun():
|
|
944
|
+
return (
|
|
945
|
+
pr('create:', i)
|
|
946
|
+
for i in range(3)
|
|
947
|
+
)
|
|
948
|
+
|
|
949
|
+
for i in fun():
|
|
950
|
+
pr('use:', i)
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
Run in [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/lazy_express.py&play)
|
|
954
|
+
|
|
955
|
+
</td></tr>
|
|
956
|
+
<tr><td width="50%" valign="top">
|
|
957
|
+
|
|
958
|
+
```text
|
|
959
|
+
create: 0
|
|
960
|
+
create: 1
|
|
961
|
+
create: 2
|
|
962
|
+
use: 0
|
|
963
|
+
use: 1
|
|
964
|
+
use: 2
|
|
965
|
+
```
|
|
966
|
+
|
|
967
|
+
</td><td width="50%" valign="top">
|
|
968
|
+
|
|
969
|
+
```text
|
|
970
|
+
create: 0
|
|
971
|
+
use: 0
|
|
972
|
+
create: 1
|
|
973
|
+
use: 1
|
|
974
|
+
create: 2
|
|
975
|
+
use: 2
|
|
976
|
+
```
|
|
977
|
+
|
|
978
|
+
</td></tr>
|
|
979
|
+
</table>
|
|
980
|
+
|
|
981
|
+
|
|
877
982
|
# Configuration #
|
|
878
983
|
Different aspects of memory_graph can be configured. The default configuration can be reset by calling 'mg.config_default.reset()'. The Memory Graph Web Debugger gives examples of the [most important configurations](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/config.py&play).
|
|
879
984
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "memory_graph"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.75"
|
|
8
8
|
description = "Teaching tool and debugging aid in context of references, mutable data types, and shallow and deep copy."
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "Bas Terwijn", email = "bterwijn@gmail.com"}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# This file is part of memory_graph.
|
|
2
|
-
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
-
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
-
|
|
5
|
-
""" This module provides helper functions to access the configuration of the memory graph. """
|
|
6
|
-
from memory_graph.slicer import Slicer
|
|
7
|
-
import memory_graph.config as config
|
|
8
|
-
|
|
9
|
-
def get_property(data_id, data_type, node_type, dictionary, default):
|
|
10
|
-
if data_id in dictionary:
|
|
11
|
-
return dictionary[data_id]
|
|
12
|
-
if data_type in dictionary:
|
|
13
|
-
return dictionary[data_type]
|
|
14
|
-
if node_type in dictionary:
|
|
15
|
-
return dictionary[node_type]
|
|
16
|
-
return default
|
|
17
|
-
|
|
18
|
-
def get_color(node, default='white'):
|
|
19
|
-
return get_property(node.get_id(),
|
|
20
|
-
node.get_type(),
|
|
21
|
-
type(node),
|
|
22
|
-
config.type_to_color,
|
|
23
|
-
default)
|
|
24
|
-
|
|
25
|
-
def get_vertical(node, default):
|
|
26
|
-
horizontal = get_property(node.get_id(),
|
|
27
|
-
node.get_type(),
|
|
28
|
-
type(node),
|
|
29
|
-
config.type_to_horizontal,
|
|
30
|
-
None)
|
|
31
|
-
if isinstance(horizontal, bool):
|
|
32
|
-
return not horizontal
|
|
33
|
-
return get_property(node.get_id(),
|
|
34
|
-
node.get_type(),
|
|
35
|
-
type(node),
|
|
36
|
-
config.type_to_vertical,
|
|
37
|
-
default)
|
|
38
|
-
|
|
39
|
-
def get_slicer(node, data, default=Slicer(3,2,3)):
|
|
40
|
-
return get_property(id(data),
|
|
41
|
-
type(data),
|
|
42
|
-
type(node),
|
|
43
|
-
config.type_to_slicer,
|
|
44
|
-
default)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|