memory-graph 0.3.71__tar.gz → 0.3.73__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.71 → memory_graph-0.3.73}/PKG-INFO +115 -2
  2. {memory_graph-0.3.71 → memory_graph-0.3.73}/README.md +114 -1
  3. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/__init__.py +1 -1
  4. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/memory_to_nodes.py +3 -0
  5. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/utils.py +22 -11
  6. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph.egg-info/PKG-INFO +115 -2
  7. {memory_graph-0.3.71 → memory_graph-0.3.73}/pyproject.toml +1 -1
  8. {memory_graph-0.3.71 → memory_graph-0.3.73}/LICENSE.txt +0 -0
  9. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/call_stack.py +0 -0
  10. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/config.py +0 -0
  11. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/config_default.py +0 -0
  12. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/config_helpers.py +0 -0
  13. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/extension_numpy.py +0 -0
  14. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/extension_pandas.py +0 -0
  15. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/extension_torch.py +0 -0
  16. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/html_table.py +0 -0
  17. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/list_view.py +0 -0
  18. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/node_base.py +0 -0
  19. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/node_key_value.py +0 -0
  20. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/node_leaf.py +0 -0
  21. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/node_linear.py +0 -0
  22. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/node_table.py +0 -0
  23. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/sequence.py +0 -0
  24. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/slicer.py +0 -0
  25. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/slices.py +0 -0
  26. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/slices_iterator.py +0 -0
  27. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/slices_table_iterator.py +0 -0
  28. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/test.py +0 -0
  29. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/test_max_graph_depth.py +0 -0
  30. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/test_memory_graph.py +0 -0
  31. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/test_memory_to_nodes.py +0 -0
  32. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/test_sequence.py +0 -0
  33. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/test_slicer.py +0 -0
  34. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/test_slices.py +0 -0
  35. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/test_slices_iterator.py +0 -0
  36. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph.egg-info/SOURCES.txt +0 -0
  37. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph.egg-info/dependency_links.txt +0 -0
  38. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph.egg-info/requires.txt +0 -0
  39. {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph.egg-info/top_level.txt +0 -0
  40. {memory_graph-0.3.71 → memory_graph-0.3.73}/setup.cfg +0 -0
  41. {memory_graph-0.3.71 → memory_graph-0.3.73}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memory_graph
3
- Version: 0.3.71
3
+ Version: 0.3.73
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).
@@ -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).
@@ -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.71"
5
+ __version__ = "0.3.73"
6
6
  __author__ = 'Bas Terwijn'
7
7
 
8
8
  import memory_graph.memory_to_nodes as memory_to_nodes
@@ -26,6 +26,9 @@ def read_nodes(data):
26
26
  return config.type_to_node[data_id](data)
27
27
  elif data_type in config.type_to_node: # for predefined types
28
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)
29
32
  elif utils.has_dict_attributes(data): # for user defined classes
30
33
  return Node_Key_Value(data, utils.filter_dict(utils.get_dict_attributes(data)) )
31
34
  elif utils.is_finite_iterable(data): # for lists, tuples, sets, ...
@@ -4,6 +4,7 @@
4
4
 
5
5
  import math
6
6
  import types
7
+ import functools
7
8
 
8
9
  def has_dict_attributes(value):
9
10
  """ Returns 'True' if 'value' has a '__dict__' attribute. """
@@ -13,33 +14,43 @@ def get_dict_attributes(value):
13
14
  """ Returns the items of the '__dict__' attribute of 'value'."""
14
15
  return getattr(value,"__dict__")
15
16
 
16
- def is_function(obj):
17
- if isinstance(obj, types.FunctionType) or isinstance(obj, types.MethodType):
17
+ def is_not_state(obj):
18
+ if isinstance(obj, (types.FunctionType, types.MethodType,
19
+ types.BuiltinFunctionType, types.BuiltinMethodType,
20
+ classmethod, staticmethod,
21
+ property, functools.partialmethod)):
18
22
  return True
19
- return type(obj).__name__ in {'method_descriptor', 'builtin_function_or_method', 'getset_descriptor', 'classmethod_descriptor'}
23
+ return type(obj).__name__ in {
24
+ 'method_descriptor',
25
+ 'builtin_function_or_method',
26
+ 'getset_descriptor',
27
+ 'classmethod_descriptor',
28
+ 'wrapper_descriptor',
29
+ 'member_descriptor',
30
+ 'method-wrapper',
31
+ }
20
32
 
21
33
  def filter_dict(dictionary):
22
34
  """ Filters out the unwanted dict attributes. """
23
- if '__name__' in dictionary: # filter stack frames in global scope
24
- return [
35
+ if '__name__' in dictionary:
36
+ return [ # filter classes and modules, no non-state allowed as values
25
37
  (k,v) for k, v in dictionary.items() if
26
38
  not (type(k) is str and k.startswith('__')) and
27
39
  not isinstance(v,types.ModuleType) and
28
- not is_function(v)
40
+ not is_not_state(v)
29
41
  ]
30
- return [
42
+ return [ # filter dictionaries, non-state allowed as values
31
43
  (k,v) for k, v in dictionary.items() if
32
44
  not (type(k) is str and k.startswith('__'))
33
45
  ]
34
46
 
35
47
  def filter_type_attributes(tuples):
36
48
  """ Filters out the unwanted type attributes (class/static methods). """
37
- return [
49
+ return [ # filter type objects, no non-state allowed as values
38
50
  (k,v) for k, v in tuples if
39
51
  not (type(k) is str and k.startswith('__')) and
40
- not type(v) in {classmethod, staticmethod} and
41
- not callable(v)
42
- ]
52
+ not is_not_state(v)
53
+ ]
43
54
 
44
55
  def make_sliceable(data):
45
56
  """ Returns a sliceble version of data, convert to list if not yet sliceble. """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memory_graph
3
- Version: 0.3.71
3
+ Version: 0.3.73
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).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "memory_graph"
7
- version = "0.3.71"
7
+ version = "0.3.73"
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