invocation-tree 0.0.6__tar.gz → 0.0.9__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 (203) hide show
  1. invocation_tree-0.0.9/PKG-INFO +546 -0
  2. invocation_tree-0.0.9/README.md +527 -0
  3. invocation_tree-0.0.9/images/#generator_function.py# +13 -0
  4. invocation_tree-0.0.9/images/compute.gif +0 -0
  5. invocation_tree-0.0.9/images/compute.py +22 -0
  6. invocation_tree-0.0.9/images/compute.py~ +22 -0
  7. invocation_tree-0.0.9/images/compute1.png +0 -0
  8. invocation_tree-0.0.9/images/compute2.png +0 -0
  9. invocation_tree-0.0.9/images/compute3.png +0 -0
  10. invocation_tree-0.0.9/images/compute4.png +0 -0
  11. invocation_tree-0.0.9/images/compute5.png +0 -0
  12. invocation_tree-0.0.9/images/compute6.png +0 -0
  13. invocation_tree-0.0.9/images/compute7.png +0 -0
  14. invocation_tree-0.0.9/images/compute8.png +0 -0
  15. invocation_tree-0.0.9/images/compute9.png +0 -0
  16. invocation_tree-0.0.9/images/create_images.sh +41 -0
  17. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/factorial1.png +0 -0
  18. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/factorial2.png +0 -0
  19. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/factorial3.png +0 -0
  20. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/factorial4.png +0 -0
  21. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/factorial5.png +0 -0
  22. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/factorial6.png +0 -0
  23. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/factorial7.png +0 -0
  24. invocation_tree-0.0.9/images/generator_expression.gif +0 -0
  25. invocation_tree-0.0.9/images/generator_expression.py +11 -0
  26. invocation_tree-0.0.9/images/generator_expression.py~ +9 -0
  27. invocation_tree-0.0.9/images/generator_expression1.png +0 -0
  28. invocation_tree-0.0.9/images/generator_expression2.png +0 -0
  29. invocation_tree-0.0.9/images/generator_expression3.png +0 -0
  30. invocation_tree-0.0.9/images/generator_expression4.png +0 -0
  31. invocation_tree-0.0.9/images/generator_expression5.png +0 -0
  32. invocation_tree-0.0.9/images/generator_expression6.png +0 -0
  33. invocation_tree-0.0.9/images/generator_expression7.png +0 -0
  34. invocation_tree-0.0.9/images/generator_expression8.png +0 -0
  35. invocation_tree-0.0.9/images/generator_expression9.png +0 -0
  36. invocation_tree-0.0.9/images/generator_function.gif +0 -0
  37. invocation_tree-0.0.9/images/generator_function.py +12 -0
  38. invocation_tree-0.0.9/images/generator_function1.png +0 -0
  39. invocation_tree-0.0.9/images/generator_function2.png +0 -0
  40. invocation_tree-0.0.9/images/generator_function3.png +0 -0
  41. invocation_tree-0.0.9/images/generator_function4.png +0 -0
  42. invocation_tree-0.0.9/images/generator_function5.png +0 -0
  43. invocation_tree-0.0.9/images/generator_function6.png +0 -0
  44. invocation_tree-0.0.9/images/generator_function7.png +0 -0
  45. invocation_tree-0.0.9/images/generator_function8.png +0 -0
  46. invocation_tree-0.0.9/images/generator_function9.png +0 -0
  47. invocation_tree-0.0.9/images/generator_pipeline.gif +0 -0
  48. invocation_tree-0.0.9/images/generator_pipeline.py +22 -0
  49. invocation_tree-0.0.9/images/generator_pipeline.py~ +26 -0
  50. invocation_tree-0.0.9/images/generator_pipeline1.png +0 -0
  51. invocation_tree-0.0.9/images/generator_pipeline10.png +0 -0
  52. invocation_tree-0.0.9/images/generator_pipeline11.png +0 -0
  53. invocation_tree-0.0.9/images/generator_pipeline12.png +0 -0
  54. invocation_tree-0.0.9/images/generator_pipeline13.png +0 -0
  55. invocation_tree-0.0.9/images/generator_pipeline14.png +0 -0
  56. invocation_tree-0.0.9/images/generator_pipeline15.png +0 -0
  57. invocation_tree-0.0.9/images/generator_pipeline16.png +0 -0
  58. invocation_tree-0.0.9/images/generator_pipeline17.png +0 -0
  59. invocation_tree-0.0.9/images/generator_pipeline18.png +0 -0
  60. invocation_tree-0.0.9/images/generator_pipeline19.png +0 -0
  61. invocation_tree-0.0.9/images/generator_pipeline2.png +0 -0
  62. invocation_tree-0.0.9/images/generator_pipeline20.png +0 -0
  63. invocation_tree-0.0.9/images/generator_pipeline21.png +0 -0
  64. invocation_tree-0.0.9/images/generator_pipeline22.png +0 -0
  65. invocation_tree-0.0.9/images/generator_pipeline23.png +0 -0
  66. invocation_tree-0.0.9/images/generator_pipeline24.png +0 -0
  67. invocation_tree-0.0.9/images/generator_pipeline25.png +0 -0
  68. invocation_tree-0.0.9/images/generator_pipeline3.png +0 -0
  69. invocation_tree-0.0.9/images/generator_pipeline4.png +0 -0
  70. invocation_tree-0.0.9/images/generator_pipeline5.png +0 -0
  71. invocation_tree-0.0.9/images/generator_pipeline6.png +0 -0
  72. invocation_tree-0.0.9/images/generator_pipeline7.png +0 -0
  73. invocation_tree-0.0.9/images/generator_pipeline8.png +0 -0
  74. invocation_tree-0.0.9/images/generator_pipeline9.png +0 -0
  75. invocation_tree-0.0.9/images/iterable.py +44 -0
  76. invocation_tree-0.0.9/images/iterable.py~ +31 -0
  77. invocation_tree-0.0.9/images/my_list.py +20 -0
  78. invocation_tree-0.0.9/images/my_list.py~ +4 -0
  79. invocation_tree-0.0.9/images/my_range.gif +0 -0
  80. invocation_tree-0.0.9/images/my_range.py +41 -0
  81. invocation_tree-0.0.9/images/my_range.py~ +14 -0
  82. invocation_tree-0.0.9/images/my_range1.png +0 -0
  83. invocation_tree-0.0.9/images/my_range10.png +0 -0
  84. invocation_tree-0.0.9/images/my_range11.png +0 -0
  85. invocation_tree-0.0.9/images/my_range12.png +0 -0
  86. invocation_tree-0.0.9/images/my_range13.png +0 -0
  87. invocation_tree-0.0.9/images/my_range14.png +0 -0
  88. invocation_tree-0.0.9/images/my_range15.png +0 -0
  89. invocation_tree-0.0.9/images/my_range2.png +0 -0
  90. invocation_tree-0.0.9/images/my_range3.png +0 -0
  91. invocation_tree-0.0.9/images/my_range4.png +0 -0
  92. invocation_tree-0.0.9/images/my_range5.png +0 -0
  93. invocation_tree-0.0.9/images/my_range6.png +0 -0
  94. invocation_tree-0.0.9/images/my_range7.png +0 -0
  95. invocation_tree-0.0.9/images/my_range8.png +0 -0
  96. invocation_tree-0.0.9/images/my_range9.png +0 -0
  97. invocation_tree-0.0.9/images/out.txt +8 -0
  98. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/permutations1.png +0 -0
  99. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/permutations10.png +0 -0
  100. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/permutations11.png +0 -0
  101. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/permutations12.png +0 -0
  102. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/permutations13.png +0 -0
  103. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/permutations2.png +0 -0
  104. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/permutations3.png +0 -0
  105. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/permutations4.png +0 -0
  106. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/permutations5.png +0 -0
  107. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/permutations6.png +0 -0
  108. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/permutations7.png +0 -0
  109. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/permutations8.png +0 -0
  110. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/permutations9.png +0 -0
  111. invocation_tree-0.0.9/images/students.gif +0 -0
  112. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/students.py +2 -2
  113. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/students1.png +0 -0
  114. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/students10.png +0 -0
  115. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/students11.png +0 -0
  116. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/students12.png +0 -0
  117. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/students13.png +0 -0
  118. invocation_tree-0.0.9/images/students14.png +0 -0
  119. invocation_tree-0.0.9/images/students15.png +0 -0
  120. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/students2.png +0 -0
  121. invocation_tree-0.0.9/images/students3.png +0 -0
  122. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/students4.png +0 -0
  123. invocation_tree-0.0.9/images/students5.png +0 -0
  124. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/students6.png +0 -0
  125. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/students7.png +0 -0
  126. invocation_tree-0.0.9/images/students8.png +0 -0
  127. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/students9.png +0 -0
  128. invocation_tree-0.0.9/images/tree.gv~ +22 -0
  129. invocation_tree-0.0.9/images/tree.pdf +0 -0
  130. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/invocation_tree/__init__.py +14 -8
  131. invocation_tree-0.0.9/invocation_tree.egg-info/PKG-INFO +546 -0
  132. invocation_tree-0.0.9/invocation_tree.egg-info/SOURCES.txt +153 -0
  133. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/setup.py +1 -1
  134. invocation_tree-0.0.6/PKG-INFO +0 -189
  135. invocation_tree-0.0.6/README.md +0 -170
  136. invocation_tree-0.0.6/images/create_images.sh +0 -16
  137. invocation_tree-0.0.6/images/factorial0.png +0 -0
  138. invocation_tree-0.0.6/images/factorial8.gv +0 -36
  139. invocation_tree-0.0.6/images/factorial9.gv +0 -37
  140. invocation_tree-0.0.6/images/permutations0.png +0 -0
  141. invocation_tree-0.0.6/images/students.gif +0 -0
  142. invocation_tree-0.0.6/images/students14.png +0 -0
  143. invocation_tree-0.0.6/images/students15.png +0 -0
  144. invocation_tree-0.0.6/images/students16.gv +0 -44
  145. invocation_tree-0.0.6/images/students17.gv +0 -50
  146. invocation_tree-0.0.6/images/students18.gv +0 -51
  147. invocation_tree-0.0.6/images/students19.gv +0 -58
  148. invocation_tree-0.0.6/images/students20.gv +0 -59
  149. invocation_tree-0.0.6/images/students21.gv +0 -60
  150. invocation_tree-0.0.6/images/students22.gv +0 -61
  151. invocation_tree-0.0.6/images/students23.gv +0 -60
  152. invocation_tree-0.0.6/images/students24.gv +0 -66
  153. invocation_tree-0.0.6/images/students25.gv +0 -68
  154. invocation_tree-0.0.6/images/students26.gv +0 -68
  155. invocation_tree-0.0.6/images/students27.gv +0 -68
  156. invocation_tree-0.0.6/images/students28.gv +0 -67
  157. invocation_tree-0.0.6/images/students29.gv +0 -68
  158. invocation_tree-0.0.6/images/students3.png +0 -0
  159. invocation_tree-0.0.6/images/students30.gv +0 -69
  160. invocation_tree-0.0.6/images/students5.png +0 -0
  161. invocation_tree-0.0.6/images/students8.png +0 -0
  162. invocation_tree-0.0.6/images/tree0.gv +0 -7
  163. invocation_tree-0.0.6/images/tree1.gv +0 -13
  164. invocation_tree-0.0.6/images/tree10.gv +0 -56
  165. invocation_tree-0.0.6/images/tree11.gv +0 -64
  166. invocation_tree-0.0.6/images/tree12.gv +0 -66
  167. invocation_tree-0.0.6/images/tree13.gv +0 -67
  168. invocation_tree-0.0.6/images/tree14.gv +0 -72
  169. invocation_tree-0.0.6/images/tree15.gv +0 -73
  170. invocation_tree-0.0.6/images/tree16.gv +0 -75
  171. invocation_tree-0.0.6/images/tree17.gv +0 -75
  172. invocation_tree-0.0.6/images/tree2.gv +0 -22
  173. invocation_tree-0.0.6/images/tree3.gv +0 -30
  174. invocation_tree-0.0.6/images/tree4.gv +0 -32
  175. invocation_tree-0.0.6/images/tree5.gv +0 -33
  176. invocation_tree-0.0.6/images/tree6.gv +0 -39
  177. invocation_tree-0.0.6/images/tree7.gv +0 -47
  178. invocation_tree-0.0.6/images/tree8.gv +0 -49
  179. invocation_tree-0.0.6/images/tree9.gv +0 -50
  180. invocation_tree-0.0.6/invocation_tree.egg-info/PKG-INFO +0 -189
  181. invocation_tree-0.0.6/invocation_tree.egg-info/SOURCES.txt +0 -101
  182. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/LICENSE.txt +0 -0
  183. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/MANIFEST.in +0 -0
  184. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/create_gif.sh +0 -0
  185. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/create_images.sh~ +0 -0
  186. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/factorial.gif +0 -0
  187. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/factorial.py +0 -0
  188. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/factorial.py~ +0 -0
  189. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/invocation_tree.pdf +0 -0
  190. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/invocation_tree0.pdf +0 -0
  191. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/permutations.gif +0 -0
  192. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/permutations.py +0 -0
  193. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/permutations.py~ +0 -0
  194. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/permutations22.png~ +0 -0
  195. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/student.gif +0 -0
  196. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/students.py~ +0 -0
  197. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/test.py +0 -0
  198. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/images/vscode.png +0 -0
  199. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/install.txt +0 -0
  200. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/invocation_tree.egg-info/dependency_links.txt +0 -0
  201. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/invocation_tree.egg-info/requires.txt +0 -0
  202. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/invocation_tree.egg-info/top_level.txt +0 -0
  203. {invocation_tree-0.0.6 → invocation_tree-0.0.9}/setup.cfg +0 -0
