invocation-tree 0.0.1__py2.py3-none-any.whl

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.
@@ -0,0 +1,151 @@
1
+ # This file is part of invocation_tree.
2
+ # Copyright (c) 2023, Bas Terwijn.
3
+ # SPDX-License-Identifier: BSD-2-Clause
4
+
5
+ from graphviz import Digraph
6
+ import html
7
+ import sys
8
+ import inspect
9
+
10
+ __version__ = "0.0.1"
11
+ __author__ = 'Bas Terwijn'
12
+
13
+ class Tree_Node:
14
+
15
+ def __init__(self, node_id, frame, return_value):
16
+ self.node_id = node_id
17
+ self.frame = frame
18
+ self.return_value = return_value
19
+
20
+ def __repr__(self):
21
+ return f'node_id:{self.node_id} frame:{self.frame} return_value:{self.return_value}'
22
+
23
+ class Invocation_Tree:
24
+
25
+ def __init__(self, output_filename='invocation_tree.pdf', block=True, src_location=True, max_string_len=100, indent=' ',
26
+ color_paused = '#ccffcc', color_active = '#ffffff', color_returned = '#ffcccc', each_line=False, to_string=None):
27
+ # --- config
28
+ self.output_filename = output_filename
29
+ self.block = block
30
+ self.src_location = src_location
31
+ self.max_string_len = max_string_len
32
+ self.indent = indent
33
+ self.color_paused = color_paused
34
+ self.color_active = color_active
35
+ self.color_returned = color_returned
36
+ self.each_line = each_line
37
+ self.to_string = {}
38
+ if not to_string is None:
39
+ self.to_string = to_string
40
+ # --- core
41
+ self.stack = []
42
+ self.node_id = 0
43
+ self.node_id_to_table = {}
44
+ self.edges = []
45
+
46
+ def print_stack(self):
47
+ for tree_node in self.stack:
48
+ print(tree_node)
49
+
50
+ def value_to_string(self, value, name):
51
+ if id(value) in self.to_string:
52
+ return self.to_string[id(value)](value)
53
+ elif name in self.to_string:
54
+ return self.to_string[name](value)
55
+ elif type(value) in self.to_string:
56
+ return self.to_string[type(value)](value)
57
+ else:
58
+ val_str = str(value)
59
+ if len(val_str) > self.max_string_len:
60
+ val_str = '...'+val_str[-self.max_string_len:]
61
+ return html.escape(val_str)
62
+
63
+ def build_html_table(self, tree_node, active=False, returned=False):
64
+ function_name = tree_node.frame.f_code.co_name
65
+ local_vars = tree_node.frame.f_locals
66
+ return_value = tree_node.return_value
67
+ border = 1
68
+ color = self.color_paused
69
+ if active:
70
+ color = self.color_active
71
+ border = 3
72
+ if returned:
73
+ color = self.color_returned
74
+ table = f'<\n<TABLE BORDER="{str(border)}" CELLBORDER="0" CELLSPACING="1" BGCOLOR="{color}">\n <TR>'
75
+ table += '<TD ALIGN="left">'+ self.value_to_string(function_name, 'function_'+function_name) +'</TD>'
76
+ table += '</TR>\n <TR>'
77
+ new_line = True
78
+ for var,val in local_vars.items():
79
+ if not new_line:
80
+ table += '</TR>\n <TR>'
81
+ table += '<TD ALIGN="left">'+ self.indent + self.value_to_string(var, function_name+'_'+var) + ': ' + \
82
+ self.value_to_string(val, function_name+'.'+var) +'</TD>'
83
+ new_line = False
84
+ if returned:
85
+ if not new_line:
86
+ table += '</TR>\n <TR>'
87
+ table += '<TD ALIGN="left">'+ 'return ' + self.value_to_string(return_value, function_name+'.'+'return') +'</TD>'
88
+ new_line = False
89
+ table += '</TR>\n</TABLE>>'
90
+ return table
91
+
92
+ def update_node(self, tree_node, active=False, returned=False):
93
+ table = self.build_html_table(tree_node, active, returned)
94
+ self.node_id_to_table[str(tree_node.node_id)] = table
95
+
96
+ def add_edge(self, tree_node1, tree_node2):
97
+ self.edges.append((str(tree_node1.node_id), str(tree_node2.node_id)))
98
+
99
+ def render_graph(self, frame, event):
100
+ graphviz_graph_attr = {}
101
+ graphviz_node_attr = {'shape':'plaintext'}
102
+ graphviz_edge_attr = {}
103
+ graph = Digraph(comment="recursion_graph",
104
+ graph_attr=graphviz_graph_attr,
105
+ node_attr=graphviz_node_attr,
106
+ edge_attr=graphviz_edge_attr)
107
+ for nid, table in self.node_id_to_table.items():
108
+ graph.node(nid, label=table)
109
+ for nid1, nid2 in self.edges:
110
+ graph.edge(nid1, nid2)
111
+ graph.render(outfile=self.output_filename, cleanup=True)
112
+ if self.block:
113
+ if self.src_location:
114
+ filename = frame.f_code.co_filename
115
+ line_nr = frame.f_lineno
116
+ print(f'{event.capitalize()} at {filename}:{line_nr}', end='. ')
117
+ input('Press <Enter> to continue...')
118
+
119
+ def trace_calls(self, frame, event, arg):
120
+ #print('========= event:',event)
121
+ if event == 'call':
122
+ self.stack.append(Tree_Node(self.node_id, frame, None))
123
+ self.node_id += 1
124
+ #self.print_stack()
125
+ if len(self.stack)>1:
126
+ self.update_node(self.stack[-2])
127
+ self.add_edge(self.stack[-2], self.stack[-1])
128
+ self.update_node(self.stack[-1], active=True)
129
+ self.render_graph(frame, event)
130
+ elif event == 'return':
131
+ self.stack[-1].return_value = arg
132
+ #self.print_stack()
133
+ self.update_node(self.stack[-1], returned=True)
134
+ if len(self.stack)>1:
135
+ self.update_node(self.stack[-2], active=True)
136
+ self.stack.pop()
137
+ self.render_graph(frame, event)
138
+ elif event == 'line' and self.each_line:
139
+ self.update_node(self.stack[-1], active=True)
140
+ self.render_graph(frame, event)
141
+ return self.trace_calls
142
+
143
+ def __call__(self, fun, *args, **kwargs):
144
+ sys.settrace(self.trace_calls)
145
+ try:
146
+ result = fun(*args, **kwargs)
147
+ except Exception as e:
148
+ raise
149
+ finally:
150
+ sys.settrace(None)
151
+ return result
@@ -0,0 +1,25 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2017, pyexample
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ * Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,27 @@
1
+ Metadata-Version: 2.1
2
+ Name: invocation_tree
3
+ Version: 0.0.1
4
+ Summary: Generate a call tree of functions.
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
+ # Call Tree #
@@ -0,0 +1,6 @@
1
+ invocation_tree/__init__.py,sha256=PSFzqim1R5-s1J-1BvzoXLmILReaihfqVJyWmMxYzs4,5912
2
+ invocation_tree-0.0.1.dist-info/LICENSE.txt,sha256=lhBfhX4yJut_-ahPAsH87Xhk-01Df17Std-X1Xy6rhU,1314
3
+ invocation_tree-0.0.1.dist-info/METADATA,sha256=tL3T1-cPqejFpoWY3kMaMaiAPLl5tpFtU5Gs_RHJIv0,846
4
+ invocation_tree-0.0.1.dist-info/WHEEL,sha256=B03JAMGq6-VHSCrtDNma_J9UaPCNDvoQGYRXpzGHxgg,109
5
+ invocation_tree-0.0.1.dist-info/top_level.txt,sha256=-UiQipEd5_8mqbNW12sUtrMfxlTqV0HV0T4WFCeeD_I,16
6
+ invocation_tree-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,6 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.4.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py2-none-any
5
+ Tag: py3-none-any
6
+
@@ -0,0 +1 @@
1
+ invocation_tree