memory-graph 0.3.70__tar.gz → 0.3.72__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.70 → memory_graph-0.3.72}/PKG-INFO +122 -6
- {memory_graph-0.3.70 → memory_graph-0.3.72}/README.md +121 -5
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/__init__.py +1 -1
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/memory_to_nodes.py +9 -4
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph.egg-info/PKG-INFO +122 -6
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph.egg-info/SOURCES.txt +1 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/pyproject.toml +1 -1
- memory_graph-0.3.72/setup.py +7 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/LICENSE.txt +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/call_stack.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/config.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/config_default.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/config_helpers.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/extension_numpy.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/extension_pandas.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/extension_torch.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/html_table.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/list_view.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/node_base.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/node_key_value.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/node_leaf.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/node_linear.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/node_table.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/sequence.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/slicer.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/slices.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/slices_iterator.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/slices_table_iterator.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/test.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/test_max_graph_depth.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/test_memory_graph.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/test_memory_to_nodes.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/test_sequence.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/test_slicer.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/test_slices.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/test_slices_iterator.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/utils.py +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph.egg-info/dependency_links.txt +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph.egg-info/requires.txt +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph.egg-info/top_level.txt +0 -0
- {memory_graph-0.3.70 → memory_graph-0.3.72}/setup.cfg +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.72
|
|
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
|
|
@@ -131,6 +131,8 @@ A better way to understand what values are shared is to draw a graph using [memo
|
|
|
131
131
|
|
|
132
132
|
[Sliding Puzzle Solver](#sliding-puzzle-solver)
|
|
133
133
|
|
|
134
|
+
[Control Flow](#control-flow)
|
|
135
|
+
|
|
134
136
|
[Configuration](#configuration)
|
|
135
137
|
|
|
136
138
|
[Introspection](#introspection)
|
|
@@ -751,7 +753,6 @@ In this configuration example we show the decimal, binary and [two's complement
|
|
|
751
753
|
|
|
752
754
|

|
|
753
755
|
|
|
754
|
-
|
|
755
756
|
# Sliding Puzzle Solver #
|
|
756
757
|
|
|
757
758
|
A sliding puzzle solver as a challenging example showing how memory_graph deals with large amounts of data. Click "Continue" to step through the breadth-first search generations until a solution path is found:
|
|
@@ -760,6 +761,118 @@ A sliding puzzle solver as a challenging example showing how memory_graph deals
|
|
|
760
761
|
|
|
761
762
|

|
|
762
763
|
|
|
764
|
+
# Control Flow #
|
|
765
|
+
|
|
766
|
+
Some examples where we focus on the flow of control of Python code.
|
|
767
|
+
|
|
768
|
+
## Inheritance ##
|
|
769
|
+
|
|
770
|
+
This example shows the flow of control when using inheritance with:
|
|
771
|
+
- `super()`
|
|
772
|
+
- class variable `message_count`
|
|
773
|
+
|
|
774
|
+
```python
|
|
775
|
+
from datetime import datetime
|
|
776
|
+
|
|
777
|
+
class Notification_Service:
|
|
778
|
+
message_count = 0
|
|
779
|
+
|
|
780
|
+
def __init__(self, priority):
|
|
781
|
+
self.priority = priority
|
|
782
|
+
self.log = []
|
|
783
|
+
|
|
784
|
+
def send(self, sender, receiver, message):
|
|
785
|
+
self.log.append((type(self).__name__, self.priority, sender, receiver, message))
|
|
786
|
+
Notification_Service.message_count += 1
|
|
787
|
+
|
|
788
|
+
class Email_Notification(Notification_Service):
|
|
789
|
+
|
|
790
|
+
def __init__(self, priority, from_email):
|
|
791
|
+
super().__init__(priority)
|
|
792
|
+
self.from_email = from_email
|
|
793
|
+
|
|
794
|
+
def send(self, to_email, message):
|
|
795
|
+
super().send(self.from_email, to_email, message)
|
|
796
|
+
print(f'sending Email from:{self.from_email} to:{to_email}')
|
|
797
|
+
print(f'priority: {self.priority} message: "{message}"')
|
|
798
|
+
|
|
799
|
+
class SMS_Notification(Notification_Service):
|
|
800
|
+
|
|
801
|
+
def __init__(self, priority, from_nr):
|
|
802
|
+
super().__init__(priority)
|
|
803
|
+
self.from_nr = from_nr
|
|
804
|
+
|
|
805
|
+
def send(self, to_nr, message):
|
|
806
|
+
super().send(self.from_nr, to_nr, message)
|
|
807
|
+
print(f'sending SMS from:{self.from_nr} to:{to_nr}')
|
|
808
|
+
print(f'priority: {self.priority} message: "{message}"')
|
|
809
|
+
|
|
810
|
+
email = Email_Notification(3, 'support@company.com')
|
|
811
|
+
email.send('customer@email.com', 'Your report is ready')
|
|
812
|
+
email.send('customer@email.com', 'Update to Privacy Policy')
|
|
813
|
+
sms = Email_Notification(3, '0123456789')
|
|
814
|
+
sms.send('001122334455', 'Update to Privacy Policy')
|
|
815
|
+
```
|
|
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=36&continues=1&play).
|
|
817
|
+
|
|
818
|
+
## Decorator ##
|
|
819
|
+
|
|
820
|
+
This example shows the flow of control when using a decorator.
|
|
821
|
+
|
|
822
|
+
```python
|
|
823
|
+
def log_call(function):
|
|
824
|
+
def wrapper(*args, **kwargs):
|
|
825
|
+
print(f"Calling {function.__name__} with: {args}, {kwargs}")
|
|
826
|
+
returned = function(*args, **kwargs)
|
|
827
|
+
print(f"Finished {function.__name__} with return value: {returned}")
|
|
828
|
+
return returned
|
|
829
|
+
return wrapper
|
|
830
|
+
|
|
831
|
+
@log_call
|
|
832
|
+
def calculate_total(price, quantity, rounding=False):
|
|
833
|
+
result = price * quantity
|
|
834
|
+
return round(result) if rounding else result
|
|
835
|
+
|
|
836
|
+
@log_call
|
|
837
|
+
def send_email(receiver, message, sender="support@company.com"):
|
|
838
|
+
print(f"Sending email to:{receiver} from:{sender}, {message}")
|
|
839
|
+
|
|
840
|
+
total = calculate_total(7.5, 3, rounding=True)
|
|
841
|
+
print(total)
|
|
842
|
+
send_email("alice@example.com", "Your order is ready")
|
|
843
|
+
```
|
|
844
|
+
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/decorator.py&breakpoints=18&continues=1&play).
|
|
845
|
+
|
|
846
|
+
## Exception Handling ##
|
|
847
|
+
|
|
848
|
+
This example shows the flow of control when using exception handling.
|
|
849
|
+
|
|
850
|
+
```python
|
|
851
|
+
def fun2():
|
|
852
|
+
try:
|
|
853
|
+
d = [0] * 3
|
|
854
|
+
for i in range(4):
|
|
855
|
+
try:
|
|
856
|
+
d[i] = i # raises IndexError when i=3
|
|
857
|
+
except TypeError as e:
|
|
858
|
+
print(type(e), e)
|
|
859
|
+
except AssertionError as e:
|
|
860
|
+
print(type(e), e)
|
|
861
|
+
|
|
862
|
+
def fun1():
|
|
863
|
+
try:
|
|
864
|
+
return fun2()
|
|
865
|
+
except NameError as e:
|
|
866
|
+
print(type(e), e)
|
|
867
|
+
|
|
868
|
+
try:
|
|
869
|
+
fun1()
|
|
870
|
+
except LookupError as e:
|
|
871
|
+
print(type(e), e)
|
|
872
|
+
```
|
|
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=19&continues=1&play). In the program an `IndexError` is raise and matched with various exceptions in the exception hierarchy below. It matches with the 'LookupError' exception and is handled there by just printing it. Exceptions that are not handled stop the execution of a program.
|
|
874
|
+
|
|
875
|
+

|
|
763
876
|
|
|
764
877
|
# Configuration #
|
|
765
878
|
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).
|
|
@@ -1045,10 +1158,10 @@ import memory_graph as mg
|
|
|
1045
1158
|
class MyClass:
|
|
1046
1159
|
def __init__(self):
|
|
1047
1160
|
self.children = {i: [i]*10 for i in range(5)}
|
|
1048
|
-
|
|
1049
1161
|
a = 1
|
|
1050
|
-
b = (1,2,3)
|
|
1162
|
+
b = (1, 2, 3)
|
|
1051
1163
|
c = MyClass()
|
|
1164
|
+
d = (4, 5, 6, 7)
|
|
1052
1165
|
|
|
1053
1166
|
mg.l()
|
|
1054
1167
|
```
|
|
@@ -1064,10 +1177,13 @@ class MyClass:
|
|
|
1064
1177
|
self.children = {i: [i]*10 for i in range(5)}
|
|
1065
1178
|
|
|
1066
1179
|
a = 1
|
|
1067
|
-
b = (1,2,3)
|
|
1180
|
+
b = (1, 2, 3)
|
|
1068
1181
|
c = MyClass()
|
|
1182
|
+
d = (4, 5, 6, 7)
|
|
1069
1183
|
|
|
1070
|
-
|
|
1184
|
+
# for better graph readability in large graphs:
|
|
1185
|
+
mg.collapse_type(type(c)) # collapse type(c)
|
|
1186
|
+
mg.collapse_type(id(d)) # collapse id(d)
|
|
1071
1187
|
mg.l()
|
|
1072
1188
|
```
|
|
1073
1189
|

|
|
@@ -111,6 +111,8 @@ A better way to understand what values are shared is to draw a graph using [memo
|
|
|
111
111
|
|
|
112
112
|
[Sliding Puzzle Solver](#sliding-puzzle-solver)
|
|
113
113
|
|
|
114
|
+
[Control Flow](#control-flow)
|
|
115
|
+
|
|
114
116
|
[Configuration](#configuration)
|
|
115
117
|
|
|
116
118
|
[Introspection](#introspection)
|
|
@@ -731,7 +733,6 @@ In this configuration example we show the decimal, binary and [two's complement
|
|
|
731
733
|
|
|
732
734
|

|
|
733
735
|
|
|
734
|
-
|
|
735
736
|
# Sliding Puzzle Solver #
|
|
736
737
|
|
|
737
738
|
A sliding puzzle solver as a challenging example showing how memory_graph deals with large amounts of data. Click "Continue" to step through the breadth-first search generations until a solution path is found:
|
|
@@ -740,6 +741,118 @@ A sliding puzzle solver as a challenging example showing how memory_graph deals
|
|
|
740
741
|
|
|
741
742
|

|
|
742
743
|
|
|
744
|
+
# Control Flow #
|
|
745
|
+
|
|
746
|
+
Some examples where we focus on the flow of control of Python code.
|
|
747
|
+
|
|
748
|
+
## Inheritance ##
|
|
749
|
+
|
|
750
|
+
This example shows the flow of control when using inheritance with:
|
|
751
|
+
- `super()`
|
|
752
|
+
- class variable `message_count`
|
|
753
|
+
|
|
754
|
+
```python
|
|
755
|
+
from datetime import datetime
|
|
756
|
+
|
|
757
|
+
class Notification_Service:
|
|
758
|
+
message_count = 0
|
|
759
|
+
|
|
760
|
+
def __init__(self, priority):
|
|
761
|
+
self.priority = priority
|
|
762
|
+
self.log = []
|
|
763
|
+
|
|
764
|
+
def send(self, sender, receiver, message):
|
|
765
|
+
self.log.append((type(self).__name__, self.priority, sender, receiver, message))
|
|
766
|
+
Notification_Service.message_count += 1
|
|
767
|
+
|
|
768
|
+
class Email_Notification(Notification_Service):
|
|
769
|
+
|
|
770
|
+
def __init__(self, priority, from_email):
|
|
771
|
+
super().__init__(priority)
|
|
772
|
+
self.from_email = from_email
|
|
773
|
+
|
|
774
|
+
def send(self, to_email, message):
|
|
775
|
+
super().send(self.from_email, to_email, message)
|
|
776
|
+
print(f'sending Email from:{self.from_email} to:{to_email}')
|
|
777
|
+
print(f'priority: {self.priority} message: "{message}"')
|
|
778
|
+
|
|
779
|
+
class SMS_Notification(Notification_Service):
|
|
780
|
+
|
|
781
|
+
def __init__(self, priority, from_nr):
|
|
782
|
+
super().__init__(priority)
|
|
783
|
+
self.from_nr = from_nr
|
|
784
|
+
|
|
785
|
+
def send(self, to_nr, message):
|
|
786
|
+
super().send(self.from_nr, to_nr, message)
|
|
787
|
+
print(f'sending SMS from:{self.from_nr} to:{to_nr}')
|
|
788
|
+
print(f'priority: {self.priority} message: "{message}"')
|
|
789
|
+
|
|
790
|
+
email = Email_Notification(3, 'support@company.com')
|
|
791
|
+
email.send('customer@email.com', 'Your report is ready')
|
|
792
|
+
email.send('customer@email.com', 'Update to Privacy Policy')
|
|
793
|
+
sms = Email_Notification(3, '0123456789')
|
|
794
|
+
sms.send('001122334455', 'Update to Privacy Policy')
|
|
795
|
+
```
|
|
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=36&continues=1&play).
|
|
797
|
+
|
|
798
|
+
## Decorator ##
|
|
799
|
+
|
|
800
|
+
This example shows the flow of control when using a decorator.
|
|
801
|
+
|
|
802
|
+
```python
|
|
803
|
+
def log_call(function):
|
|
804
|
+
def wrapper(*args, **kwargs):
|
|
805
|
+
print(f"Calling {function.__name__} with: {args}, {kwargs}")
|
|
806
|
+
returned = function(*args, **kwargs)
|
|
807
|
+
print(f"Finished {function.__name__} with return value: {returned}")
|
|
808
|
+
return returned
|
|
809
|
+
return wrapper
|
|
810
|
+
|
|
811
|
+
@log_call
|
|
812
|
+
def calculate_total(price, quantity, rounding=False):
|
|
813
|
+
result = price * quantity
|
|
814
|
+
return round(result) if rounding else result
|
|
815
|
+
|
|
816
|
+
@log_call
|
|
817
|
+
def send_email(receiver, message, sender="support@company.com"):
|
|
818
|
+
print(f"Sending email to:{receiver} from:{sender}, {message}")
|
|
819
|
+
|
|
820
|
+
total = calculate_total(7.5, 3, rounding=True)
|
|
821
|
+
print(total)
|
|
822
|
+
send_email("alice@example.com", "Your order is ready")
|
|
823
|
+
```
|
|
824
|
+
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/decorator.py&breakpoints=18&continues=1&play).
|
|
825
|
+
|
|
826
|
+
## Exception Handling ##
|
|
827
|
+
|
|
828
|
+
This example shows the flow of control when using exception handling.
|
|
829
|
+
|
|
830
|
+
```python
|
|
831
|
+
def fun2():
|
|
832
|
+
try:
|
|
833
|
+
d = [0] * 3
|
|
834
|
+
for i in range(4):
|
|
835
|
+
try:
|
|
836
|
+
d[i] = i # raises IndexError when i=3
|
|
837
|
+
except TypeError as e:
|
|
838
|
+
print(type(e), e)
|
|
839
|
+
except AssertionError as e:
|
|
840
|
+
print(type(e), e)
|
|
841
|
+
|
|
842
|
+
def fun1():
|
|
843
|
+
try:
|
|
844
|
+
return fun2()
|
|
845
|
+
except NameError as e:
|
|
846
|
+
print(type(e), e)
|
|
847
|
+
|
|
848
|
+
try:
|
|
849
|
+
fun1()
|
|
850
|
+
except LookupError as e:
|
|
851
|
+
print(type(e), e)
|
|
852
|
+
```
|
|
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=19&continues=1&play). In the program an `IndexError` is raise and matched with various exceptions in the exception hierarchy below. It matches with the 'LookupError' exception and is handled there by just printing it. Exceptions that are not handled stop the execution of a program.
|
|
854
|
+
|
|
855
|
+

|
|
743
856
|
|
|
744
857
|
# Configuration #
|
|
745
858
|
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).
|
|
@@ -1025,10 +1138,10 @@ import memory_graph as mg
|
|
|
1025
1138
|
class MyClass:
|
|
1026
1139
|
def __init__(self):
|
|
1027
1140
|
self.children = {i: [i]*10 for i in range(5)}
|
|
1028
|
-
|
|
1029
1141
|
a = 1
|
|
1030
|
-
b = (1,2,3)
|
|
1142
|
+
b = (1, 2, 3)
|
|
1031
1143
|
c = MyClass()
|
|
1144
|
+
d = (4, 5, 6, 7)
|
|
1032
1145
|
|
|
1033
1146
|
mg.l()
|
|
1034
1147
|
```
|
|
@@ -1044,10 +1157,13 @@ class MyClass:
|
|
|
1044
1157
|
self.children = {i: [i]*10 for i in range(5)}
|
|
1045
1158
|
|
|
1046
1159
|
a = 1
|
|
1047
|
-
b = (1,2,3)
|
|
1160
|
+
b = (1, 2, 3)
|
|
1048
1161
|
c = MyClass()
|
|
1162
|
+
d = (4, 5, 6, 7)
|
|
1049
1163
|
|
|
1050
|
-
|
|
1164
|
+
# for better graph readability in large graphs:
|
|
1165
|
+
mg.collapse_type(type(c)) # collapse type(c)
|
|
1166
|
+
mg.collapse_type(id(d)) # collapse id(d)
|
|
1051
1167
|
mg.l()
|
|
1052
1168
|
```
|
|
1053
1169
|

|
|
@@ -20,10 +20,15 @@ def read_nodes(data):
|
|
|
20
20
|
- the id of 'data' as root node.
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
-
def data_to_node(data_type, data):
|
|
24
|
-
""" Returns the Node for 'data' based on it's type. """
|
|
25
|
-
if
|
|
23
|
+
def data_to_node(data_id, data_type, data):
|
|
24
|
+
""" Returns the Node for 'data' based on it's id or type. """
|
|
25
|
+
if data_id in config.type_to_node: # for ids
|
|
26
|
+
return config.type_to_node[data_id](data)
|
|
27
|
+
elif data_type in config.type_to_node: # for predefined types
|
|
26
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)
|
|
27
32
|
elif utils.has_dict_attributes(data): # for user defined classes
|
|
28
33
|
return Node_Key_Value(data, utils.filter_dict(utils.get_dict_attributes(data)) )
|
|
29
34
|
elif utils.is_finite_iterable(data): # for lists, tuples, sets, ...
|
|
@@ -41,7 +46,7 @@ def read_nodes(data):
|
|
|
41
46
|
if data_id in nodes:
|
|
42
47
|
node = nodes[data_id]
|
|
43
48
|
else:
|
|
44
|
-
node = data_to_node(data_type, data)
|
|
49
|
+
node = data_to_node(data_id, data_type, data)
|
|
45
50
|
if isinstance(node, Node_Key_Value):
|
|
46
51
|
nodes_key_value.append(data_id)
|
|
47
52
|
nodes[data_id] = node
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memory_graph
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.72
|
|
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
|
|
@@ -131,6 +131,8 @@ A better way to understand what values are shared is to draw a graph using [memo
|
|
|
131
131
|
|
|
132
132
|
[Sliding Puzzle Solver](#sliding-puzzle-solver)
|
|
133
133
|
|
|
134
|
+
[Control Flow](#control-flow)
|
|
135
|
+
|
|
134
136
|
[Configuration](#configuration)
|
|
135
137
|
|
|
136
138
|
[Introspection](#introspection)
|
|
@@ -751,7 +753,6 @@ In this configuration example we show the decimal, binary and [two's complement
|
|
|
751
753
|
|
|
752
754
|

|
|
753
755
|
|
|
754
|
-
|
|
755
756
|
# Sliding Puzzle Solver #
|
|
756
757
|
|
|
757
758
|
A sliding puzzle solver as a challenging example showing how memory_graph deals with large amounts of data. Click "Continue" to step through the breadth-first search generations until a solution path is found:
|
|
@@ -760,6 +761,118 @@ A sliding puzzle solver as a challenging example showing how memory_graph deals
|
|
|
760
761
|
|
|
761
762
|

|
|
762
763
|
|
|
764
|
+
# Control Flow #
|
|
765
|
+
|
|
766
|
+
Some examples where we focus on the flow of control of Python code.
|
|
767
|
+
|
|
768
|
+
## Inheritance ##
|
|
769
|
+
|
|
770
|
+
This example shows the flow of control when using inheritance with:
|
|
771
|
+
- `super()`
|
|
772
|
+
- class variable `message_count`
|
|
773
|
+
|
|
774
|
+
```python
|
|
775
|
+
from datetime import datetime
|
|
776
|
+
|
|
777
|
+
class Notification_Service:
|
|
778
|
+
message_count = 0
|
|
779
|
+
|
|
780
|
+
def __init__(self, priority):
|
|
781
|
+
self.priority = priority
|
|
782
|
+
self.log = []
|
|
783
|
+
|
|
784
|
+
def send(self, sender, receiver, message):
|
|
785
|
+
self.log.append((type(self).__name__, self.priority, sender, receiver, message))
|
|
786
|
+
Notification_Service.message_count += 1
|
|
787
|
+
|
|
788
|
+
class Email_Notification(Notification_Service):
|
|
789
|
+
|
|
790
|
+
def __init__(self, priority, from_email):
|
|
791
|
+
super().__init__(priority)
|
|
792
|
+
self.from_email = from_email
|
|
793
|
+
|
|
794
|
+
def send(self, to_email, message):
|
|
795
|
+
super().send(self.from_email, to_email, message)
|
|
796
|
+
print(f'sending Email from:{self.from_email} to:{to_email}')
|
|
797
|
+
print(f'priority: {self.priority} message: "{message}"')
|
|
798
|
+
|
|
799
|
+
class SMS_Notification(Notification_Service):
|
|
800
|
+
|
|
801
|
+
def __init__(self, priority, from_nr):
|
|
802
|
+
super().__init__(priority)
|
|
803
|
+
self.from_nr = from_nr
|
|
804
|
+
|
|
805
|
+
def send(self, to_nr, message):
|
|
806
|
+
super().send(self.from_nr, to_nr, message)
|
|
807
|
+
print(f'sending SMS from:{self.from_nr} to:{to_nr}')
|
|
808
|
+
print(f'priority: {self.priority} message: "{message}"')
|
|
809
|
+
|
|
810
|
+
email = Email_Notification(3, 'support@company.com')
|
|
811
|
+
email.send('customer@email.com', 'Your report is ready')
|
|
812
|
+
email.send('customer@email.com', 'Update to Privacy Policy')
|
|
813
|
+
sms = Email_Notification(3, '0123456789')
|
|
814
|
+
sms.send('001122334455', 'Update to Privacy Policy')
|
|
815
|
+
```
|
|
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=36&continues=1&play).
|
|
817
|
+
|
|
818
|
+
## Decorator ##
|
|
819
|
+
|
|
820
|
+
This example shows the flow of control when using a decorator.
|
|
821
|
+
|
|
822
|
+
```python
|
|
823
|
+
def log_call(function):
|
|
824
|
+
def wrapper(*args, **kwargs):
|
|
825
|
+
print(f"Calling {function.__name__} with: {args}, {kwargs}")
|
|
826
|
+
returned = function(*args, **kwargs)
|
|
827
|
+
print(f"Finished {function.__name__} with return value: {returned}")
|
|
828
|
+
return returned
|
|
829
|
+
return wrapper
|
|
830
|
+
|
|
831
|
+
@log_call
|
|
832
|
+
def calculate_total(price, quantity, rounding=False):
|
|
833
|
+
result = price * quantity
|
|
834
|
+
return round(result) if rounding else result
|
|
835
|
+
|
|
836
|
+
@log_call
|
|
837
|
+
def send_email(receiver, message, sender="support@company.com"):
|
|
838
|
+
print(f"Sending email to:{receiver} from:{sender}, {message}")
|
|
839
|
+
|
|
840
|
+
total = calculate_total(7.5, 3, rounding=True)
|
|
841
|
+
print(total)
|
|
842
|
+
send_email("alice@example.com", "Your order is ready")
|
|
843
|
+
```
|
|
844
|
+
Run it in the [Memory Graph Web Debugger](https://memory-graph.com/#codeurl=https://raw.githubusercontent.com/bterwijn/memory_graph/refs/heads/main/src/decorator.py&breakpoints=18&continues=1&play).
|
|
845
|
+
|
|
846
|
+
## Exception Handling ##
|
|
847
|
+
|
|
848
|
+
This example shows the flow of control when using exception handling.
|
|
849
|
+
|
|
850
|
+
```python
|
|
851
|
+
def fun2():
|
|
852
|
+
try:
|
|
853
|
+
d = [0] * 3
|
|
854
|
+
for i in range(4):
|
|
855
|
+
try:
|
|
856
|
+
d[i] = i # raises IndexError when i=3
|
|
857
|
+
except TypeError as e:
|
|
858
|
+
print(type(e), e)
|
|
859
|
+
except AssertionError as e:
|
|
860
|
+
print(type(e), e)
|
|
861
|
+
|
|
862
|
+
def fun1():
|
|
863
|
+
try:
|
|
864
|
+
return fun2()
|
|
865
|
+
except NameError as e:
|
|
866
|
+
print(type(e), e)
|
|
867
|
+
|
|
868
|
+
try:
|
|
869
|
+
fun1()
|
|
870
|
+
except LookupError as e:
|
|
871
|
+
print(type(e), e)
|
|
872
|
+
```
|
|
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=19&continues=1&play). In the program an `IndexError` is raise and matched with various exceptions in the exception hierarchy below. It matches with the 'LookupError' exception and is handled there by just printing it. Exceptions that are not handled stop the execution of a program.
|
|
874
|
+
|
|
875
|
+

|
|
763
876
|
|
|
764
877
|
# Configuration #
|
|
765
878
|
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).
|
|
@@ -1045,10 +1158,10 @@ import memory_graph as mg
|
|
|
1045
1158
|
class MyClass:
|
|
1046
1159
|
def __init__(self):
|
|
1047
1160
|
self.children = {i: [i]*10 for i in range(5)}
|
|
1048
|
-
|
|
1049
1161
|
a = 1
|
|
1050
|
-
b = (1,2,3)
|
|
1162
|
+
b = (1, 2, 3)
|
|
1051
1163
|
c = MyClass()
|
|
1164
|
+
d = (4, 5, 6, 7)
|
|
1052
1165
|
|
|
1053
1166
|
mg.l()
|
|
1054
1167
|
```
|
|
@@ -1064,10 +1177,13 @@ class MyClass:
|
|
|
1064
1177
|
self.children = {i: [i]*10 for i in range(5)}
|
|
1065
1178
|
|
|
1066
1179
|
a = 1
|
|
1067
|
-
b = (1,2,3)
|
|
1180
|
+
b = (1, 2, 3)
|
|
1068
1181
|
c = MyClass()
|
|
1182
|
+
d = (4, 5, 6, 7)
|
|
1069
1183
|
|
|
1070
|
-
|
|
1184
|
+
# for better graph readability in large graphs:
|
|
1185
|
+
mg.collapse_type(type(c)) # collapse type(c)
|
|
1186
|
+
mg.collapse_type(id(d)) # collapse id(d)
|
|
1071
1187
|
mg.l()
|
|
1072
1188
|
```
|
|
1073
1189
|

|
|
@@ -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.72"
|
|
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"}
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|