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