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.
Files changed (41) hide show
  1. {memory_graph-0.3.70 → memory_graph-0.3.72}/PKG-INFO +122 -6
  2. {memory_graph-0.3.70 → memory_graph-0.3.72}/README.md +121 -5
  3. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/__init__.py +1 -1
  4. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/memory_to_nodes.py +9 -4
  5. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph.egg-info/PKG-INFO +122 -6
  6. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph.egg-info/SOURCES.txt +1 -0
  7. {memory_graph-0.3.70 → memory_graph-0.3.72}/pyproject.toml +1 -1
  8. memory_graph-0.3.72/setup.py +7 -0
  9. {memory_graph-0.3.70 → memory_graph-0.3.72}/LICENSE.txt +0 -0
  10. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/call_stack.py +0 -0
  11. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/config.py +0 -0
  12. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/config_default.py +0 -0
  13. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/config_helpers.py +0 -0
  14. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/extension_numpy.py +0 -0
  15. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/extension_pandas.py +0 -0
  16. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/extension_torch.py +0 -0
  17. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/html_table.py +0 -0
  18. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/list_view.py +0 -0
  19. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/node_base.py +0 -0
  20. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/node_key_value.py +0 -0
  21. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/node_leaf.py +0 -0
  22. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/node_linear.py +0 -0
  23. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/node_table.py +0 -0
  24. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/sequence.py +0 -0
  25. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/slicer.py +0 -0
  26. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/slices.py +0 -0
  27. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/slices_iterator.py +0 -0
  28. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/slices_table_iterator.py +0 -0
  29. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/test.py +0 -0
  30. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/test_max_graph_depth.py +0 -0
  31. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/test_memory_graph.py +0 -0
  32. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/test_memory_to_nodes.py +0 -0
  33. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/test_sequence.py +0 -0
  34. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/test_slicer.py +0 -0
  35. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/test_slices.py +0 -0
  36. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/test_slices_iterator.py +0 -0
  37. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph/utils.py +0 -0
  38. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph.egg-info/dependency_links.txt +0 -0
  39. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph.egg-info/requires.txt +0 -0
  40. {memory_graph-0.3.70 → memory_graph-0.3.72}/memory_graph.egg-info/top_level.txt +0 -0
  41. {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.70
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
  ![bitwise_operators.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/bitwise_operators.png)
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
  ![sliding_puzzle.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/sliding_puzzle.png)
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
+ ![exception_tree.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/exception_tree.png)
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
- mg.collapse_type(MyClass) # collapse type 'MyClass' for better graph readability
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
  ![collapse_type2.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/collapse_type2.png)
@@ -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
  ![bitwise_operators.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/bitwise_operators.png)
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
  ![sliding_puzzle.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/sliding_puzzle.png)
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
+ ![exception_tree.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/exception_tree.png)
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
- mg.collapse_type(MyClass) # collapse type 'MyClass' for better graph readability
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
  ![collapse_type2.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/collapse_type2.png)
@@ -2,7 +2,7 @@
2
2
  # Copyright (c) 2023, Bas Terwijn.
3
3
  # SPDX-License-Identifier: BSD-2-Clause
4
4
 
5
- __version__ = "0.3.70"
5
+ __version__ = "0.3.72"
6
6
  __author__ = 'Bas Terwijn'
7
7
 
8
8
  import memory_graph.memory_to_nodes as memory_to_nodes
@@ -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 data_type in config.type_to_node: # for predefined types
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.70
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
  ![bitwise_operators.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/bitwise_operators.png)
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
  ![sliding_puzzle.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/sliding_puzzle.png)
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
+ ![exception_tree.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/exception_tree.png)
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
- mg.collapse_type(MyClass) # collapse type 'MyClass' for better graph readability
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
  ![collapse_type2.png](https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/collapse_type2.png)
@@ -1,6 +1,7 @@
1
1
  LICENSE.txt
2
2
  README.md
3
3
  pyproject.toml
4
+ setup.py
4
5
  memory_graph/__init__.py
5
6
  memory_graph/call_stack.py
6
7
  memory_graph/config.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "memory_graph"
7
- version = "0.3.70"
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"}
@@ -0,0 +1,7 @@
1
+ from setuptools import setup
2
+
3
+ setup(
4
+ name="memory-graph",
5
+ version="0.3.30",
6
+ packages=["memory_graph"],
7
+ )
File without changes
File without changes