atrace 0.1.5__py3-none-any.whl → 0.1.6__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 +33 -22
- atrace/model.py +10 -4
- atrace/outputlogger.py +1 -1
- atrace/report.py +49 -73
- atrace/vartracer.py +11 -5
- atrace-0.1.6.dist-info/METADATA +67 -0
- atrace-0.1.6.dist-info/RECORD +9 -0
- atrace-0.1.5.dist-info/METADATA +0 -86
- atrace-0.1.5.dist-info/RECORD +0 -9
- {atrace-0.1.5.dist-info → atrace-0.1.6.dist-info}/WHEEL +0 -0
atrace/__init__.py
CHANGED
|
@@ -3,27 +3,13 @@ import inspect
|
|
|
3
3
|
import signal
|
|
4
4
|
import sys
|
|
5
5
|
from types import FrameType
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import Optional
|
|
7
7
|
|
|
8
8
|
from . import model, outputlogger, report, vartracer
|
|
9
9
|
|
|
10
10
|
trace: model.Trace = []
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
def just_kicking_off(frame: FrameType, event: str, arg: Any):
|
|
14
|
-
return None
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def get_importer_frame() -> Optional[FrameType]:
|
|
18
|
-
# Get the current call stack
|
|
19
|
-
for frame_info in inspect.stack():
|
|
20
|
-
# Filter out internal importlib frames and the current module's frame
|
|
21
|
-
filename = frame_info.filename
|
|
22
|
-
if not filename.startswith("<") and filename != __file__:
|
|
23
|
-
return frame_info.frame
|
|
24
|
-
return None
|
|
25
|
-
|
|
26
|
-
|
|
27
13
|
original_stdout = sys.stdout
|
|
28
14
|
|
|
29
15
|
|
|
@@ -37,12 +23,19 @@ def sig_handler(_signo, _frame):
|
|
|
37
23
|
sys.exit(0)
|
|
38
24
|
|
|
39
25
|
|
|
26
|
+
def get_importer_frame() -> Optional[FrameType]:
|
|
27
|
+
# Get the current call stack
|
|
28
|
+
for frame_info in inspect.stack():
|
|
29
|
+
# Filter out internal importlib frames and the current module's frame
|
|
30
|
+
filename = frame_info.filename
|
|
31
|
+
if not filename.startswith("<") and filename != __file__:
|
|
32
|
+
return frame_info.frame
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
|
|
40
36
|
def setup():
|
|
41
37
|
"""See https://docs.python.org/3/library/sys.html#sys.settrace for an explanation of all the convoluted things in here"""
|
|
42
38
|
|
|
43
|
-
# This kicks off the tracing machinery (so that the lines below work)
|
|
44
|
-
sys.settrace(just_kicking_off)
|
|
45
|
-
|
|
46
39
|
# We want to only trace the module that imports us
|
|
47
40
|
importer_frame = get_importer_frame()
|
|
48
41
|
if importer_frame:
|
|
@@ -50,12 +43,13 @@ def setup():
|
|
|
50
43
|
if module_of_interest:
|
|
51
44
|
var_tracer = vartracer.VarTracer(trace, module_of_interest)
|
|
52
45
|
|
|
46
|
+
# Setup tracing inside of functions.
|
|
47
|
+
# Order is important. This must be called before seting the trace function for the current frame
|
|
48
|
+
sys.settrace(var_tracer.trace_vars)
|
|
49
|
+
|
|
53
50
|
# Setup tracing outside of functions
|
|
54
51
|
importer_frame.f_trace = var_tracer.trace_vars
|
|
55
52
|
|
|
56
|
-
# Setup tracing inside of functions
|
|
57
|
-
sys.settrace(var_tracer.trace_vars)
|
|
58
|
-
|
|
59
53
|
sys.stdout = outputlogger.OutputLogger(trace=trace, stdout=sys.stdout)
|
|
60
54
|
|
|
61
55
|
atexit.register(exit_handler)
|
|
@@ -64,4 +58,21 @@ def setup():
|
|
|
64
58
|
signal.signal(sig, sig_handler)
|
|
65
59
|
|
|
66
60
|
|
|
67
|
-
|
|
61
|
+
def var_trace_for_code(code: str) -> model.Trace:
|
|
62
|
+
trace: model.Trace = []
|
|
63
|
+
# We want to only trace the module that imports us
|
|
64
|
+
importer_frame = get_importer_frame()
|
|
65
|
+
if importer_frame:
|
|
66
|
+
module_of_interest = inspect.getmodule(importer_frame)
|
|
67
|
+
if module_of_interest:
|
|
68
|
+
var_tracer = vartracer.VarTracer(trace, module_of_interest)
|
|
69
|
+
try:
|
|
70
|
+
sys.settrace(var_tracer.trace_vars)
|
|
71
|
+
exec(code)
|
|
72
|
+
finally:
|
|
73
|
+
sys.settrace(None)
|
|
74
|
+
return trace
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if "pytest" not in sys.modules:
|
|
78
|
+
setup()
|
atrace/model.py
CHANGED
|
@@ -2,28 +2,34 @@ from dataclasses import dataclass
|
|
|
2
2
|
from typing import Any, TypeAlias
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
@dataclass
|
|
5
|
+
@dataclass(frozen=True)
|
|
6
6
|
class PrintEvent:
|
|
7
7
|
text: str
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
@dataclass(frozen=True)
|
|
11
11
|
class Variable:
|
|
12
|
-
|
|
12
|
+
scope: str
|
|
13
13
|
name: str
|
|
14
14
|
|
|
15
15
|
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class ReturnEvent:
|
|
18
|
+
function_name: str
|
|
19
|
+
return_value: Any
|
|
20
|
+
|
|
21
|
+
|
|
16
22
|
@dataclass(frozen=True)
|
|
17
23
|
class VariableChangeEvent:
|
|
18
24
|
variable: Variable
|
|
19
25
|
value: Any
|
|
20
26
|
|
|
21
27
|
|
|
22
|
-
@dataclass
|
|
28
|
+
@dataclass(frozen=True)
|
|
23
29
|
class TraceItem:
|
|
24
30
|
line_no: int
|
|
25
31
|
function_name: str
|
|
26
|
-
event: PrintEvent | VariableChangeEvent
|
|
32
|
+
event: PrintEvent | VariableChangeEvent | ReturnEvent
|
|
27
33
|
|
|
28
34
|
|
|
29
35
|
Trace: TypeAlias = list[TraceItem]
|
atrace/outputlogger.py
CHANGED
|
@@ -6,7 +6,7 @@ from . import model
|
|
|
6
6
|
|
|
7
7
|
class OutputLogger:
|
|
8
8
|
"""
|
|
9
|
-
OutputLogger wraps stdout,
|
|
9
|
+
OutputLogger wraps stdout, passing down writes and flushes to it, while simultaneously capturing the text to the trace.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
def __init__(self, trace: model.Trace, stdout: TextIO):
|
atrace/report.py
CHANGED
|
@@ -1,98 +1,74 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from typing import Any
|
|
2
|
+
from typing import Any, TypeAlias
|
|
3
3
|
|
|
4
4
|
from tabulate import tabulate
|
|
5
5
|
|
|
6
6
|
from . import model
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if variable_name not in variable_list:
|
|
15
|
-
variable_list.append(variable_name)
|
|
16
|
-
return variable_list
|
|
17
|
-
|
|
9
|
+
@dataclass
|
|
10
|
+
class Instant:
|
|
11
|
+
line_no: int
|
|
12
|
+
variable_changes: dict[model.Variable, Any]
|
|
13
|
+
output: str
|
|
18
14
|
|
|
19
|
-
def coalesce_print_events(trace: model.Trace) -> model.Trace:
|
|
20
|
-
last_print_item = None
|
|
21
|
-
for trace_item in trace:
|
|
22
|
-
match trace_item.event:
|
|
23
|
-
case model.PrintEvent(text):
|
|
24
|
-
if (
|
|
25
|
-
last_print_item is None
|
|
26
|
-
or trace_item.line_no != last_print_item.line_no
|
|
27
|
-
):
|
|
28
|
-
last_print_item = trace_item
|
|
29
|
-
else:
|
|
30
|
-
last_print_item.event.text += text
|
|
31
|
-
trace_item.event = None # Mark for deletion
|
|
32
|
-
case _:
|
|
33
|
-
last_print_item = None
|
|
34
15
|
|
|
35
|
-
|
|
16
|
+
Instants: TypeAlias = list[Instant]
|
|
36
17
|
|
|
37
18
|
|
|
38
|
-
def
|
|
19
|
+
def trace_to_instants(trace: model.Trace) -> Instants:
|
|
20
|
+
instants = []
|
|
21
|
+
instant = None
|
|
39
22
|
for trace_item in trace:
|
|
23
|
+
if instant is None or instant.line_no != trace_item.line_no:
|
|
24
|
+
instant = Instant(trace_item.line_no, {}, "")
|
|
25
|
+
instants.append(instant)
|
|
26
|
+
|
|
40
27
|
match trace_item.event:
|
|
41
|
-
case model.PrintEvent:
|
|
42
|
-
|
|
43
|
-
|
|
28
|
+
case model.PrintEvent(text):
|
|
29
|
+
instant.output += text
|
|
30
|
+
case model.VariableChangeEvent(variable, value):
|
|
31
|
+
# Don't clobber an existing value a variable if it changes again on the same line
|
|
32
|
+
# (this happens for example in tight loops)
|
|
33
|
+
if variable in instant.variable_changes:
|
|
34
|
+
instant = Instant(trace_item.line_no, {}, "")
|
|
35
|
+
instants.append(instant)
|
|
36
|
+
instant.variable_changes[variable] = value
|
|
37
|
+
case model.ReturnEvent():
|
|
38
|
+
pass # XXX
|
|
39
|
+
|
|
40
|
+
return instants
|
|
44
41
|
|
|
45
42
|
|
|
46
43
|
def variable_to_column_name(var: model.Variable) -> str:
|
|
47
|
-
|
|
48
|
-
return "(" + context + ") " + var.name if context else var.name
|
|
44
|
+
return var.name if var.scope == "<module>" else f"({var.scope}) {var.name}"
|
|
49
45
|
|
|
50
46
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
line_no: int
|
|
54
|
-
variable_changes: dict[str, Any]
|
|
55
|
-
output: str | None
|
|
47
|
+
def formatted_table_from_instants(instants: Instants) -> str:
|
|
48
|
+
variables = []
|
|
56
49
|
|
|
50
|
+
for instant in instants:
|
|
51
|
+
for variable in instant.variable_changes:
|
|
52
|
+
if variable not in variables:
|
|
53
|
+
variables.append(variable)
|
|
57
54
|
|
|
58
|
-
|
|
59
|
-
trace = coalesce_print_events(trace)
|
|
55
|
+
LINE_NO, OUTPUT = "ligne", "affichage"
|
|
60
56
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if current_line is None or current_line.line_no != trace_item.line_no:
|
|
66
|
-
current_line = Line(trace_item.line_no, {}, None)
|
|
67
|
-
lines.append(current_line)
|
|
57
|
+
table = []
|
|
58
|
+
for instant in instants:
|
|
59
|
+
row: dict[str, Any] = {}
|
|
60
|
+
table.append(row)
|
|
68
61
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
current_line = Line(trace_item.line_no, {}, None)
|
|
75
|
-
lines.append(current_line)
|
|
62
|
+
row[LINE_NO] = instant.line_no
|
|
63
|
+
for variable in variables:
|
|
64
|
+
cell_for_variable = instant.variable_changes.get(variable, "")
|
|
65
|
+
row[variable_to_column_name(variable)] = cell_for_variable
|
|
66
|
+
row[OUTPUT] = instant.output
|
|
76
67
|
|
|
77
|
-
|
|
78
|
-
case model.PrintEvent(text):
|
|
79
|
-
current_line.output = text
|
|
68
|
+
return tabulate(table, headers="keys", tablefmt="simple_outline")
|
|
80
69
|
|
|
81
|
-
# The extra spaces so the names cannot collide with variable names
|
|
82
|
-
LINE_NO, OUTPUT = " ligne", "affichage "
|
|
83
70
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
table_row = [line.line_no]
|
|
89
|
-
for column_name in headers[1:-1]:
|
|
90
|
-
variable_value = line.variable_changes.get(column_name)
|
|
91
|
-
if variable_value:
|
|
92
|
-
table_row.append(variable_value)
|
|
93
|
-
else:
|
|
94
|
-
table_row.append(None)
|
|
95
|
-
table_row.append(line.output)
|
|
96
|
-
table.append(table_row)
|
|
97
|
-
|
|
98
|
-
print(tabulate(table, headers=headers, tablefmt="outline"))
|
|
71
|
+
def dump_report(trace: model.Trace) -> None:
|
|
72
|
+
instants = trace_to_instants(trace)
|
|
73
|
+
formatted_table = formatted_table_from_instants(instants)
|
|
74
|
+
print(formatted_table)
|
atrace/vartracer.py
CHANGED
|
@@ -33,13 +33,21 @@ class VarTracer:
|
|
|
33
33
|
def __init__(self, trace: model.Trace, module_of_interest: ModuleType):
|
|
34
34
|
self.trace = trace
|
|
35
35
|
self.module_of_interest = module_of_interest
|
|
36
|
-
self.last_locals = {}
|
|
36
|
+
self.last_locals: dict[str, Any] = {}
|
|
37
37
|
|
|
38
38
|
def trace_vars(self, frame: FrameType, event: str, arg: Any):
|
|
39
39
|
if inspect.getmodule(frame) != self.module_of_interest:
|
|
40
40
|
return
|
|
41
41
|
if event == "return":
|
|
42
|
-
|
|
42
|
+
self.trace.append(
|
|
43
|
+
model.TraceItem(
|
|
44
|
+
line_no=frame.f_lineno,
|
|
45
|
+
function_name=frame.f_code.co_name,
|
|
46
|
+
event=model.ReturnEvent(
|
|
47
|
+
function_name=frame.f_code.co_name, return_value=arg # XXX
|
|
48
|
+
),
|
|
49
|
+
)
|
|
50
|
+
)
|
|
43
51
|
|
|
44
52
|
code = frame.f_code
|
|
45
53
|
|
|
@@ -57,9 +65,7 @@ class VarTracer:
|
|
|
57
65
|
line_no=frame.f_lineno,
|
|
58
66
|
function_name=frame.f_code.co_name,
|
|
59
67
|
event=model.VariableChangeEvent(
|
|
60
|
-
variable=model.Variable(
|
|
61
|
-
function_name=code.co_name, name=var
|
|
62
|
-
),
|
|
68
|
+
variable=model.Variable(scope=code.co_name, name=var),
|
|
63
69
|
value=new_val,
|
|
64
70
|
),
|
|
65
71
|
)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: atrace
|
|
3
|
+
Version: 0.1.6
|
|
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.10
|
|
8
|
+
Requires-Dist: tabulate>=0.9.0
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# Usage
|
|
12
|
+
|
|
13
|
+
Automatically prints a trace table of a program once the execution is finished.
|
|
14
|
+
|
|
15
|
+
Just import the module.
|
|
16
|
+
|
|
17
|
+
An idea of how things look:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Does not work with :
|
|
24
|
+
|
|
25
|
+
- Multithreaded programs
|
|
26
|
+
- Multi-module programs
|
|
27
|
+
- Classes
|
|
28
|
+
|
|
29
|
+
# TODO
|
|
30
|
+
|
|
31
|
+
- Unit tests for the capture part (because the line numbers are so hard). If pytest adds too much magic, then revert to UnitTest
|
|
32
|
+
- Fix line numbers in trace_vars. https://discuss.python.org/t/trace-a-line-after-the-line-but-not-only-before-the-line/89475/7
|
|
33
|
+
This might not even be possible. Start solving it without functions in the scope.
|
|
34
|
+
|
|
35
|
+
- Handle returns (with and without return statements)
|
|
36
|
+
|
|
37
|
+
- Thonny, which adds a lot of indirection and magic. Try and find a way to edit the package directly when running in thonny
|
|
38
|
+
|
|
39
|
+
# Later
|
|
40
|
+
|
|
41
|
+
- localize the names of the line and output columns in the report
|
|
42
|
+
|
|
43
|
+
# Done
|
|
44
|
+
|
|
45
|
+
- Sets the program print to stdout unhindered (this is important for input to work properly),
|
|
46
|
+
but captures the prints at the same time to show in the trace at the end.
|
|
47
|
+
- Emits the trace at the end if the application ends normally and abruptly (exception, signal, etc.)
|
|
48
|
+
- Shows bindings to local variables when entering a function
|
|
49
|
+
- Handles mutations to objects like lists (by copying the previous version and then comparing)
|
|
50
|
+
- Parallel assignations show up properly
|
|
51
|
+
- Changes to variables in other scopes (for instance global)
|
|
52
|
+
|
|
53
|
+
# Build
|
|
54
|
+
|
|
55
|
+
Automatically deployed to pypi every time a new tag is pushed: https://pypi.org/project/atrace/
|
|
56
|
+
|
|
57
|
+
# Inspiration
|
|
58
|
+
|
|
59
|
+
https://github.com/DarshanLakshman/PyTracerTool/blob/master/PyTracerTool/pytracertool.py
|
|
60
|
+
|
|
61
|
+
Does almost everything I want, but has many flaws:
|
|
62
|
+
|
|
63
|
+
- it chokes trying to deepcopy some objects
|
|
64
|
+
- it cannot be simply imported into a module
|
|
65
|
+
- it fumbles the handling of output (preventing programs with input from working properly).
|
|
66
|
+
- it repeats values for no good reason
|
|
67
|
+
- its line numbers are very confused
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
atrace/__init__.py,sha256=FBVbvNBcU8agaIv62OT0PjhccY2-bhe0mkWOKT1H2fg,2406
|
|
2
|
+
atrace/model.py,sha256=yhBJuciqRc4X_10QuHv18y4lB2N35m39DvpIpYYtiIQ,556
|
|
3
|
+
atrace/outputlogger.py,sha256=gAM15WF7DOVxc6ZDBnMmutUJ7okeRoc9JUC4iqJaN9I,928
|
|
4
|
+
atrace/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
atrace/report.py,sha256=P6fgj-dJUZYk1wbW_Q-MATXoedXUvZ28VR3RdBjU8dA,2240
|
|
6
|
+
atrace/vartracer.py,sha256=bmENaHoSnh8-kdlJMaopWj5W8kPHjKC2E4k2Rx03f0k,2316
|
|
7
|
+
atrace-0.1.6.dist-info/METADATA,sha256=vHaImexdGhE25FbLMO7e3vN3gyskzuub90fY6RIw9ZU,2200
|
|
8
|
+
atrace-0.1.6.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
9
|
+
atrace-0.1.6.dist-info/RECORD,,
|
atrace-0.1.5.dist-info/METADATA
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: atrace
|
|
3
|
-
Version: 0.1.5
|
|
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.10
|
|
8
|
-
Requires-Dist: tabulate>=0.9.0
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
10
|
-
|
|
11
|
-
# Usage
|
|
12
|
-
|
|
13
|
-
Automatically prints a trace table of a program once the execution is finished.
|
|
14
|
-
|
|
15
|
-
Just import the module.
|
|
16
|
-
|
|
17
|
-
An animated example of a trace table: https://www.101computing.net/using-trace-tables/
|
|
18
|
-
|
|
19
|
-
An idea of how things look:
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
+-----------+-----+-----+---------+---------------+-----------------------+-------------------------+
|
|
23
|
-
| ligne | x | y | (f) a | (f) b | (f) c | affichage |
|
|
24
|
-
+===========+=====+=====+=========+===============+=======================+=========================+
|
|
25
|
-
| 5 | 1 | 3 | | | | |
|
|
26
|
-
| 7 | 2 | | | | | |
|
|
27
|
-
| 5 | | 2 | | | | |
|
|
28
|
-
| 7 | 3 | | | | | |
|
|
29
|
-
| 5 | | 1 | | | | |
|
|
30
|
-
| 7 | 4 | | | | | |
|
|
31
|
-
| 5 | | | | | | |
|
|
32
|
-
| 9 | | | | | | somme: 4 |
|
|
33
|
-
| 12 | | | Bonjour | tout le monde | | |
|
|
34
|
-
| 14 | | | | | Bonjour tout le monde | Bonjour tout le monde ! |
|
|
35
|
-
+-----------+-----+-----+---------+---------------+-----------------------+-------------------------+
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
Does not work with :
|
|
39
|
-
|
|
40
|
-
- Multithreaded programs
|
|
41
|
-
- Multi-module programs
|
|
42
|
-
- Classes
|
|
43
|
-
|
|
44
|
-
# TODO
|
|
45
|
-
|
|
46
|
-
- Fix line numbers in trace_vars. https://discuss.python.org/t/trace-a-line-after-the-line-but-not-only-before-the-line/89475/7
|
|
47
|
-
This is a bitch
|
|
48
|
-
- Thonny, which adds a lot of indirection and magic
|
|
49
|
-
|
|
50
|
-
# Later
|
|
51
|
-
|
|
52
|
-
- Make robust. In other words should never raise an exception.
|
|
53
|
-
- mypy, unit-tests. Integrate in build pipeline
|
|
54
|
-
- localize the names of the line and output columns in the report
|
|
55
|
-
- Handle classes better
|
|
56
|
-
|
|
57
|
-
# Possible enhancements
|
|
58
|
-
|
|
59
|
-
- More details when recursive invocations
|
|
60
|
-
- Think about how to show returns
|
|
61
|
-
- Find if there could be a good use for colors in the trace
|
|
62
|
-
|
|
63
|
-
# Done
|
|
64
|
-
|
|
65
|
-
- Sets the program print to stdout unhindered (this is important for input to work properly),
|
|
66
|
-
but captures the prints at the same time to show in the trace at the end.
|
|
67
|
-
- Emits the trace at the end if the application ends normally and abruptly (exception, signal, etc.)
|
|
68
|
-
- Shows bindings to local variables when entering a function
|
|
69
|
-
- Handles mutations to objects like lists (by copying the previous version and then comparing)
|
|
70
|
-
- Parallel assignations show up properly
|
|
71
|
-
|
|
72
|
-
# Build
|
|
73
|
-
|
|
74
|
-
Automatically deployed to pypi every time a new tag is pushed: https://pypi.org/project/atrace/
|
|
75
|
-
|
|
76
|
-
# Inspiration
|
|
77
|
-
|
|
78
|
-
https://github.com/DarshanLakshman/PyTracerTool/blob/master/PyTracerTool/pytracertool.py
|
|
79
|
-
|
|
80
|
-
Does almost everything I want, but has many flaws:
|
|
81
|
-
|
|
82
|
-
- it chokes trying to deepcopy some objects
|
|
83
|
-
- it cannot be simply imported into a module
|
|
84
|
-
- it fumbles the handling of output (preventing programs with input from working properly).
|
|
85
|
-
- it repeats values for no good reason
|
|
86
|
-
- its line numbers are very confused
|
atrace-0.1.5.dist-info/RECORD
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
atrace/__init__.py,sha256=MdubXhWJW6S5OqDrA3-YYOjl91EomeoCmBC4MwInPmQ,1920
|
|
2
|
-
atrace/model.py,sha256=uNX2rjYOZYIATY_ZvXFYiD5FhLeQ02Bqy8ExnRFPiK8,441
|
|
3
|
-
atrace/outputlogger.py,sha256=ct1jlUlK03ZI5hy45aysdb2I9UDWPLK4hxRJ_aU83Xk,901
|
|
4
|
-
atrace/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
atrace/report.py,sha256=6p19dK3YWI8mn-HPiGRp55UE7ddJg7TjGgw1b2S0CCA,3350
|
|
6
|
-
atrace/vartracer.py,sha256=O3FPZhWl2nHcmtQHu1HECwP1ZF8X9XGNuk9xTe7Y1Og,2041
|
|
7
|
-
atrace-0.1.5.dist-info/METADATA,sha256=AtkCObDbIF95triDjCOqba1Xx93zWm7Wqb9MhKuh2Qc,3628
|
|
8
|
-
atrace-0.1.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
9
|
-
atrace-0.1.5.dist-info/RECORD,,
|
|
File without changes
|