@@ -0,0 +1,546 @@
1
+ Metadata-Version: 2.1
2
+ Name: invocation_tree
3
+ Version: 0.0.9
4
+ Summary: Generate an invocation tree of functions calls.
5
+ Home-page: https://github.com/bterwijn/invocation_tree
6
+ Author: Bas Terwijn
7
+ Author-email: bterwijn@gmail.com
8
+ License: BSD 2-clause
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Education
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: BSD License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Education
15
+ Classifier: Topic :: Software Development :: Debuggers
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE.txt
18
+ Requires-Dist: graphviz
19
+
20
+ # Installation #
21
+ Install (or upgrade) `invocation_tree` using pip:
22
+ ```
23
+ pip install --upgrade invocation_tree
24
+ ```
25
+ Additionally [Graphviz](https://graphviz.org/download/) needs to be installed.
26
+
27
+ # Invocation Tree #
28
+ The [invocation_tree](https://pypi.org/project/invocation-tree/) package is designed to help with **program understanding and debugging** by visualizing the **tree of function invocations** that occur during program execution. Here’s a simple example of how it works, we start with `a = 1` and compute:
29
+
30
+ ```
31
+ (a - 3 + 9) * 6
32
+ ```
33
+
34
+ ```python
35
+ import invocation_tree as invo_tree
36
+
37
+ def main():
38
+ a = 1
39
+ a = expression(a)
40
+ return multiply(a, 6)
41
+
42
+ def expression(a):
43
+ a = subtract(a, 3)
44
+ return add(a, 9)
45
+
46
+ def subtract(a, b):
47
+ return a - b
48
+
49
+ def add(a, b):
50
+ return a + b
51
+
52
+ def multiply(a, b):
53
+ return a * b
54
+
55
+ tree = invo_tree.blocking()
56
+ print( tree(main) )
57
+ ```
58
+ Running the program and pressing <Enter> a number of times results in:
59
+ ![compute](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/compute.gif)
60
+ ```
61
+ 42
62
+ ```
63
+ Each node in the tree represents a function call, and the node's color indicates its state:
64
+
65
+ - White: The function is currently being executed (it is at the top of the call stack).
66
+ - Green: The function is paused and will resume execution later (it is lower down on the call stack).
67
+ - Red: The function has completed execution and returned (it has been removed from the call stack).
68
+
69
+ For every function, the package displays its **local variables** and **return value**. Changes to these values over time are highlighted using bold text and gray shading to make them easy to track.
70
+
71
+ # Chapters #
72
+
73
+ [Comprehensions](#Comprehensions)
74
+
75
+ [Debugger](#Debugger)
76
+
77
+ [Recursion](#Recursion)
78
+
79
+ [Lazy Evalution](#Lazy-Evalution)
80
+
81
+ [Configuration](#Configuration)
82
+
83
+ [Troubleshooting](#Troubleshooting)
84
+
85
+ # Author #
86
+ Bas Terwijn
87
+
88
+ # Inspiration #
89
+ Inspired by [rcviz](https://github.com/carlsborg/rcviz).
90
+
91
+ # Supported by #
92
+ <img src="https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/uva.png" alt="University of Amsterdam" width="600">
93
+
94
+ ___
95
+ ___
96
+
97
+ # Comprehensions #
98
+ In this more interesting example we compute which students pass a course by using list and dictionary comprehensions.
99
+
100
+ ```python
101
+ import invocation_tree as invo_tree
102
+ from decimal import Decimal, ROUND_HALF_UP
103
+
104
+ def main():
105
+ students = {'Ann':[7.5, 8.0],
106
+ 'Bob':[4.5, 6.0],
107
+ 'Coy':[7.5, 6.0]}
108
+ averages = {student:compute_average(grades)
109
+ for student, grades in students.items()}
110
+ passing = passing_students(averages)
111
+ print(passing)
112
+
113
+ def compute_average(grades):
114
+ average = sum(grades)/len(grades)
115
+ return half_up_round(average, 1)
116
+
117
+ def half_up_round(value, digits=0):
118
+ """ High-precision half-up rounding of 'value' to a specified number of 'digits'. """
119
+ return float(Decimal(str(value)).quantize(Decimal(f"1e-{digits}"),
120
+ rounding=ROUND_HALF_UP))
121
+
122
+ def passing_students(averages):
123
+ return [student
124
+ for student, average in averages.items()
125
+ if average >= 5.5]
126
+
127
+ if __name__ == '__main__':
128
+ tree = invo_tree.blocking()
129
+ tree(main) # show invocation tree starting at main
130
+ ```
131
+ ![students](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/students.gif)
132
+ ```
133
+ ['Ann', 'Coy']
134
+ ```
135
+
136
+ ## Blocking ##
137
+ The program blocks execution at every function call and return statement, printing the current location in the source code. Press the &lt;Enter&gt; key to continue execution. To block at every line of the program (like in a debugger tool) and only where a change of value occured, use instead:
138
+
139
+ ```python
140
+ tree = invo_tree.blocking_each_change()
141
+ ```
142
+
143
+ # Debugger #
144
+ To visualize the invocation tree in a debugger tool, such as the integrated debugger in Visual Studio Code, use instead:
145
+
146
+ ```python
147
+ tree = invo_tree.debugger()
148
+ ```
149
+
150
+ and open the 'tree.pdf' file manually.
151
+ ![Visual Studio Code debugger](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/vscode.png)
152
+
153
+ # Recursion #
154
+ An invocation tree is particularly helpful to better understand recursion. A simple `factorial()` example:
155
+
156
+ ```python
157
+ import invocation_tree as invo_tree
158
+
159
+ def factorial(n):
160
+ if n <= 1:
161
+ return 1
162
+ return n * factorial(n - 1)
163
+
164
+ tree = invo_tree.blocking()
165
+ print( tree(factorial, 4) ) # show invocation tree of calling factorial(4)
166
+ ```
167
+ ![factorial](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/factorial.gif)
168
+
169
+ This `permutations()` example shows the depth-first nature of recursive execution:
170
+
171
+ ```python
172
+ import invocation_tree as invo_tree
173
+
174
+ def permutations(elements, perm, n):
175
+ if n==0:
176
+ return [perm]
177
+ all_perms = []
178
+ for element in elements:
179
+ all_perms.extend(permutations(elements, perm + element, n-1))
180
+ return all_perms
181
+
182
+ tree = invo_tree.blocking()
183
+ result = tree(permutations, ['L','R'], '', 2)
184
+ print(result) # all permutations of going Left and Right of length 2
185
+ ```
186
+ ![permutations](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/permutations.gif)
187
+
188
+ ## Hide Variables ##
189
+ In an educational context it can be useful to hide certian variables to avoid unnecessary complexity. This can for example be done with:
190
+
191
+ ```python
192
+ tree = invo_tree.blocking()
193
+ tree.hide.add('permutations.elements')
194
+ tree.hide.add('permutations.element')
195
+ tree.hide.add('permutations.all_perms')
196
+ ```
197
+
198
+ # Lazy Evalution
199
+ An invocation tree is helpful to understand how a pipeline of generators is lazily evaluated. But to understand generators and lazy evaluation we first have to understand the Iterator Protocol.
200
+
201
+ ## Iterator Protocol ##
202
+ The [Iterator Protocol](https://docs.python.org/3/library/stdtypes.html#iterator-types) is implemented by many different types:
203
+
204
+ `range`, `list`, `set`, `dict`, ...
205
+
206
+ which make these type iterable, meaning we can iterate over values of these types to get a sequence of values. It works by:
207
+
208
+ - first calling iter(iterable) to get an iterator
209
+ - then repeatedly calling next(iterator) to get each value
210
+ - the sequence ends when a StopIteration exceptions is raised
211
+
212
+ An example of iterable `range` and `list` in the Python interpreter looks like:
213
+
214
+ <TABLE><TR><TD>
215
+
216
+ ```
217
+ $ python
218
+ >>> iterator = iter(range(1,4))
219
+ >>> next(iterator)
220
+ 1
221
+ >>> next(iterator)
222
+ 2
223
+ >>> next(iterator)
224
+ 3
225
+ >>> next(iterator)
226
+ Traceback (most recent call last):
227
+ File "<stdin>", line 1, in <module>
228
+ StopIteration
229
+ ```
230
+
231
+ </TD><TD>
232
+
233
+ ```
234
+ $ python
235
+ >>> iterator = iter([1,2,3])
236
+ >>> next(iterator)
237
+ 1
238
+ >>> next(iterator)
239
+ 2
240
+ >>> next(iterator)
241
+ 3
242
+ >>> next(iterator)
243
+ Traceback (most recent call last):
244
+ File "<stdin>", line 1, in <module>
245
+ StopIteration
246
+ ```
247
+
248
+ </TD></TR></TABLE>
249
+
250
+ It is the Iterator Protocol that allows a for-loop to read a sequence of values from an iterable:
251
+
252
+ ```python
253
+ iterable = range(1,4)
254
+ for value in iterable:
255
+ print(value)
256
+ ```
257
+ ```
258
+ 1
259
+ 2
260
+ 3
261
+ ```
262
+
263
+ and the same holds for many functions like `list()`, `sum()`, `max()`, `min()`, ...
264
+
265
+ <TABLE><TR><TD>
266
+
267
+ ```python
268
+ iterable = range(1,4)
269
+ print('list:', list(iterable))
270
+ ```
271
+ ```
272
+ list: [1, 2, 3]
273
+ ```
274
+
275
+ </TD><TD>
276
+
277
+ ```python
278
+ iterable = range(1,4)
279
+ print('sum:', sum(iterable))
280
+ ```
281
+ ```
282
+ sum: 6
283
+ ```
284
+
285
+ </TD></TR></TABLE>
286
+
287
+ We can define our own `My_Range` and `My_Iterator` class to see the Iterator Protocol in action.
288
+ ```python
289
+ import invocation_tree as invo_tree
290
+
291
+ class My_Iterator:
292
+
293
+ def __init__(self, my_range):
294
+ self.my_range = my_range
295
+ self.value = self.my_range.start
296
+
297
+ def __repr__(self):
298
+ return f'My_Iterator value:{self.value}'
299
+
300
+ def __next__(self):
301
+ print('My_Iterator.__next__')
302
+ prev = self.value
303
+ self.value += self.my_range.step
304
+ if prev < self.my_range.stop:
305
+ return prev
306
+ raise StopIteration
307
+
308
+ class My_Range:
309
+
310
+ def __init__(self, start, stop, step=1):
311
+ self.start = start
312
+ self.stop = stop
313
+ self.step = step
314
+
315
+ def __repr__(self):
316
+ return f'My_Range start:{self.start} stop:{self.stop} step:{self.step}'
317
+
318
+ def __iter__(self):
319
+ print('My_Range.__iter__')
320
+ return My_Iterator(self)
321
+
322
+ def main():
323
+ my_range = My_Range(1, 4)
324
+ for i in my_range:
325
+ print(i)
326
+
327
+ tree = invo_tree.blocking()
328
+ tree(main)
329
+ ```
330
+ ![my_range.gif](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/my_range.gif)
331
+
332
+ ```
333
+ My_Range.__iter__
334
+ My_Iterator.__next__
335
+ 1
336
+ My_Iterator.__next__
337
+ 2
338
+ My_Iterator.__next__
339
+ 3
340
+ My_Iterator.__next__
341
+ ```
342
+ As you can see a lot happens in `main()` to complete the for-loop:
343
+ - A 'my_range' object is created using its `My_Range.__init__` method.
344
+ - The for-loop requests an iterator using 'iter(my_range)' resulting in a `My_Range.__iter__` method call.
345
+ - The for-loop keeps calling 'next(iterator)' to get the sequence of values resulting in `My_Iterator.__next__` calls.
346
+ - At the 4th call the sequence is ended with a `StopIteration` exception.
347
+
348
+ ## Generator Functions ##
349
+ By using `yield` instead of `return` in a function, we can create a [generator](https://docs.python.org/3/reference/expressions.html#yield-expressions) that produces a sequence of values as an iterable.
350
+
351
+ ```python
352
+ def my_generator():
353
+ yield 1
354
+ yield 2
355
+ yield 3
356
+
357
+ def main():
358
+ for i in my_generator():
359
+ print(i)
360
+ print('sum:', sum(my_generator()))
361
+
362
+ main()
363
+ ```
364
+ ```
365
+ 1
366
+ 2
367
+ 3
368
+ sum: 6
369
+ ```
370
+
371
+ The generator iterable can only be used once. Call the generator again when you need a new iterable:
372
+
373
+ ```python
374
+ def my_generator():
375
+ yield 1
376
+ yield 2
377
+ yield 3
378
+
379
+ def main():
380
+ iterable = my_generator()
381
+ print('sum:', sum(iterable)) # 6
382
+ print('sum:', sum(iterable)) # 0, a used up generator doesn't give any values
383
+ iterable = my_generator()
384
+ print('sum:', sum(iterable)) # 6
385
+
386
+ main()
387
+ ```
388
+
389
+ A generator is lazy, meaning that it will only produce its values if you request them via the Iterator Protocol. That means that if you print the iterable produced by a generator it will just print '&lt;generator object ...&gt;'. To print its values you can for example use `list()` that uses the Iterator Protocol to request its values and converts them to a `list`, but then you have used the generator so it no longer has values.
390
+
391
+ ```python
392
+ def my_generator():
393
+ yield 1
394
+ yield 2
395
+ yield 3
396
+
397
+ def main():
398
+ my_gen = my_generator()
399
+ print( my_gen )
400
+ print( list(my_gen) ) # printing uses up the generator
401
+ print( list(my_gen) ) # no more values available
402
+ print( list(my_generator()) ) # new generator
403
+
404
+ main()
405
+ ```
406
+ ```
407
+ <generator object my_generator at 0x7fd965cf0ca0>
408
+ [1, 2, 3]
409
+ []
410
+ [1, 2, 3]
411
+ ```
412
+
413
+ By using invocation_tree we can see how a the Iterator Protocol works on the generator.
414
+
415
+ ```python
416
+ import invocation_tree as invo_tree
417
+
418
+ def my_generator():
419
+ yield 1
420
+ yield 2
421
+ yield 3
422
+
423
+ def main():
424
+ return list(my_generator())
425
+
426
+ tree = invo_tree.blocking()
427
+ print( tree(main) )
428
+ ```
429
+ ![generator_function.gif](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/generator_function.gif)
430
+ ```
431
+ [1, 2, 3]
432
+ ```
433
+ In `main()`:
434
+ - The 'list(my_generator())' call requests an iterator from the generator.
435
+ - It keep calling next() on it to read the sequence resulting in `my_generator()` calls.
436
+ - When called `my_generator()` yields a value, and then pauses and saves its state, allowing it to continue from where it left off when called again.
437
+ - At the 4th call `my_generator()` returns None and automatically raises a StopIteration exception that signals the end of the sequence and makes `list()` return its result.
438
+
439
+ ## Generator Expressions ##
440
+ Another way to create a generator is with a [generator expression](https://docs.python.org/3/reference/expressions.html#generator-expressions) that looks like a list comprehension except that it uses the '(' and ')' parentheses instead of the '[' and ']' brackets. A generator expression reads from an iterable and produces a generator iterable:
441
+
442
+ ```python
443
+ import invocation_tree as invo_tree
444
+
445
+ def main():
446
+ my_generator = (i*10 for i in range(1,4)) # generator expression
447
+ return list(my_generator)
448
+
449
+ tree = invo_tree.blocking()
450
+ import types
451
+ tree.to_string[types.GeneratorType] = lambda x: 'generator' # short name for generators
452
+ tree.to_string[type(iter(range(0)))] = lambda x: 'range_iterator' # short name for range_iterator
453
+ print( tree(main) )
454
+ ```
455
+ ![generator_expression.gif](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/generator_expression.gif)
456
+ ```
457
+ [10, 20, 30]
458
+ ```
459
+ ## Generator Pipeline ##
460
+ The key advantage of Python generators is their ability to create a **pipeline of computations**, where each generator handles a specific part of the process. Values are processed one at a time and flow through the pipeline lazily, meaning computations are performed only when needed. This eliminates the need to store the entire dataset in memory, such as in a list, making generators highly memory-efficient. Because the computation is split into modular steps, it’s easy to add, remove, or modify generators in the pipeline. This combination of flexibility, low memory usage, and on-demand processing makes generators ideal for handling large datasets or continuous data streams.
461
+
462
+ ```python
463
+ import invocation_tree as invo_tree
464
+
465
+ def subtract(pipeline):
466
+ for a in pipeline:
467
+ yield a - 3
468
+
469
+ def multiply(pipeline):
470
+ for a in pipeline:
471
+ yield a * 6
472
+
473
+ def main():
474
+ pipeline = range(1,4)
475
+ pipeline = subtract(pipeline)
476
+ pipeline = (a + 9 for a in pipeline)
477
+ pipeline = multiply(pipeline)
478
+ return sum(pipeline)
479
+
480
+ tree = invo_tree.blocking()
481
+ import types
482
+ tree.to_string[types.GeneratorType] = lambda x: 'generator' # short name for generators
483
+ tree.to_string[type(iter(range(0)))] = lambda x: 'range_iterator' # short name for range_iterator
484
+ print( tree(main) )
485
+ ```
486
+ ![generator_pipeline.gif](https://raw.githubusercontent.com/bterwijn/invocation_tree/main/images/generator_pipeline.gif)
487
+ ```
488
+ 144
489
+ ```
490
+ Note that the generators are lazy but the `sum()` function is not, and that is what is pulling the values through the pipeline one at the time.
491
+
492
+ ## Itertools ##
493
+ The pythonic (or idiomatic) way of programming in Python is not to use raw for-loops but to use iterables, generators and [itertools](https://docs.python.org/3/library/itertools.html) functions instead. See for a short introduction:
494
+
495
+ [![Idiomatic Python: The `itertools` Module](https://img.youtube.com/vi/guEDsBshGfI/maxresdefault.jpg)](https://www.youtube.com/watch?v=guEDsBshGfI)
496
+
497
+ Whenever you write a for-loop, finish it and make it work correctly, but afterwards see of if you can rewrite it with generators and itertools functions. Then in time you will find you can think in terms of generators and itertools from the start. This can make your code shorter, more expressive, easier to change, use less memory, faster, and generally more correct.
498
+
499
+ # Configuration #
500
+ These invocation_tree configurations are available for an `Invocation_Tree` objects:
501
+
502
+ ```python
503
+ tree = invo_tree.Invocation_Tree()
504
+ ```
505
+
506
+ - **tree.filename** : str
507
+ - filename to save the tree to, defaults to 'tree.pdf'
508
+ - **tree.show** : bool
509
+ - if `True` the default application is open to view 'tree.filename'
510
+ - **tree.block** : bool
511
+ - if `True` program execution is blocked after the tree is saved
512
+ - **tree.src_loc** : bool
513
+ - if `True` the source location is printed when blocking
514
+ - **tree.each_line** : bool
515
+ - if `True` each line of the program is stepped through
516
+ - **tree.max_string_len** : int
517
+ - the maximum string length, only the end is shown of longer strings
518
+ - **tree.gifcount** : int
519
+ - if `>=0` the out filename is numbered for animated gif making
520
+ - **tree.indent** : string
521
+ - the string used for identing the local variables
522
+ - **tree.color_active** : string
523
+ - HTML color for active function
524
+ - **tree.color_paused*** : string
525
+ - HTML color for paused functions
526
+ - **tree.color_returned***: string
527
+ - HTML color for returned functions
528
+ - **tree.hide** : set()
529
+ - set of all variables names that are not shown in the tree
530
+ - **tree.to_string** : dict[str, fun]
531
+ - mapping from type/name to a to_string() function for custom printing of values
532
+
533
+ For convenience we provide these functions to set common configurations:
534
+
535
+ - **invo_tree.blocking(filename)**, blocks on function call and return
536
+ - **invo_tree.blocking_each_change(filename)**, blocks on each change of value
537
+ - **invo_tree.debugger(filename)**, non-blocking for use in debugger tool (open &lt;filename&gt; manually)
538
+ - **invo_tree.gif(filename)**, generates many output files on function call and return for gif creation
539
+ - **invo_tree.gif_each_change(filename)**, generates many output files on each change of value for gif creation
540
+ - **invo_tree.non_blocking(filename)**, non blocking on each function call and return
541
+
542
+ # Troubleshooting #
543
+ - Adobe Acrobat Reader [doesn't refresh a PDF file](https://superuser.com/questions/337011/windows-pdf-viewer-that-auto-refreshes-pdf-when-compiling-with-pdflatex) when it changes on disk and blocks updates which results in an `Could not open 'somefile.pdf' for writing : Permission denied` error. One solution is to install a PDF reader that does refresh ([Evince](https://www.fosshub.com/Evince.html), [Okular](https://okular.kde.org/), [SumatraPDF](https://www.sumatrapdfreader.org/), ...) and set it as the default PDF reader. Another solution is to save the tree to a different [Graphviz Output Format](https://graphviz.org/docs/outputs/).
544
+
545
+ ## Memory_Graph Package ##
546
+ The [invocation_tree](https://pypi.org/project/invocation-tree/) package visualizes function calls at different moments in time. If instead you want a detailed visualization of your data at the current time, check out the [memory_graph](https://pypi.org/project/memory-graph/) package.