invocation-tree 0.0.33__tar.gz → 0.0.35__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.
- {invocation_tree-0.0.33/invocation_tree.egg-info → invocation_tree-0.0.35}/PKG-INFO +32 -7
- {invocation_tree-0.0.33 → invocation_tree-0.0.35}/README.md +31 -6
- {invocation_tree-0.0.33 → invocation_tree-0.0.35}/invocation_tree/__init__.py +108 -9
- {invocation_tree-0.0.33 → invocation_tree-0.0.35/invocation_tree.egg-info}/PKG-INFO +32 -7
- invocation_tree-0.0.35/invocation_tree.egg-info/SOURCES.txt +10 -0
- {invocation_tree-0.0.33 → invocation_tree-0.0.35}/pyproject.toml +1 -1
- invocation_tree-0.0.33/MANIFEST.in +0 -1
- invocation_tree-0.0.33/images/compute.gif +0 -0
- invocation_tree-0.0.33/images/compute.py +0 -22
- invocation_tree-0.0.33/images/create_gif.sh +0 -25
- invocation_tree-0.0.33/images/create_images.sh +0 -47
- invocation_tree-0.0.33/images/draw_graph.py +0 -32
- invocation_tree-0.0.33/images/draw_graph_d_x.py +0 -36
- invocation_tree-0.0.33/images/edges_big.out +0 -1
- invocation_tree-0.0.33/images/edges_big_d_x.out +0 -1
- invocation_tree-0.0.33/images/edges_small.out +0 -1
- invocation_tree-0.0.33/images/factorial.gif +0 -0
- invocation_tree-0.0.33/images/factorial.py +0 -9
- invocation_tree-0.0.33/images/graph.py +0 -35
- invocation_tree-0.0.33/images/graph_big.png +0 -0
- invocation_tree-0.0.33/images/graph_big_d_x.png +0 -0
- invocation_tree-0.0.33/images/graph_small.png +0 -0
- invocation_tree-0.0.33/images/jugs.png +0 -0
- invocation_tree-0.0.33/images/jugs_depth_first.py +0 -38
- invocation_tree-0.0.33/images/perms_LR3.png +0 -0
- invocation_tree-0.0.33/images/permutations.gif +0 -0
- invocation_tree-0.0.33/images/permutations.py +0 -11
- invocation_tree-0.0.33/images/permutations_collect.gif +0 -0
- invocation_tree-0.0.33/images/permutations_collect.py +0 -13
- invocation_tree-0.0.33/images/permutations_dot.py +0 -30
- invocation_tree-0.0.33/images/permutations_neighbor.gif +0 -0
- invocation_tree-0.0.33/images/permutations_neighbor.py +0 -12
- invocation_tree-0.0.33/images/permutations_return.gif +0 -0
- invocation_tree-0.0.33/images/permutations_return.py +0 -13
- invocation_tree-0.0.33/images/permutations_vscode.gif +0 -0
- invocation_tree-0.0.33/images/print_all_paths.py +0 -26
- invocation_tree-0.0.33/images/print_all_paths_of_length.py +0 -27
- invocation_tree-0.0.33/images/print_all_paths_via.py +0 -32
- invocation_tree-0.0.33/images/quick_sort.gif +0 -0
- invocation_tree-0.0.33/images/quick_sort.py +0 -16
- invocation_tree-0.0.33/images/students.gif +0 -0
- invocation_tree-0.0.33/images/students.py +0 -28
- invocation_tree-0.0.33/images/vscode.png +0 -0
- invocation_tree-0.0.33/invocation_tree.egg-info/SOURCES.txt +0 -47
- {invocation_tree-0.0.33 → invocation_tree-0.0.35}/LICENSE.txt +0 -0
- {invocation_tree-0.0.33 → invocation_tree-0.0.35}/invocation_tree/regex_set.py +0 -0
- {invocation_tree-0.0.33 → invocation_tree-0.0.35}/invocation_tree.egg-info/dependency_links.txt +0 -0
- {invocation_tree-0.0.33 → invocation_tree-0.0.35}/invocation_tree.egg-info/requires.txt +0 -0
- {invocation_tree-0.0.33 → invocation_tree-0.0.35}/invocation_tree.egg-info/top_level.txt +0 -0
- {invocation_tree-0.0.33 → invocation_tree-0.0.35}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: invocation_tree
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.35
|
|
4
4
|
Summary: Generates an invocation tree of functions calls.
|
|
5
5
|
Author-email: Bas Terwijn <bterwijn@gmail.com>
|
|
6
6
|
License-Expression: BSD-2-Clause
|
|
@@ -32,6 +32,8 @@ Run a live demo in the 👉 [**Invocation Tree Web Debugger**](https://invocatio
|
|
|
32
32
|
- shows the invocation tree (call tree) of a program **in real time**
|
|
33
33
|
- helps to **understand recursion** and its depth-first nature
|
|
34
34
|
|
|
35
|
+
The new `@ivt.show` [decorator interface](#decorator) now allows to scale to application in **production code**.
|
|
36
|
+
|
|
35
37
|
# Topics #
|
|
36
38
|
|
|
37
39
|
[Iteration and Recursion](#iteration-and-recursion)
|
|
@@ -67,7 +69,7 @@ ___
|
|
|
67
69
|
|
|
68
70
|
# Iteration and Recursion #
|
|
69
71
|
|
|
70
|
-
|
|
72
|
+
Repetition can be implemented with **iteration** and **recursion**. As an example we compute the factorial of 4.
|
|
71
73
|
|
|
72
74
|
``` python
|
|
73
75
|
import math
|
|
@@ -119,7 +121,7 @@ factorial(4) = 4 * factorial(3)
|
|
|
119
121
|
= 24
|
|
120
122
|
```
|
|
121
123
|
|
|
122
|
-
To better understand what is going on when we run the program we can use invocation_tree:
|
|
124
|
+
To better understand what is going on when we run the program, we can use invocation_tree:
|
|
123
125
|
|
|
124
126
|
```python
|
|
125
127
|
import invocation_tree as ivt
|
|
@@ -133,7 +135,7 @@ tree = ivt.blocking() # block and wait for <Enter> key press
|
|
|
133
135
|
tree(factorial, 4) # call function 'factorial' with argument '4'
|
|
134
136
|
```
|
|
135
137
|
|
|
136
|
-
to graph the function invocations. Run this program and press <Enter> to step through program execution.
|
|
138
|
+
to graph the function invocations where function calls happen on the way down, and where results combine on the way up when a function returns. Run this program and press <Enter> to step through program execution.
|
|
137
139
|
|
|
138
140
|

|
|
139
141
|
|
|
@@ -147,7 +149,7 @@ Each node in the invocation tree represents a function call, and the node's colo
|
|
|
147
149
|
|
|
148
150
|
For every function call, the package displays its **local variables** and **return value**. Changes to the values of these variables over time are highlighted using bold text and gray shading to make them easier to track.
|
|
149
151
|
|
|
150
|
-
With recursion we often use a divide and conquer strategy,
|
|
152
|
+
With recursion we often use a divide and conquer strategy, splitting the problem in subproblems that are easier to solve. With factorial we split `factorial(4)` in a `4` and `factorial(3)` subproblem.
|
|
151
153
|
|
|
152
154
|
**exercise1:** Use recursions to compute the sum of all the values in a list (hint: split for example the list `[1, 2, 3, ...]` in head `1` and tail `[2, 3, ...]`).
|
|
153
155
|
```python
|
|
@@ -170,7 +172,7 @@ def binary(decimal):
|
|
|
170
172
|
print( binary(22) ) # [1, 0, 1, 1, 0]
|
|
171
173
|
```
|
|
172
174
|
|
|
173
|
-
Checking that `[1, 0, 1, 1, 0]` is the correct binary
|
|
175
|
+
Checking that `[1, 0, 1, 1, 0]` is the correct binary representation for decimal 22:
|
|
174
176
|
|
|
175
177
|
| | 2<sup>4</sup> | 2<sup>3</sup> | 2<sup>2</sup> | 2<sup>1</sup> | 2<sup>0</sup> | |
|
|
176
178
|
|------:|:-------------:|:-------------:|:-------------:|:-------------:|:-------------:|:-------------:|
|
|
@@ -187,7 +189,7 @@ We can use recursion to compute all permutation of a number of elements with rep
|
|
|
187
189
|
|
|
188
190
|

|
|
189
191
|
|
|
190
|
-
This can be implemented recursively like:
|
|
192
|
+
This can be implemented recursively, using a divide and conquer strategy, like:
|
|
191
193
|
|
|
192
194
|
```python
|
|
193
195
|
import invocation_tree as ivt
|
|
@@ -510,6 +512,7 @@ $ python jugs_breadth_first.py 51 3,5,34,107
|
|
|
510
512
|
# Configuration #
|
|
511
513
|
The Invocation Tree Web Debugger gives examples of the [most important configurations](https://invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/config.py).
|
|
512
514
|
|
|
515
|
+
|
|
513
516
|
## Hidding ##
|
|
514
517
|
It can be useful to hide certian variables or functions to avoid unnecessary complexity. This can be done with:
|
|
515
518
|
|
|
@@ -543,6 +546,28 @@ tree.ignore_calls.add(r're:namespace\..*')
|
|
|
543
546
|
|
|
544
547
|
ignores all function of `namespace`.
|
|
545
548
|
|
|
549
|
+
## Decorator ##
|
|
550
|
+
|
|
551
|
+
A better way to hide functions is to use the `@ivt.show` decorator on only the functions you want to graph. The decorator uses the global `ivt.decorator_tree` tree.
|
|
552
|
+
|
|
553
|
+
```python
|
|
554
|
+
import invocation_tree as ivt
|
|
555
|
+
ivt.decorator_tree = ivt.blocking() # set tree used by decorator
|
|
556
|
+
#ivt.decorator_tree = ivt.blocking_each_change() # block at each change, but much slower
|
|
557
|
+
|
|
558
|
+
@ivt.show # use decorator to select which functions to graph
|
|
559
|
+
def permutations(elements, perm, n):
|
|
560
|
+
if n == 0:
|
|
561
|
+
print(perm)
|
|
562
|
+
else:
|
|
563
|
+
for element in elements:
|
|
564
|
+
perm.append(element)
|
|
565
|
+
permutations(elements, perm, n-1)
|
|
566
|
+
perm.pop()
|
|
567
|
+
|
|
568
|
+
permutations( 'LR', [], 3) # all permutations of L and R of length 3
|
|
569
|
+
```
|
|
570
|
+
|
|
546
571
|
## Blocking ##
|
|
547
572
|
|
|
548
573
|
For convenience we provide these functions to set common configurations:
|
|
@@ -12,6 +12,8 @@ Run a live demo in the 👉 [**Invocation Tree Web Debugger**](https://invocatio
|
|
|
12
12
|
- shows the invocation tree (call tree) of a program **in real time**
|
|
13
13
|
- helps to **understand recursion** and its depth-first nature
|
|
14
14
|
|
|
15
|
+
The new `@ivt.show` [decorator interface](#decorator) now allows to scale to application in **production code**.
|
|
16
|
+
|
|
15
17
|
# Topics #
|
|
16
18
|
|
|
17
19
|
[Iteration and Recursion](#iteration-and-recursion)
|
|
@@ -47,7 +49,7 @@ ___
|
|
|
47
49
|
|
|
48
50
|
# Iteration and Recursion #
|
|
49
51
|
|
|
50
|
-
|
|
52
|
+
Repetition can be implemented with **iteration** and **recursion**. As an example we compute the factorial of 4.
|
|
51
53
|
|
|
52
54
|
``` python
|
|
53
55
|
import math
|
|
@@ -99,7 +101,7 @@ factorial(4) = 4 * factorial(3)
|
|
|
99
101
|
= 24
|
|
100
102
|
```
|
|
101
103
|
|
|
102
|
-
To better understand what is going on when we run the program we can use invocation_tree:
|
|
104
|
+
To better understand what is going on when we run the program, we can use invocation_tree:
|
|
103
105
|
|
|
104
106
|
```python
|
|
105
107
|
import invocation_tree as ivt
|
|
@@ -113,7 +115,7 @@ tree = ivt.blocking() # block and wait for <Enter> key press
|
|
|
113
115
|
tree(factorial, 4) # call function 'factorial' with argument '4'
|
|
114
116
|
```
|
|
115
117
|
|
|
116
|
-
to graph the function invocations. Run this program and press <Enter> to step through program execution.
|
|
118
|
+
to graph the function invocations where function calls happen on the way down, and where results combine on the way up when a function returns. Run this program and press <Enter> to step through program execution.
|
|
117
119
|
|
|
118
120
|

|
|
119
121
|
|
|
@@ -127,7 +129,7 @@ Each node in the invocation tree represents a function call, and the node's colo
|
|
|
127
129
|
|
|
128
130
|
For every function call, the package displays its **local variables** and **return value**. Changes to the values of these variables over time are highlighted using bold text and gray shading to make them easier to track.
|
|
129
131
|
|
|
130
|
-
With recursion we often use a divide and conquer strategy,
|
|
132
|
+
With recursion we often use a divide and conquer strategy, splitting the problem in subproblems that are easier to solve. With factorial we split `factorial(4)` in a `4` and `factorial(3)` subproblem.
|
|
131
133
|
|
|
132
134
|
**exercise1:** Use recursions to compute the sum of all the values in a list (hint: split for example the list `[1, 2, 3, ...]` in head `1` and tail `[2, 3, ...]`).
|
|
133
135
|
```python
|
|
@@ -150,7 +152,7 @@ def binary(decimal):
|
|
|
150
152
|
print( binary(22) ) # [1, 0, 1, 1, 0]
|
|
151
153
|
```
|
|
152
154
|
|
|
153
|
-
Checking that `[1, 0, 1, 1, 0]` is the correct binary
|
|
155
|
+
Checking that `[1, 0, 1, 1, 0]` is the correct binary representation for decimal 22:
|
|
154
156
|
|
|
155
157
|
| | 2<sup>4</sup> | 2<sup>3</sup> | 2<sup>2</sup> | 2<sup>1</sup> | 2<sup>0</sup> | |
|
|
156
158
|
|------:|:-------------:|:-------------:|:-------------:|:-------------:|:-------------:|:-------------:|
|
|
@@ -167,7 +169,7 @@ We can use recursion to compute all permutation of a number of elements with rep
|
|
|
167
169
|
|
|
168
170
|

|
|
169
171
|
|
|
170
|
-
This can be implemented recursively like:
|
|
172
|
+
This can be implemented recursively, using a divide and conquer strategy, like:
|
|
171
173
|
|
|
172
174
|
```python
|
|
173
175
|
import invocation_tree as ivt
|
|
@@ -490,6 +492,7 @@ $ python jugs_breadth_first.py 51 3,5,34,107
|
|
|
490
492
|
# Configuration #
|
|
491
493
|
The Invocation Tree Web Debugger gives examples of the [most important configurations](https://invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/config.py).
|
|
492
494
|
|
|
495
|
+
|
|
493
496
|
## Hidding ##
|
|
494
497
|
It can be useful to hide certian variables or functions to avoid unnecessary complexity. This can be done with:
|
|
495
498
|
|
|
@@ -523,6 +526,28 @@ tree.ignore_calls.add(r're:namespace\..*')
|
|
|
523
526
|
|
|
524
527
|
ignores all function of `namespace`.
|
|
525
528
|
|
|
529
|
+
## Decorator ##
|
|
530
|
+
|
|
531
|
+
A better way to hide functions is to use the `@ivt.show` decorator on only the functions you want to graph. The decorator uses the global `ivt.decorator_tree` tree.
|
|
532
|
+
|
|
533
|
+
```python
|
|
534
|
+
import invocation_tree as ivt
|
|
535
|
+
ivt.decorator_tree = ivt.blocking() # set tree used by decorator
|
|
536
|
+
#ivt.decorator_tree = ivt.blocking_each_change() # block at each change, but much slower
|
|
537
|
+
|
|
538
|
+
@ivt.show # use decorator to select which functions to graph
|
|
539
|
+
def permutations(elements, perm, n):
|
|
540
|
+
if n == 0:
|
|
541
|
+
print(perm)
|
|
542
|
+
else:
|
|
543
|
+
for element in elements:
|
|
544
|
+
perm.append(element)
|
|
545
|
+
permutations(elements, perm, n-1)
|
|
546
|
+
perm.pop()
|
|
547
|
+
|
|
548
|
+
permutations( 'LR', [], 3) # all permutations of L and R of length 3
|
|
549
|
+
```
|
|
550
|
+
|
|
526
551
|
## Blocking ##
|
|
527
552
|
|
|
528
553
|
For convenience we provide these functions to set common configurations:
|
|
@@ -6,10 +6,11 @@ from graphviz import Digraph
|
|
|
6
6
|
import html
|
|
7
7
|
import sys
|
|
8
8
|
import difflib
|
|
9
|
+
import functools
|
|
9
10
|
|
|
10
11
|
import invocation_tree.regex_set as regset
|
|
11
12
|
|
|
12
|
-
__version__ = "0.0.
|
|
13
|
+
__version__ = "0.0.35"
|
|
13
14
|
__author__ = 'Bas Terwijn'
|
|
14
15
|
|
|
15
16
|
def highlight_diff(str1, str2):
|
|
@@ -134,7 +135,7 @@ class Invocation_Tree:
|
|
|
134
135
|
sys.settrace(self.prev_global_tracer)
|
|
135
136
|
return result
|
|
136
137
|
|
|
137
|
-
def value_to_string(self, key, value
|
|
138
|
+
def value_to_string(self, key, value):
|
|
138
139
|
try:
|
|
139
140
|
if id(value) in self.to_string:
|
|
140
141
|
val_str = self.to_string[id(value)](value)
|
|
@@ -143,18 +144,21 @@ class Invocation_Tree:
|
|
|
143
144
|
elif type(value) in self.to_string:
|
|
144
145
|
val_str = self.to_string[type(value)](value)
|
|
145
146
|
else:
|
|
146
|
-
val_str =
|
|
147
|
+
val_str = str(value)
|
|
147
148
|
except Exception as e:
|
|
148
149
|
val_str = '<not-string-convertable>'
|
|
149
150
|
if len(val_str) > self.max_string_len:
|
|
150
151
|
val_str = '...'+val_str[-self.max_string_len:]
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
152
|
+
result = html.escape(val_str)
|
|
153
|
+
if '\n' in result:
|
|
154
|
+
result = '<BR/>' + result.replace('\n', '<BR/>') # use HTML line breaks
|
|
155
|
+
return result
|
|
156
|
+
|
|
157
|
+
def get_hightlighted_content(self, tree_node, key, value, use_old_content=False):
|
|
154
158
|
if use_old_content and key in tree_node.strings:
|
|
155
159
|
return tree_node.strings[key]
|
|
156
160
|
is_highlighted = False
|
|
157
|
-
content = self.value_to_string(key, value
|
|
161
|
+
content = self.value_to_string(key, value)
|
|
158
162
|
if key in tree_node.strings:
|
|
159
163
|
use_old_content = tree_node.strings[key]
|
|
160
164
|
hightlighted_content, is_highlighted = highlight_diff(use_old_content, content)
|
|
@@ -192,14 +196,14 @@ class Invocation_Tree:
|
|
|
192
196
|
if filter_variables(var,val) and not self.regset_hide_vars.match(val_name, self.hide_vars):
|
|
193
197
|
table += '</TR>\n <TR>'
|
|
194
198
|
hightlighted_var = self.get_hightlighted_content(tree_node, var_name, var, use_old_content)
|
|
195
|
-
hightlighted_val = self.get_hightlighted_content(tree_node, val_name, val, use_old_content
|
|
199
|
+
hightlighted_val = self.get_hightlighted_content(tree_node, val_name, val, use_old_content)
|
|
196
200
|
hightlighted_content = self.indent + hightlighted_var + ': ' + hightlighted_val
|
|
197
201
|
table += '<TD ALIGN="left">'+ hightlighted_content +'</TD>'
|
|
198
202
|
if is_returned:
|
|
199
203
|
return_name = class_fun_name+'.return'
|
|
200
204
|
if not self.regset_hide_vars.match(return_name, self.hide_vars):
|
|
201
205
|
table += '</TR>\n <TR>'
|
|
202
|
-
hightlighted_content = self.get_hightlighted_content(tree_node, return_name, return_value, use_old_content
|
|
206
|
+
hightlighted_content = self.get_hightlighted_content(tree_node, return_name, return_value, use_old_content)
|
|
203
207
|
table += '<TD ALIGN="left">'+ 'return ' + hightlighted_content +'</TD>'
|
|
204
208
|
table += '</TR>\n</TABLE>>'
|
|
205
209
|
return table
|
|
@@ -349,6 +353,11 @@ class Invocation_Tree:
|
|
|
349
353
|
return None # stop tracing if debugger stopped tracing
|
|
350
354
|
return local_multiplexer
|
|
351
355
|
|
|
356
|
+
# Optimize: disable line tracing if not needed
|
|
357
|
+
if not self.each_line:
|
|
358
|
+
frame.f_trace_lines = False
|
|
359
|
+
frame.f_trace_opcodes = False
|
|
360
|
+
|
|
352
361
|
return local_multiplexer
|
|
353
362
|
|
|
354
363
|
def blocking(filename='tree.pdf'):
|
|
@@ -371,3 +380,93 @@ def gif_each_change(filename='tree.png'):
|
|
|
371
380
|
|
|
372
381
|
def non_blocking(filename='tree.pdf'):
|
|
373
382
|
return Invocation_Tree(filename=filename, block=False)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
# ------ decorator ------
|
|
386
|
+
|
|
387
|
+
decorator_tree = None
|
|
388
|
+
|
|
389
|
+
def show(fun):
|
|
390
|
+
""" decorate a function with @ivt.show to visualize its invocation tree """
|
|
391
|
+
target_code = fun.__code__
|
|
392
|
+
|
|
393
|
+
@functools.wraps(fun)
|
|
394
|
+
def wrapper_ivt_deco(*args, **kwargs):
|
|
395
|
+
global decorator_tree
|
|
396
|
+
if decorator_tree is None:
|
|
397
|
+
return fun(*args, **kwargs)
|
|
398
|
+
prev_tracer = sys.gettrace()
|
|
399
|
+
active_depth = 0 # handles recursion of fun
|
|
400
|
+
|
|
401
|
+
def tracer(frame, event, arg):
|
|
402
|
+
nonlocal active_depth
|
|
403
|
+
|
|
404
|
+
if event == "exception":
|
|
405
|
+
exc_type, exc, tb = arg
|
|
406
|
+
if exc_type is KeyboardInterrupt:
|
|
407
|
+
# ensure we stop tracing before bailing out
|
|
408
|
+
sys.settrace(prev_tracer)
|
|
409
|
+
raise exc.with_traceback(tb)
|
|
410
|
+
|
|
411
|
+
# Check if this is our target function
|
|
412
|
+
if frame.f_code is target_code:
|
|
413
|
+
if event == "call":
|
|
414
|
+
active_depth += 1
|
|
415
|
+
if active_depth == 1:
|
|
416
|
+
decorator_tree.trace(frame, event, None)
|
|
417
|
+
# Optimize: disable line tracing if not needed
|
|
418
|
+
if not getattr(decorator_tree, "each_line", False):
|
|
419
|
+
frame.f_trace_lines = False
|
|
420
|
+
frame.f_trace_opcodes = False
|
|
421
|
+
elif event == "line":
|
|
422
|
+
if active_depth == 1 and getattr(decorator_tree, "each_line", False):
|
|
423
|
+
decorator_tree.trace(frame, event, None)
|
|
424
|
+
elif event == "return":
|
|
425
|
+
if active_depth == 1:
|
|
426
|
+
decorator_tree.trace(frame, event, arg)
|
|
427
|
+
active_depth -= 1
|
|
428
|
+
return tracer
|
|
429
|
+
|
|
430
|
+
# Not our target: chain to previous tracer or return None to stop tracing this frame
|
|
431
|
+
if prev_tracer is not None:
|
|
432
|
+
return prev_tracer(frame, event, arg)
|
|
433
|
+
|
|
434
|
+
return None # Don't trace non-target frames
|
|
435
|
+
|
|
436
|
+
sys.settrace(tracer)
|
|
437
|
+
try:
|
|
438
|
+
return fun(*args, **kwargs)
|
|
439
|
+
finally:
|
|
440
|
+
sys.settrace(prev_tracer)
|
|
441
|
+
return wrapper_ivt_deco
|
|
442
|
+
|
|
443
|
+
# faster decorator but no 'line' events
|
|
444
|
+
def decorate_profile(fun):
|
|
445
|
+
target_code = fun.__code__
|
|
446
|
+
|
|
447
|
+
@functools.wraps(fun)
|
|
448
|
+
def wrapper_ivt_deco(*args, **kwargs):
|
|
449
|
+
global decorator_tree
|
|
450
|
+
if decorator_tree is None:
|
|
451
|
+
return fun(*args, **kwargs)
|
|
452
|
+
prev_profiler = sys.getprofile()
|
|
453
|
+
active_depth = 0 # handles recursion of fun
|
|
454
|
+
|
|
455
|
+
def profiler(frame, event, arg):
|
|
456
|
+
nonlocal active_depth
|
|
457
|
+
if frame.f_code is target_code:
|
|
458
|
+
if event == "call":
|
|
459
|
+
active_depth += 1
|
|
460
|
+
if active_depth == 1:
|
|
461
|
+
decorator_tree.trace(frame, event, None)
|
|
462
|
+
elif event == "return":
|
|
463
|
+
if active_depth == 1:
|
|
464
|
+
decorator_tree.trace(frame, event, arg)
|
|
465
|
+
active_depth -= 1
|
|
466
|
+
|
|
467
|
+
sys.setprofile(profiler)
|
|
468
|
+
try:
|
|
469
|
+
return fun(*args, **kwargs)
|
|
470
|
+
finally:
|
|
471
|
+
sys.setprofile(prev_profiler)
|
|
472
|
+
return wrapper_ivt_deco
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: invocation_tree
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.35
|
|
4
4
|
Summary: Generates an invocation tree of functions calls.
|
|
5
5
|
Author-email: Bas Terwijn <bterwijn@gmail.com>
|
|
6
6
|
License-Expression: BSD-2-Clause
|
|
@@ -32,6 +32,8 @@ Run a live demo in the 👉 [**Invocation Tree Web Debugger**](https://invocatio
|
|
|
32
32
|
- shows the invocation tree (call tree) of a program **in real time**
|
|
33
33
|
- helps to **understand recursion** and its depth-first nature
|
|
34
34
|
|
|
35
|
+
The new `@ivt.show` [decorator interface](#decorator) now allows to scale to application in **production code**.
|
|
36
|
+
|
|
35
37
|
# Topics #
|
|
36
38
|
|
|
37
39
|
[Iteration and Recursion](#iteration-and-recursion)
|
|
@@ -67,7 +69,7 @@ ___
|
|
|
67
69
|
|
|
68
70
|
# Iteration and Recursion #
|
|
69
71
|
|
|
70
|
-
|
|
72
|
+
Repetition can be implemented with **iteration** and **recursion**. As an example we compute the factorial of 4.
|
|
71
73
|
|
|
72
74
|
``` python
|
|
73
75
|
import math
|
|
@@ -119,7 +121,7 @@ factorial(4) = 4 * factorial(3)
|
|
|
119
121
|
= 24
|
|
120
122
|
```
|
|
121
123
|
|
|
122
|
-
To better understand what is going on when we run the program we can use invocation_tree:
|
|
124
|
+
To better understand what is going on when we run the program, we can use invocation_tree:
|
|
123
125
|
|
|
124
126
|
```python
|
|
125
127
|
import invocation_tree as ivt
|
|
@@ -133,7 +135,7 @@ tree = ivt.blocking() # block and wait for <Enter> key press
|
|
|
133
135
|
tree(factorial, 4) # call function 'factorial' with argument '4'
|
|
134
136
|
```
|
|
135
137
|
|
|
136
|
-
to graph the function invocations. Run this program and press <Enter> to step through program execution.
|
|
138
|
+
to graph the function invocations where function calls happen on the way down, and where results combine on the way up when a function returns. Run this program and press <Enter> to step through program execution.
|
|
137
139
|
|
|
138
140
|

|
|
139
141
|
|
|
@@ -147,7 +149,7 @@ Each node in the invocation tree represents a function call, and the node's colo
|
|
|
147
149
|
|
|
148
150
|
For every function call, the package displays its **local variables** and **return value**. Changes to the values of these variables over time are highlighted using bold text and gray shading to make them easier to track.
|
|
149
151
|
|
|
150
|
-
With recursion we often use a divide and conquer strategy,
|
|
152
|
+
With recursion we often use a divide and conquer strategy, splitting the problem in subproblems that are easier to solve. With factorial we split `factorial(4)` in a `4` and `factorial(3)` subproblem.
|
|
151
153
|
|
|
152
154
|
**exercise1:** Use recursions to compute the sum of all the values in a list (hint: split for example the list `[1, 2, 3, ...]` in head `1` and tail `[2, 3, ...]`).
|
|
153
155
|
```python
|
|
@@ -170,7 +172,7 @@ def binary(decimal):
|
|
|
170
172
|
print( binary(22) ) # [1, 0, 1, 1, 0]
|
|
171
173
|
```
|
|
172
174
|
|
|
173
|
-
Checking that `[1, 0, 1, 1, 0]` is the correct binary
|
|
175
|
+
Checking that `[1, 0, 1, 1, 0]` is the correct binary representation for decimal 22:
|
|
174
176
|
|
|
175
177
|
| | 2<sup>4</sup> | 2<sup>3</sup> | 2<sup>2</sup> | 2<sup>1</sup> | 2<sup>0</sup> | |
|
|
176
178
|
|------:|:-------------:|:-------------:|:-------------:|:-------------:|:-------------:|:-------------:|
|
|
@@ -187,7 +189,7 @@ We can use recursion to compute all permutation of a number of elements with rep
|
|
|
187
189
|
|
|
188
190
|

|
|
189
191
|
|
|
190
|
-
This can be implemented recursively like:
|
|
192
|
+
This can be implemented recursively, using a divide and conquer strategy, like:
|
|
191
193
|
|
|
192
194
|
```python
|
|
193
195
|
import invocation_tree as ivt
|
|
@@ -510,6 +512,7 @@ $ python jugs_breadth_first.py 51 3,5,34,107
|
|
|
510
512
|
# Configuration #
|
|
511
513
|
The Invocation Tree Web Debugger gives examples of the [most important configurations](https://invocation-tree.com/#codeurl=https://raw.githubusercontent.com/bterwijn/invocation_tree/refs/heads/main/src/config.py).
|
|
512
514
|
|
|
515
|
+
|
|
513
516
|
## Hidding ##
|
|
514
517
|
It can be useful to hide certian variables or functions to avoid unnecessary complexity. This can be done with:
|
|
515
518
|
|
|
@@ -543,6 +546,28 @@ tree.ignore_calls.add(r're:namespace\..*')
|
|
|
543
546
|
|
|
544
547
|
ignores all function of `namespace`.
|
|
545
548
|
|
|
549
|
+
## Decorator ##
|
|
550
|
+
|
|
551
|
+
A better way to hide functions is to use the `@ivt.show` decorator on only the functions you want to graph. The decorator uses the global `ivt.decorator_tree` tree.
|
|
552
|
+
|
|
553
|
+
```python
|
|
554
|
+
import invocation_tree as ivt
|
|
555
|
+
ivt.decorator_tree = ivt.blocking() # set tree used by decorator
|
|
556
|
+
#ivt.decorator_tree = ivt.blocking_each_change() # block at each change, but much slower
|
|
557
|
+
|
|
558
|
+
@ivt.show # use decorator to select which functions to graph
|
|
559
|
+
def permutations(elements, perm, n):
|
|
560
|
+
if n == 0:
|
|
561
|
+
print(perm)
|
|
562
|
+
else:
|
|
563
|
+
for element in elements:
|
|
564
|
+
perm.append(element)
|
|
565
|
+
permutations(elements, perm, n-1)
|
|
566
|
+
perm.pop()
|
|
567
|
+
|
|
568
|
+
permutations( 'LR', [], 3) # all permutations of L and R of length 3
|
|
569
|
+
```
|
|
570
|
+
|
|
546
571
|
## Blocking ##
|
|
547
572
|
|
|
548
573
|
For convenience we provide these functions to set common configurations:
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
LICENSE.txt
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
invocation_tree/__init__.py
|
|
5
|
+
invocation_tree/regex_set.py
|
|
6
|
+
invocation_tree.egg-info/PKG-INFO
|
|
7
|
+
invocation_tree.egg-info/SOURCES.txt
|
|
8
|
+
invocation_tree.egg-info/dependency_links.txt
|
|
9
|
+
invocation_tree.egg-info/requires.txt
|
|
10
|
+
invocation_tree.egg-info/top_level.txt
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
recursive-include images/ *
|
|
Binary file
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import invocation_tree as ivt
|
|
2
|
-
|
|
3
|
-
def main():
|
|
4
|
-
a = 1
|
|
5
|
-
a = expression(a)
|
|
6
|
-
return multiply(a, 6)
|
|
7
|
-
|
|
8
|
-
def expression(a):
|
|
9
|
-
a = subtract(a, 3)
|
|
10
|
-
return add(a, 9)
|
|
11
|
-
|
|
12
|
-
def subtract(a, b):
|
|
13
|
-
return a - b
|
|
14
|
-
|
|
15
|
-
def add(a, b):
|
|
16
|
-
return a + b
|
|
17
|
-
|
|
18
|
-
def multiply(a, b):
|
|
19
|
-
return a * b
|
|
20
|
-
|
|
21
|
-
tree = ivt.gif('compute.png')
|
|
22
|
-
print( tree(main) )
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
#
|
|
3
|
-
# install:
|
|
4
|
-
#
|
|
5
|
-
# sudo apt install imagemagick
|
|
6
|
-
|
|
7
|
-
name="$1"
|
|
8
|
-
files=$(ls -v $name*.png | tr '\n' ' ')
|
|
9
|
-
echo "creating gif with:"
|
|
10
|
-
echo "$files"
|
|
11
|
-
|
|
12
|
-
largest_size=$(identify -format "%H %Wx%H %f\n" $name*.png | sort -nr | head -n1| awk '{print $2}')
|
|
13
|
-
echo "largest_size: $largest_size"
|
|
14
|
-
|
|
15
|
-
echo "resizing images"
|
|
16
|
-
mogrify -resize $largest_size -background white -gravity North -extent $largest_size $files
|
|
17
|
-
echo "creating file: $name.gif"
|
|
18
|
-
convert -delay 150 -dither None -loop 0 $files $name.gif
|
|
19
|
-
|
|
20
|
-
if [ "$2" = "-d" ]; then
|
|
21
|
-
echo "deleting: $files"
|
|
22
|
-
rm $files
|
|
23
|
-
fi
|
|
24
|
-
|
|
25
|
-
echo "done"
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
#rm -f compute*.png
|
|
3
|
-
#python compute.py
|
|
4
|
-
#rm -f compute0.png
|
|
5
|
-
#bash create_gif.sh compute -d
|
|
6
|
-
|
|
7
|
-
#rm -f students*.png
|
|
8
|
-
#python students.py
|
|
9
|
-
#rm -f students0.png
|
|
10
|
-
#bash create_gif.sh students -d
|
|
11
|
-
|
|
12
|
-
rm -f factorial*.png
|
|
13
|
-
python factorial.py
|
|
14
|
-
rm -f factorial0.png
|
|
15
|
-
bash create_gif.sh factorial -d
|
|
16
|
-
|
|
17
|
-
python permutations_dot.py perms_LR3.png LR 3
|
|
18
|
-
|
|
19
|
-
rm -f permutations*.png
|
|
20
|
-
python permutations.py
|
|
21
|
-
rm -f permutations0.png
|
|
22
|
-
bash create_gif.sh permutations -d
|
|
23
|
-
|
|
24
|
-
rm -f permutations_neighbor*.png
|
|
25
|
-
python permutations_neighbor.py
|
|
26
|
-
rm -f permutations_neighbor0.png
|
|
27
|
-
bash create_gif.sh permutations_neighbor -d
|
|
28
|
-
|
|
29
|
-
python draw_graph.py 10 2 graph_small 2 > edges_small.out
|
|
30
|
-
python draw_graph.py 26 3 graph_big 1 > edges_big.out
|
|
31
|
-
python draw_graph_d_x.py 26 3 graph_big_d_x 1 > edges_big_d_x.out
|
|
32
|
-
|
|
33
|
-
rm -f permutations_return*.png
|
|
34
|
-
python permutations_return.py
|
|
35
|
-
rm -f permutations_return0.png
|
|
36
|
-
bash create_gif.sh permutations_return -d
|
|
37
|
-
|
|
38
|
-
rm -f permutations_collect*.png
|
|
39
|
-
python permutations_collect.py
|
|
40
|
-
rm -f permutations_collect0.png
|
|
41
|
-
bash create_gif.sh permutations_collect -d
|
|
42
|
-
|
|
43
|
-
rm -f quick_sort*.png
|
|
44
|
-
python quick_sort.py
|
|
45
|
-
rm -f quick_sort0.png
|
|
46
|
-
bash create_gif.sh quick_sort -d
|
|
47
|
-
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
import graph
|
|
3
|
-
import graphviz
|
|
4
|
-
|
|
5
|
-
def draw_graph(nodes, edges, filename='graph.png'):
|
|
6
|
-
dot = graphviz.Graph(
|
|
7
|
-
format='png',
|
|
8
|
-
)
|
|
9
|
-
for node in nodes:
|
|
10
|
-
# Color specific nodes
|
|
11
|
-
if node == 'a':
|
|
12
|
-
dot.node(node, fillcolor='green', style='filled')
|
|
13
|
-
elif node == 'b':
|
|
14
|
-
dot.node(node, fillcolor='red', style='filled')
|
|
15
|
-
else:
|
|
16
|
-
dot.node(node)
|
|
17
|
-
for edge in edges:
|
|
18
|
-
dot.edge(edge[0], edge[1])
|
|
19
|
-
dot.render(filename, cleanup=True)
|
|
20
|
-
print('edges = ', edges)
|
|
21
|
-
|
|
22
|
-
if __name__ == "__main__":
|
|
23
|
-
if len(sys.argv) != 5:
|
|
24
|
-
print("Usage: python draw_graph.py <n> <max_edges> <output_file> <seed>")
|
|
25
|
-
sys.exit(1)
|
|
26
|
-
n = int(sys.argv[1])
|
|
27
|
-
max_edges = int(sys.argv[2])
|
|
28
|
-
output_file = sys.argv[3]
|
|
29
|
-
seed = int(sys.argv[4])
|
|
30
|
-
|
|
31
|
-
nodes, edges = graph.generate(n, max_edges, seed)
|
|
32
|
-
draw_graph(nodes, edges, output_file)
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
import graph
|
|
3
|
-
import graphviz
|
|
4
|
-
|
|
5
|
-
def draw_graph(nodes, edges, filename='graph.png'):
|
|
6
|
-
dot = graphviz.Graph(
|
|
7
|
-
format='png',
|
|
8
|
-
)
|
|
9
|
-
for node in nodes:
|
|
10
|
-
# Color specific nodes
|
|
11
|
-
if node == 'a':
|
|
12
|
-
dot.node(node, fillcolor='green', style='filled')
|
|
13
|
-
elif node == 'b':
|
|
14
|
-
dot.node(node, fillcolor='red', style='filled')
|
|
15
|
-
elif node == 'd':
|
|
16
|
-
dot.node(node, fillcolor='orange', style='filled')
|
|
17
|
-
elif node == 'x':
|
|
18
|
-
dot.node(node, fillcolor='gray', style='filled')
|
|
19
|
-
else:
|
|
20
|
-
dot.node(node)
|
|
21
|
-
for edge in edges:
|
|
22
|
-
dot.edge(edge[0], edge[1])
|
|
23
|
-
dot.render(filename, cleanup=True)
|
|
24
|
-
print('edges = ', edges)
|
|
25
|
-
|
|
26
|
-
if __name__ == "__main__":
|
|
27
|
-
if len(sys.argv) != 5:
|
|
28
|
-
print("Usage: python draw_graph.py <n> <max_edges> <output_file> <seed>")
|
|
29
|
-
sys.exit(1)
|
|
30
|
-
n = int(sys.argv[1])
|
|
31
|
-
max_edges = int(sys.argv[2])
|
|
32
|
-
output_file = sys.argv[3]
|
|
33
|
-
seed = int(sys.argv[4])
|
|
34
|
-
|
|
35
|
-
nodes, edges = graph.generate(n, max_edges, seed)
|
|
36
|
-
draw_graph(nodes, edges, output_file)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
edges = [('a', 's'), ('i', 'z'), ('c', 'p'), ('d', 'p'), ('d', 'u'), ('b', 'e'), ('b', 'g'), ('f', 'p'), ('g', 'm'), ('h', 't'), ('h', 'y'), ('i', 'w'), ('i', 'j'), ('i', 'x'), ('k', 's'), ('k', 'l'), ('a', 'm'), ('n', 'u'), ('a', 'o'), ('a', 'v'), ('n', 'p'), ('a', 'q'), ('a', 'h'), ('p', 'r'), ('l', 's'), ('t', 'v'), ('u', 'y'), ('j', 'v'), ('a', 'j'), ('r', 'w'), ('r', 'u'), ('f', 'x'), ('x', 'y'), ('j', 'x'), ('d', 'j'), ('b', 'k'), ('b', 'x'), ('b', 'w')]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
edges = [('a', 's'), ('i', 'z'), ('c', 'p'), ('d', 'p'), ('d', 'u'), ('b', 'e'), ('b', 'g'), ('f', 'p'), ('g', 'm'), ('h', 't'), ('h', 'y'), ('i', 'w'), ('i', 'j'), ('i', 'x'), ('k', 's'), ('k', 'l'), ('a', 'm'), ('n', 'u'), ('a', 'o'), ('a', 'v'), ('n', 'p'), ('a', 'q'), ('a', 'h'), ('p', 'r'), ('l', 's'), ('t', 'v'), ('u', 'y'), ('j', 'v'), ('a', 'j'), ('r', 'w'), ('r', 'u'), ('f', 'x'), ('x', 'y'), ('j', 'x'), ('d', 'j'), ('b', 'k'), ('b', 'x'), ('b', 'w')]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
edges = [('a', 'j'), ('f', 'j'), ('c', 'e'), ('b', 'd'), ('b', 'e'), ('f', 'g'), ('g', 'i'), ('h', 'i'), ('e', 'h'), ('a', 'i'), ('b', 'h'), ('b', 'f')]
|
|
Binary file
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import random
|
|
2
|
-
import string
|
|
3
|
-
|
|
4
|
-
def get_name(i):
|
|
5
|
-
name = ''
|
|
6
|
-
while True:
|
|
7
|
-
i, remainder = divmod(i, 26)
|
|
8
|
-
name = string.ascii_lowercase[remainder] + name
|
|
9
|
-
if i == 0:
|
|
10
|
-
break
|
|
11
|
-
i -= 1
|
|
12
|
-
return name
|
|
13
|
-
|
|
14
|
-
def generate(n, max_edge, seed=0):
|
|
15
|
-
random.seed(seed)
|
|
16
|
-
nodes = [get_name(i) for i in range(n)]
|
|
17
|
-
if len(nodes) > 2:
|
|
18
|
-
nodes[1], nodes[-1] = nodes[-1], nodes[1]
|
|
19
|
-
edges = []
|
|
20
|
-
for node1 in nodes:
|
|
21
|
-
nr_edges = random.randint(1, max_edge)
|
|
22
|
-
for _ in range(nr_edges):
|
|
23
|
-
node2 = random.choice(nodes)
|
|
24
|
-
if node1 != node2:
|
|
25
|
-
if node1 > node2:
|
|
26
|
-
node1 , node2 = node2, node1
|
|
27
|
-
edge = (node1, node2)
|
|
28
|
-
if edge not in edges:
|
|
29
|
-
edges.append(edge)
|
|
30
|
-
return nodes, edges
|
|
31
|
-
|
|
32
|
-
if __name__ == '__main__':
|
|
33
|
-
nodes, edges = generate(10, 3)
|
|
34
|
-
print('nodes =', nodes)
|
|
35
|
-
print('edges =', edges)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import invocation_tree as ivt
|
|
2
|
-
import jugs as jg
|
|
3
|
-
|
|
4
|
-
def solver_depth_first(jugs, goal):
|
|
5
|
-
|
|
6
|
-
def solver_depth_first_recursive(jugs, goal, jugs_hist, action_hist):
|
|
7
|
-
actions = jugs.all_actions()
|
|
8
|
-
for action in actions:
|
|
9
|
-
goal_reached = jugs.do_action(action, goal)
|
|
10
|
-
if jugs not in jugs_hist:
|
|
11
|
-
jugs_hist.add(jugs.copy())
|
|
12
|
-
action_hist.append(action)
|
|
13
|
-
if goal_reached:
|
|
14
|
-
return action_hist
|
|
15
|
-
result = solver_depth_first_recursive(jugs, goal, jugs_hist, action_hist)
|
|
16
|
-
if result:
|
|
17
|
-
return result
|
|
18
|
-
action_hist.pop()
|
|
19
|
-
jugs_hist.remove(jugs)
|
|
20
|
-
jugs.undo_action(action)
|
|
21
|
-
return None
|
|
22
|
-
|
|
23
|
-
jugs_hist = {jugs}
|
|
24
|
-
action_hist = []
|
|
25
|
-
return solver_depth_first_recursive(jugs.copy(), goal, jugs_hist, action_hist)
|
|
26
|
-
|
|
27
|
-
if __name__ == '__main__':
|
|
28
|
-
goal = 4
|
|
29
|
-
print('Goal is to get a jug with', goal, 'liters')
|
|
30
|
-
jugs = jg.Jugs((3, 5))
|
|
31
|
-
print('We start with jugs:',jugs)
|
|
32
|
-
tree = ivt.blocking()
|
|
33
|
-
tree.hide_vars.add('solver_depth_first_recursive.jugs_hist')
|
|
34
|
-
tree.hide_vars.add('solver_depth_first_recursive.action_hist')
|
|
35
|
-
tree.ignore_calls.add('re:Jugs.*')
|
|
36
|
-
solution_actions = tree(solver_depth_first, jugs, goal)
|
|
37
|
-
#solution_actions = solver_depth_first( jugs, goal)
|
|
38
|
-
jg.print_solution(jugs, solution_actions)
|
|
Binary file
|
|
Binary file
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import invocation_tree as ivt
|
|
2
|
-
|
|
3
|
-
def permutations(elements, perm, n):
|
|
4
|
-
if n == 0:
|
|
5
|
-
print(perm)
|
|
6
|
-
else:
|
|
7
|
-
for element in elements:
|
|
8
|
-
permutations(elements, perm + element, n-1)
|
|
9
|
-
|
|
10
|
-
tree = ivt.gif('permutations.png')
|
|
11
|
-
result = tree(permutations, 'LR', '', 3)
|
|
Binary file
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import invocation_tree as ivt
|
|
2
|
-
|
|
3
|
-
def permutations(elements, perm, n, results):
|
|
4
|
-
if n == 0:
|
|
5
|
-
results.append(perm)
|
|
6
|
-
else:
|
|
7
|
-
for element in elements:
|
|
8
|
-
permutations(elements, perm + element, n-1, results)
|
|
9
|
-
|
|
10
|
-
tree = ivt.gif('permutations_collect.png')
|
|
11
|
-
results = []
|
|
12
|
-
tree(permutations, 'LR', '', 3, results)
|
|
13
|
-
print(results)
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
|
|
3
|
-
from graphviz import Digraph
|
|
4
|
-
|
|
5
|
-
def add_permute_nodes(dot, elements, perm, n, parent_id=None, node_id=[0]):
|
|
6
|
-
if n < 0:
|
|
7
|
-
return
|
|
8
|
-
|
|
9
|
-
my_id = node_id[0]
|
|
10
|
-
node_id[0] += 1
|
|
11
|
-
dot.node(str(my_id), perm)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if parent_id is not None:
|
|
15
|
-
dot.edge(str(parent_id), str(my_id))
|
|
16
|
-
|
|
17
|
-
for i, ch in enumerate(elements):
|
|
18
|
-
add_permute_nodes(dot, elements, perm + ch, n-1, parent_id=my_id, node_id=node_id)
|
|
19
|
-
|
|
20
|
-
def make_permutation_tree(outfile, elements, n):
|
|
21
|
-
dot = Digraph()
|
|
22
|
-
dot.attr(rankdir='TB') # top to bottom
|
|
23
|
-
add_permute_nodes(dot, elements, "", n)
|
|
24
|
-
splits = outfile.split('.')
|
|
25
|
-
dot.format = splits[-1]
|
|
26
|
-
dot.render(filename=''.join(splits[:-1]), cleanup=True)
|
|
27
|
-
print(f"Generated graph: {outfile}")
|
|
28
|
-
|
|
29
|
-
if __name__ == "__main__":
|
|
30
|
-
make_permutation_tree(sys.argv[1], sys.argv[2], int(sys.argv[3]))
|
|
Binary file
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import invocation_tree as ivt
|
|
2
|
-
|
|
3
|
-
def permutations(elems, perm, n):
|
|
4
|
-
if n == 0:
|
|
5
|
-
print(perm)
|
|
6
|
-
else:
|
|
7
|
-
for element in elems:
|
|
8
|
-
if len(perm) == 0 or not perm[-1] == element: # test neighbor
|
|
9
|
-
permutations(elems, perm + element, n-1)
|
|
10
|
-
|
|
11
|
-
tree = ivt.gif('permutations_neighbor.png')
|
|
12
|
-
tree(permutations, 'ABC', '', 3) # permutations of A, B, C of length 3
|
|
Binary file
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import invocation_tree as ivt
|
|
2
|
-
|
|
3
|
-
def permutations(elements, perm, n):
|
|
4
|
-
if n == 0:
|
|
5
|
-
return [perm]
|
|
6
|
-
else:
|
|
7
|
-
results = []
|
|
8
|
-
for element in elements:
|
|
9
|
-
results += permutations(elements, perm + element, n-1)
|
|
10
|
-
return results
|
|
11
|
-
|
|
12
|
-
tree = ivt.gif('permutations_return.png')
|
|
13
|
-
print(tree(permutations, 'LR', '', 3))
|
|
Binary file
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
edges = [('a', 'j'), ('f', 'j'), ('c', 'e'), ('b', 'd'), ('b', 'e'), ('f', 'g'), ('g', 'i'), ('h', 'i'), ('e', 'h'), ('a', 'i'), ('b', 'h'), ('b', 'f')]
|
|
2
|
-
|
|
3
|
-
def edges_to_steps(edges: list[tuple[str, str]]) -> dict[str,list[str]]:
|
|
4
|
-
""" Returns a dict with for each node the nodes it is connected with. """
|
|
5
|
-
steps = {}
|
|
6
|
-
for n1, n2 in edges:
|
|
7
|
-
if not n1 in steps:
|
|
8
|
-
steps[n1] = []
|
|
9
|
-
steps[n1].append(n2)
|
|
10
|
-
if not n2 in steps:
|
|
11
|
-
steps[n2] = []
|
|
12
|
-
steps[n2].append(n1)
|
|
13
|
-
return steps
|
|
14
|
-
|
|
15
|
-
def print_all_paths(steps, path, goal):
|
|
16
|
-
current = path[-1]
|
|
17
|
-
if current == goal:
|
|
18
|
-
print(path)
|
|
19
|
-
else:
|
|
20
|
-
valid_steps = steps[current]
|
|
21
|
-
for s in valid_steps:
|
|
22
|
-
if s not in path:
|
|
23
|
-
print_all_paths(steps, path+s, goal)
|
|
24
|
-
|
|
25
|
-
steps = edges_to_steps(edges)
|
|
26
|
-
print_all_paths(steps, 'a', 'b')
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
edges = [('a', 's'), ('i', 'z'), ('c', 'p'), ('d', 'p'), ('d', 'u'), ('b', 'e'), ('b', 'g'), ('f', 'p'), ('g', 'm'), ('h', 't'), ('h', 'y'), ('i', 'w'), ('i', 'j'), ('i', 'x'), ('k', 's'), ('k', 'l'), ('a', 'm'), ('n', 'u'), ('a', 'o'), ('a', 'v'), ('n', 'p'), ('a', 'q'), ('a', 'h'), ('p', 'r'), ('l', 's'), ('t', 'v'), ('u', 'y'), ('j', 'v'), ('a', 'j'), ('r', 'w'), ('r', 'u'), ('f', 'x'), ('x', 'y'), ('j', 'x'), ('d', 'j'), ('b', 'k'), ('b', 'x'), ('b', 'w')]
|
|
2
|
-
|
|
3
|
-
def edges_to_steps(edges: list[tuple[str, str]]) -> dict[str,list[str]]:
|
|
4
|
-
""" Returns a dict with for each node the nodes it is connected with. """
|
|
5
|
-
steps = {}
|
|
6
|
-
for n1, n2 in edges:
|
|
7
|
-
if not n1 in steps:
|
|
8
|
-
steps[n1] = []
|
|
9
|
-
steps[n1].append(n2)
|
|
10
|
-
if not n2 in steps:
|
|
11
|
-
steps[n2] = []
|
|
12
|
-
steps[n2].append(n1)
|
|
13
|
-
return steps
|
|
14
|
-
|
|
15
|
-
def print_all_paths(steps, path, goal, length):
|
|
16
|
-
current = path[-1]
|
|
17
|
-
length_path = len(path)
|
|
18
|
-
if length_path >= length:
|
|
19
|
-
if length_path == length and current == goal:
|
|
20
|
-
print(path)
|
|
21
|
-
else:
|
|
22
|
-
valid_steps = steps[current]
|
|
23
|
-
for s in valid_steps:
|
|
24
|
-
print_all_paths(steps, path+s, goal, length)
|
|
25
|
-
|
|
26
|
-
steps = edges_to_steps(edges)
|
|
27
|
-
print_all_paths(steps, 'a', 'b', 7)
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
edges = [('a', 's'), ('i', 'z'), ('c', 'p'), ('d', 'p'), ('d', 'u'), ('b', 'e'), ('b', 'g'), ('f', 'p'), ('g', 'm'), ('h', 't'), ('h', 'y'), ('i', 'w'), ('i', 'j'), ('i', 'x'), ('k', 's'), ('k', 'l'), ('a', 'm'), ('n', 'u'), ('a', 'o'), ('a', 'v'), ('n', 'p'), ('a', 'q'), ('a', 'h'), ('p', 'r'), ('l', 's'), ('t', 'v'), ('u', 'y'), ('j', 'v'), ('a', 'j'), ('r', 'w'), ('r', 'u'), ('f', 'x'), ('x', 'y'), ('j', 'x'), ('d', 'j'), ('b', 'k'), ('b', 'x'), ('b', 'w')]
|
|
2
|
-
|
|
3
|
-
def edges_to_steps(edges: list[tuple[str, str]]) -> dict[str,list[str]]:
|
|
4
|
-
""" Returns a dict with for each node the nodes it is connected with. """
|
|
5
|
-
steps = {}
|
|
6
|
-
for n1, n2 in edges:
|
|
7
|
-
if not n1 in steps:
|
|
8
|
-
steps[n1] = []
|
|
9
|
-
steps[n1].append(n2)
|
|
10
|
-
if not n2 in steps:
|
|
11
|
-
steps[n2] = []
|
|
12
|
-
steps[n2].append(n1)
|
|
13
|
-
return steps
|
|
14
|
-
|
|
15
|
-
def print_all_paths(steps, path, goal, length, results):
|
|
16
|
-
current = path[-1]
|
|
17
|
-
length_path = len(path)
|
|
18
|
-
if length_path >= length:
|
|
19
|
-
if length_path == length and current == goal:
|
|
20
|
-
if 'd' in path:
|
|
21
|
-
results.append(path)
|
|
22
|
-
else:
|
|
23
|
-
valid_steps = steps[current]
|
|
24
|
-
for s in valid_steps:
|
|
25
|
-
if not s == 'x':
|
|
26
|
-
print_all_paths(steps, path+s, goal, length, results)
|
|
27
|
-
|
|
28
|
-
steps = edges_to_steps(edges)
|
|
29
|
-
results = []
|
|
30
|
-
print_all_paths(steps, 'a', 'b', 10, results)
|
|
31
|
-
print('results:', results)
|
|
32
|
-
print(len(results))
|
|
Binary file
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import invocation_tree as ivt
|
|
2
|
-
|
|
3
|
-
def quick_sort(values):
|
|
4
|
-
if len(values) <= 1:
|
|
5
|
-
return values
|
|
6
|
-
pivot = values[0]
|
|
7
|
-
smaller = [x for x in values if x < pivot]
|
|
8
|
-
equal = [x for x in values if x == pivot]
|
|
9
|
-
larger = [x for x in values if x > pivot]
|
|
10
|
-
return quick_sort(smaller) + equal + quick_sort(larger)
|
|
11
|
-
|
|
12
|
-
values = [7, 4, 10, 11, 2, 6, 9, 1, 5, 3, 8, 12]
|
|
13
|
-
print('unsorted values:',values)
|
|
14
|
-
tree = ivt.gif('quick_sort.png')
|
|
15
|
-
values = tree(quick_sort, values)
|
|
16
|
-
print(' sorted values:',values)
|
|
Binary file
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import invocation_tree as ivt
|
|
2
|
-
from decimal import Decimal, ROUND_HALF_UP
|
|
3
|
-
|
|
4
|
-
def main():
|
|
5
|
-
students = {'Ann':[7.5, 8.0],
|
|
6
|
-
'Bob':[4.5, 6.0],
|
|
7
|
-
'Coy':[7.5, 6.0]}
|
|
8
|
-
averages = {student:compute_average(grades)
|
|
9
|
-
for student, grades in students.items()}
|
|
10
|
-
passing = passing_students(averages)
|
|
11
|
-
print(passing)
|
|
12
|
-
|
|
13
|
-
def compute_average(grades):
|
|
14
|
-
average = sum(grades)/len(grades)
|
|
15
|
-
return half_up_round(average, 1)
|
|
16
|
-
|
|
17
|
-
def half_up_round(value, digits=0):
|
|
18
|
-
""" High-precision half-up rounding of 'value' to a specified number of 'digits'. """
|
|
19
|
-
return float(Decimal(str(value)).quantize(Decimal(f"1e-{digits}"),
|
|
20
|
-
rounding=ROUND_HALF_UP))
|
|
21
|
-
|
|
22
|
-
def passing_students(avg):
|
|
23
|
-
return [student
|
|
24
|
-
for student, average in avg.items()
|
|
25
|
-
if average >= 5.5]
|
|
26
|
-
|
|
27
|
-
if __name__ == '__main__':
|
|
28
|
-
ivt.gif(filename="students.png")(main)
|
|
Binary file
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
LICENSE.txt
|
|
2
|
-
MANIFEST.in
|
|
3
|
-
README.md
|
|
4
|
-
pyproject.toml
|
|
5
|
-
images/compute.gif
|
|
6
|
-
images/compute.py
|
|
7
|
-
images/create_gif.sh
|
|
8
|
-
images/create_images.sh
|
|
9
|
-
images/draw_graph.py
|
|
10
|
-
images/draw_graph_d_x.py
|
|
11
|
-
images/edges_big.out
|
|
12
|
-
images/edges_big_d_x.out
|
|
13
|
-
images/edges_small.out
|
|
14
|
-
images/factorial.gif
|
|
15
|
-
images/factorial.py
|
|
16
|
-
images/graph.py
|
|
17
|
-
images/graph_big.png
|
|
18
|
-
images/graph_big_d_x.png
|
|
19
|
-
images/graph_small.png
|
|
20
|
-
images/jugs.png
|
|
21
|
-
images/jugs_depth_first.py
|
|
22
|
-
images/perms_LR3.png
|
|
23
|
-
images/permutations.gif
|
|
24
|
-
images/permutations.py
|
|
25
|
-
images/permutations_collect.gif
|
|
26
|
-
images/permutations_collect.py
|
|
27
|
-
images/permutations_dot.py
|
|
28
|
-
images/permutations_neighbor.gif
|
|
29
|
-
images/permutations_neighbor.py
|
|
30
|
-
images/permutations_return.gif
|
|
31
|
-
images/permutations_return.py
|
|
32
|
-
images/permutations_vscode.gif
|
|
33
|
-
images/print_all_paths.py
|
|
34
|
-
images/print_all_paths_of_length.py
|
|
35
|
-
images/print_all_paths_via.py
|
|
36
|
-
images/quick_sort.gif
|
|
37
|
-
images/quick_sort.py
|
|
38
|
-
images/students.gif
|
|
39
|
-
images/students.py
|
|
40
|
-
images/vscode.png
|
|
41
|
-
invocation_tree/__init__.py
|
|
42
|
-
invocation_tree/regex_set.py
|
|
43
|
-
invocation_tree.egg-info/PKG-INFO
|
|
44
|
-
invocation_tree.egg-info/SOURCES.txt
|
|
45
|
-
invocation_tree.egg-info/dependency_links.txt
|
|
46
|
-
invocation_tree.egg-info/requires.txt
|
|
47
|
-
invocation_tree.egg-info/top_level.txt
|
|
File without changes
|
|
File without changes
|
{invocation_tree-0.0.33 → invocation_tree-0.0.35}/invocation_tree.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|