invocation-tree 0.0.1__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.1/LICENSE.txt +25 -0
- invocation_tree-0.0.1/MANIFEST.in +1 -0
- invocation_tree-0.0.1/PKG-INFO +27 -0
- invocation_tree-0.0.1/README.md +8 -0
- invocation_tree-0.0.1/images/invocation_tree.pdf +0 -0
- invocation_tree-0.0.1/images/permutation.py +23 -0
- invocation_tree-0.0.1/install.txt +31 -0
- invocation_tree-0.0.1/invocation_tree/__init__.py +151 -0
- invocation_tree-0.0.1/invocation_tree.egg-info/PKG-INFO +27 -0
- invocation_tree-0.0.1/invocation_tree.egg-info/SOURCES.txt +13 -0
- invocation_tree-0.0.1/invocation_tree.egg-info/dependency_links.txt +1 -0
- invocation_tree-0.0.1/invocation_tree.egg-info/requires.txt +1 -0
- invocation_tree-0.0.1/invocation_tree.egg-info/top_level.txt +1 -0
- invocation_tree-0.0.1/setup.cfg +4 -0
- invocation_tree-0.0.1/setup.py +35 -0
|
@@ -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 @@
|
|
|
1
|
+
recursive-include images/ *
|
|
@@ -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 #
|
|
Binary file
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import invocation_tree
|
|
2
|
+
|
|
3
|
+
def permutations(all_permutations, data, perm, n):
|
|
4
|
+
if n<=0:
|
|
5
|
+
print(perm)
|
|
6
|
+
all_permutations.append(perm.copy())
|
|
7
|
+
else:
|
|
8
|
+
for i in data:
|
|
9
|
+
perm.append(i)
|
|
10
|
+
permutations(all_permutations, data, perm, n-1)
|
|
11
|
+
perm.pop()
|
|
12
|
+
|
|
13
|
+
invocation_tree = invocation_tree.Invocation_Tree(each_line=True)
|
|
14
|
+
all_permutations = []
|
|
15
|
+
|
|
16
|
+
invocation_tree.to_string[list] = lambda x : f'my_list:{x}'
|
|
17
|
+
invocation_tree.to_string[id(all_permutations)] = lambda x : f'perms:{x}'
|
|
18
|
+
invocation_tree.to_string['permutations.return'] = lambda x : f'return:{x}'
|
|
19
|
+
invocation_tree.to_string['permutations.n'] = lambda x : f'{x} this is n'
|
|
20
|
+
|
|
21
|
+
#invocation_tree(permutations, ['a','b','c'], [], 2)
|
|
22
|
+
invocation_tree(permutations, all_permutations, ['a','b','c'], [], 2)
|
|
23
|
+
print('all_permutations:', all_permutations)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# just some notes about how to deal with Python modules and pip packages
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# ===== pip install
|
|
5
|
+
pip install setuptools
|
|
6
|
+
pip install wheel
|
|
7
|
+
pip install twine
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# ===== (un)install current module
|
|
11
|
+
pip uninstall invocation_tree
|
|
12
|
+
pip install .
|
|
13
|
+
pip install --upgrade .
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# ===== prepare packages for upload
|
|
17
|
+
# - increase version number in: setup.py invocation_tree/__init__.py
|
|
18
|
+
# - update images:
|
|
19
|
+
cd images; bash create_images.sh; cd ..
|
|
20
|
+
# - git commit -am "version X.X.X" && git push
|
|
21
|
+
rm -f ./dist/*
|
|
22
|
+
python setup.py check
|
|
23
|
+
python setup.py sdist
|
|
24
|
+
python setup.py bdist_wheel --universal
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# ===== upload packages to pypi for 'pip install' purposes
|
|
28
|
+
# - upload to test url:
|
|
29
|
+
twine upload --repository-url https://test.pypi.org/legacy/ dist/*
|
|
30
|
+
# - upload to pypi for real:
|
|
31
|
+
twine upload dist/*
|
|
@@ -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,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,13 @@
|
|
|
1
|
+
LICENSE.txt
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
install.txt
|
|
5
|
+
setup.py
|
|
6
|
+
images/invocation_tree.pdf
|
|
7
|
+
images/permutation.py
|
|
8
|
+
invocation_tree/__init__.py
|
|
9
|
+
invocation_tree.egg-info/PKG-INFO
|
|
10
|
+
invocation_tree.egg-info/SOURCES.txt
|
|
11
|
+
invocation_tree.egg-info/dependency_links.txt
|
|
12
|
+
invocation_tree.egg-info/requires.txt
|
|
13
|
+
invocation_tree.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
graphviz
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
invocation_tree
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# This file is part of invocation_tree.
|
|
2
|
+
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
|
|
5
|
+
from setuptools import setup
|
|
6
|
+
|
|
7
|
+
# read the contents of your README file
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
this_directory = Path(__file__).parent
|
|
10
|
+
long_description_from_readme = (this_directory / "README.md").read_text()
|
|
11
|
+
|
|
12
|
+
setup(
|
|
13
|
+
name = 'invocation_tree',
|
|
14
|
+
version = '0.0.1',
|
|
15
|
+
description = 'Generate a call tree of functions.',
|
|
16
|
+
long_description = long_description_from_readme,
|
|
17
|
+
long_description_content_type = 'text/markdown',
|
|
18
|
+
readme = 'README.md',
|
|
19
|
+
url = 'https://github.com/bterwijn/invocation_tree',
|
|
20
|
+
author = 'Bas Terwijn',
|
|
21
|
+
author_email = 'bterwijn@gmail.com',
|
|
22
|
+
license = 'BSD 2-clause',
|
|
23
|
+
packages = ['invocation_tree'],
|
|
24
|
+
install_requires = ['graphviz'],
|
|
25
|
+
|
|
26
|
+
classifiers = [
|
|
27
|
+
'Development Status :: 4 - Beta',
|
|
28
|
+
'Intended Audience :: Education',
|
|
29
|
+
'Intended Audience :: Developers',
|
|
30
|
+
'License :: OSI Approved :: BSD License',
|
|
31
|
+
'Programming Language :: Python :: 3',
|
|
32
|
+
'Topic :: Education',
|
|
33
|
+
'Topic :: Software Development :: Debuggers',
|
|
34
|
+
],
|
|
35
|
+
)
|