atrace 0.1.0__py3-none-any.whl → 0.1.1__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.
atrace/__init__.py
CHANGED
|
@@ -1,2 +1,101 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import copy
|
|
2
|
+
import inspect
|
|
3
|
+
import sys
|
|
4
|
+
from types import FrameType
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
paused = True
|
|
8
|
+
last_locals = {}
|
|
9
|
+
|
|
10
|
+
ignored_variables = set(
|
|
11
|
+
[
|
|
12
|
+
"tid",
|
|
13
|
+
]
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def ignore_variable(var: str):
|
|
18
|
+
return var in ignored_variables
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def copy_carefully_using_ignored_variables(d: dict[str, Any]):
|
|
22
|
+
res = {}
|
|
23
|
+
for k, v in d.items():
|
|
24
|
+
if not ignore_variable(k):
|
|
25
|
+
res[k] = copy.deepcopy(v)
|
|
26
|
+
return res
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def copy_carefully(d: dict[str, Any]):
|
|
30
|
+
res = {}
|
|
31
|
+
for k, v in d.items():
|
|
32
|
+
try:
|
|
33
|
+
v_copy = copy.deepcopy(v)
|
|
34
|
+
except:
|
|
35
|
+
v_copy = v
|
|
36
|
+
res[k] = v_copy
|
|
37
|
+
return res
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def trace_vars(frame: FrameType, event: str, arg: Any):
|
|
41
|
+
if paused:
|
|
42
|
+
return
|
|
43
|
+
# print(frame.f_lineno, frame.f_code.co_name, frame.f_locals, event)
|
|
44
|
+
if event != "line":
|
|
45
|
+
return trace_vars
|
|
46
|
+
code = frame.f_code
|
|
47
|
+
lineno = frame.f_lineno
|
|
48
|
+
locals_now = copy_carefully(frame.f_locals)
|
|
49
|
+
global last_locals
|
|
50
|
+
|
|
51
|
+
if last_locals is None: # We're being unloaded, it's the end of the program
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
if code.co_name not in last_locals:
|
|
55
|
+
last_locals[code.co_name] = locals_now
|
|
56
|
+
return trace_vars
|
|
57
|
+
|
|
58
|
+
old_locals = last_locals[code.co_name]
|
|
59
|
+
|
|
60
|
+
for var, new_val in locals_now.items():
|
|
61
|
+
if not ignore_variable(var):
|
|
62
|
+
if var not in old_locals:
|
|
63
|
+
print(f"[{lineno}] NEW {var} = {new_val}")
|
|
64
|
+
elif old_locals[var] != new_val:
|
|
65
|
+
print(f"[{lineno}] MODIFIED {var}: {old_locals[var]} → {new_val}")
|
|
66
|
+
|
|
67
|
+
for var in old_locals:
|
|
68
|
+
if not ignore_variable(var):
|
|
69
|
+
if var not in locals_now:
|
|
70
|
+
print(f"[{lineno}] DELETED {var}")
|
|
71
|
+
|
|
72
|
+
last_locals[code.co_name] = locals_now
|
|
73
|
+
return trace_vars
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def just_kicking_off(frame: FrameType, event: str, arg: Any):
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def get_importer_frame():
|
|
81
|
+
# Get the current call stack
|
|
82
|
+
for frame_info in inspect.stack():
|
|
83
|
+
# Filter out internal importlib frames and the current module's frame
|
|
84
|
+
filename = frame_info.filename
|
|
85
|
+
if not filename.startswith("<") and filename != __file__:
|
|
86
|
+
return frame_info.frame
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# This will work next time we enter a function.
|
|
91
|
+
# It also kicks off the tracing machinery (so that the lines below work)
|
|
92
|
+
sys.settrace(trace_vars)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# This reaches into the importing module's frame to setup tracing
|
|
96
|
+
importer_frame = get_importer_frame()
|
|
97
|
+
if importer_frame:
|
|
98
|
+
importer_frame.f_trace = trace_vars
|
|
99
|
+
paused = False
|
|
100
|
+
else:
|
|
101
|
+
print("Cannot trace")
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: atrace
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Generate trace tables for programs
|
|
5
|
+
Project-URL: Repository, https://github.com/nwolff/atrace.git
|
|
6
|
+
Author-email: Nicholas Wolff <nwolff@gmail.com>
|
|
7
|
+
Requires-Python: >=3.7
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# TODO:
|
|
11
|
+
|
|
12
|
+
- entering a function, binding the local arguments, returning
|
|
13
|
+
- ignore function definitions (look at the type of object)
|
|
14
|
+
|
|
15
|
+
- display at end only
|
|
16
|
+
|
|
17
|
+
# Usage and intent
|
|
18
|
+
|
|
19
|
+
A package that automatically prints a trace table of a program, just by importing the module
|
|
20
|
+
|
|
21
|
+
- Should not interfere with the running program (apart from capturing stdout)
|
|
22
|
+
- Should display the trace at the end of execution (not while the program is interacting with the user)
|
|
23
|
+
- Should display the trace even if an exception interrupts the program
|
|
24
|
+
- Should display the trace even if the user interrupts the program
|
|
25
|
+
- Should handle mutations to objects like lists
|
|
26
|
+
- Should handle functions properly:
|
|
27
|
+
- entering the function
|
|
28
|
+
- binding the local arguments
|
|
29
|
+
- returning
|
|
30
|
+
|
|
31
|
+
An animated example of a trace table: https://www.101computing.net/using-trace-tables/
|
|
32
|
+
|
|
33
|
+
# Not in scope
|
|
34
|
+
|
|
35
|
+
- Multithreaded programs
|
|
36
|
+
|
|
37
|
+
# Implementation
|
|
38
|
+
|
|
39
|
+
To trace variables :
|
|
40
|
+
|
|
41
|
+
- Either https://docs.python.org/3/library/sys.html#sys.settrace
|
|
42
|
+
- Or https://docs.python.org/3/library/trace.html
|
|
43
|
+
|
|
44
|
+
To capture stdout :
|
|
45
|
+
|
|
46
|
+
- Either https://docs.python.org/3/library/contextlib.html#contextlib.redirect_stdout
|
|
47
|
+
- Or just reassign stdout
|
|
48
|
+
|
|
49
|
+
To trap sigint:
|
|
50
|
+
|
|
51
|
+
- https://stackoverflow.com/questions/1112343/how-do-i-capture-sigint-in-python
|
|
52
|
+
|
|
53
|
+
# Build
|
|
54
|
+
|
|
55
|
+
Automatically deployed to pypi every time a new tag is pushed: https://pypi.org/project/atrace/
|
|
56
|
+
|
|
57
|
+
# Technical Refs
|
|
58
|
+
|
|
59
|
+
- First similar thing I found, doesn't work (chokes on deep copying some variables): https://github.com/DarshanLakshman/PyTracerTool
|
|
60
|
+
|
|
61
|
+
- 11 years old, doesn't work: https://github.com/mihneadb/python-execution-trace
|
|
62
|
+
|
|
63
|
+
- A hot mess: https://stackoverflow.com/questions/1645028/trace-table-for-python-programs
|
|
64
|
+
|
|
65
|
+
- A tutorial on the trace module: https://pymotw.com/2/trace/
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
atrace/__init__.py,sha256=wUmeppysEV7VYt8ZpVnhX9IL_Pec_50FjGqHmS5gEwc,2550
|
|
2
|
+
atrace/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
atrace-0.1.1.dist-info/METADATA,sha256=4oknmUUp4BvNtji0hRV121JKvtkjC2BOO6pt9zPqDgI,2026
|
|
4
|
+
atrace-0.1.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
5
|
+
atrace-0.1.1.dist-info/RECORD,,
|
atrace-0.1.0.dist-info/METADATA
DELETED
atrace-0.1.0.dist-info/RECORD
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
atrace/__init__.py,sha256=aT0BUPvWpbdoOpN9gOZcLNFxgENSl-ptao0487nheGE,52
|
|
2
|
-
atrace/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
atrace-0.1.0.dist-info/METADATA,sha256=P1Ej3l3jeT-SKZnSNzwb-zH7q9cwCW_7mf6DiIrWM0I,121
|
|
4
|
-
atrace-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
5
|
-
atrace-0.1.0.dist-info/RECORD,,
|
|
File without changes
|