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.
- {memory_graph-0.3.71 → memory_graph-0.3.73}/PKG-INFO +115 -2
- {memory_graph-0.3.71 → memory_graph-0.3.73}/README.md +114 -1
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/__init__.py +1 -1
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/memory_to_nodes.py +3 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/utils.py +22 -11
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph.egg-info/PKG-INFO +115 -2
- {memory_graph-0.3.71 → memory_graph-0.3.73}/pyproject.toml +1 -1
- {memory_graph-0.3.71 → memory_graph-0.3.73}/LICENSE.txt +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/call_stack.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/config.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/config_default.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/config_helpers.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/extension_numpy.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/extension_pandas.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/extension_torch.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/html_table.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/list_view.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/node_base.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/node_key_value.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/node_leaf.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/node_linear.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/node_table.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/sequence.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/slicer.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/slices.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/slices_iterator.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/slices_table_iterator.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/test.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/test_max_graph_depth.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/test_memory_graph.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/test_memory_to_nodes.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/test_sequence.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/test_slicer.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/test_slices.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph/test_slices_iterator.py +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph.egg-info/SOURCES.txt +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph.egg-info/dependency_links.txt +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph.egg-info/requires.txt +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/memory_graph.egg-info/top_level.txt +0 -0
- {memory_graph-0.3.71 → memory_graph-0.3.73}/setup.cfg +0 -0
- {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.
|
|
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
|

|
|
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).
|
|
@@ -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).
|
|
@@ -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
|
|
17
|
-
if isinstance(obj, types.FunctionType
|
|
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 {
|
|
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:
|
|
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
|
|
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
|
|
41
|
-
|
|
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.
|
|
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
|

|
|
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).
|
|
@@ -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.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
|
|
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
|