oracletrace 2.0.0__tar.gz → 2.0.2__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.
Files changed (28) hide show
  1. oracletrace-2.0.2/PKG-INFO +242 -0
  2. oracletrace-2.0.2/README.md +206 -0
  3. oracletrace-2.0.2/oracletrace/cli.py +160 -0
  4. oracletrace-2.0.2/oracletrace/compare.py +80 -0
  5. oracletrace-2.0.2/oracletrace/py.typed +0 -0
  6. oracletrace-2.0.2/oracletrace/reporters/__init__.py +3 -0
  7. oracletrace-2.0.2/oracletrace/reporters/html.py +273 -0
  8. {oracletrace-2.0.0 → oracletrace-2.0.2}/oracletrace/tracer.py +89 -57
  9. oracletrace-2.0.2/oracletrace.egg-info/PKG-INFO +242 -0
  10. {oracletrace-2.0.0 → oracletrace-2.0.2}/oracletrace.egg-info/SOURCES.txt +6 -1
  11. {oracletrace-2.0.0 → oracletrace-2.0.2}/pyproject.toml +4 -1
  12. oracletrace-2.0.2/tests/test_cli.py +438 -0
  13. oracletrace-2.0.2/tests/test_compare.py +299 -0
  14. oracletrace-2.0.2/tests/test_html_reporter.py +125 -0
  15. oracletrace-2.0.0/PKG-INFO +0 -313
  16. oracletrace-2.0.0/README.md +0 -264
  17. oracletrace-2.0.0/oracletrace/cli.py +0 -123
  18. oracletrace-2.0.0/oracletrace/compare.py +0 -56
  19. oracletrace-2.0.0/oracletrace.egg-info/PKG-INFO +0 -313
  20. oracletrace-2.0.0/tests/test_cli.py +0 -233
  21. {oracletrace-2.0.0 → oracletrace-2.0.2}/LICENSE +0 -0
  22. {oracletrace-2.0.0 → oracletrace-2.0.2}/MANIFEST.in +0 -0
  23. {oracletrace-2.0.0 → oracletrace-2.0.2}/oracletrace/__init__.py +0 -0
  24. {oracletrace-2.0.0 → oracletrace-2.0.2}/oracletrace.egg-info/dependency_links.txt +0 -0
  25. {oracletrace-2.0.0 → oracletrace-2.0.2}/oracletrace.egg-info/entry_points.txt +0 -0
  26. {oracletrace-2.0.0 → oracletrace-2.0.2}/oracletrace.egg-info/requires.txt +0 -0
  27. {oracletrace-2.0.0 → oracletrace-2.0.2}/oracletrace.egg-info/top_level.txt +0 -0
  28. {oracletrace-2.0.0 → oracletrace-2.0.2}/setup.cfg +0 -0
@@ -0,0 +1,242 @@
1
+ Metadata-Version: 2.4
2
+ Name: oracletrace
3
+ Version: 2.0.2
4
+ Summary: Detect Python performance regressions and compare execution traces with lightweight call graph visualization
5
+ Author: Kayk Caputo, André Gustavo
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Kayk Caputo and André Gustavo
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ Project-URL: Homepage, https://kaykcaputo.github.io/oracletrace/
28
+ Project-URL: Repository, https://github.com/KaykCaputo/oracletrace
29
+ Project-URL: Documentation, https://kaykcaputo.github.io/oracletrace/
30
+ Project-URL: Issues, https://github.com/KaykCaputo/oracletrace/issues
31
+ Requires-Python: >=3.10
32
+ Description-Content-Type: text/markdown
33
+ License-File: LICENSE
34
+ Requires-Dist: rich
35
+ Dynamic: license-file
36
+
37
+ # OracleTrace — Detect Python Performance Regressions with Execution Diff
38
+
39
+ > Detect performance regressions between runs of your Python script in seconds.
40
+
41
+ <table><tr>
42
+ <td><img src="https://raw.githubusercontent.com/KaykCaputo/oracletrace/master/oracletracecat.png" alt="OracleTrace Logo" width="185"/></td>
43
+ <td>
44
+
45
+ **Fail your CI when performance regresses.**
46
+
47
+ OracleTrace is a **git diff for performance.**
48
+
49
+ **Run your script twice and instantly see what got slower — with function-level precision.**
50
+
51
+ </td>
52
+ </tr></table>
53
+
54
+ [![PyPI](https://img.shields.io/pypi/v/oracletrace?label=PyPI)](https://pypi.org/project/oracletrace)
55
+ [![PyPI Downloads](https://static.pepy.tech/personalized-badge/oracletrace?period=total\&units=INTERNATIONAL_SYSTEM\&left_color=BLACK\&right_color=GREEN\&left_text=downloads)](https://pepy.tech/projects/oracletrace)
56
+ [![GitHub Stars](https://img.shields.io/github/stars/KaykCaputo/oracletrace?style=social)](https://github.com/KaykCaputo/oracletrace/stargazers)
57
+ [![GitHub Forks](https://img.shields.io/github/forks/KaykCaputo/oracletrace?style=social)](https://github.com/KaykCaputo/oracletrace/network/members)
58
+ [![CI Tests](https://github.com/KaykCaputo/oracletrace/actions/workflows/tests.yml/badge.svg)](https://github.com/KaykCaputo/oracletrace/actions/workflows/tests.yml)
59
+
60
+ Documentation: [https://kaykcaputo.github.io/oracletrace/](https://kaykcaputo.github.io/oracletrace/)
61
+
62
+ **Featured in:** [PyCoder's Weekly #729](https://pycoders.com/issues/729) • [awesome-debugger](https://github.com/taowen/awesome-debugger) • [awesome-profiling](https://github.com/msaroufim/awesome-profiling)
63
+
64
+ ---
65
+ ### Installation
66
+ ```bash
67
+ pip install oracletrace
68
+ ```
69
+
70
+ ## Quick Start
71
+
72
+ ### 1. See where your program spends time instantly:
73
+ ```bash
74
+ oracletrace app.py
75
+ ```
76
+
77
+ ### 2. Compare runs and detect regressions:
78
+ ```bash
79
+ oracletrace app.py --json baseline.json
80
+ oracletrace app.py --json new.json --compare baseline.json
81
+ ```
82
+
83
+ ---
84
+
85
+ ## See it in action
86
+
87
+ See exactly which functions got slower between runs:
88
+
89
+ ![OracleTrace CLI demo](https://raw.githubusercontent.com/KaykCaputo/oracletrace/master/oracletrace-cli-demo.gif)
90
+
91
+ ---
92
+
93
+ ## Example Output
94
+
95
+ ```
96
+ Starting application...
97
+
98
+ Iteration 1:
99
+ > Processing data...
100
+ > Calculating results...
101
+
102
+ Iteration 2:
103
+ > Processing data...
104
+ > Calculating results...
105
+
106
+ Application finished.
107
+
108
+ Summary:
109
+ Top functions by Total Time
110
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┓
111
+ ┃ Function ┃ Total Time (s) ┃ Calls ┃ Avg. Time/Call (ms) ┃
112
+ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━┩
113
+ │ my_app.py:main │ 0.6025 │ 1 │ 602.510 │
114
+ │ my_app.py:process_data │ 0.6021 │ 2 │ 301.050 │
115
+ │ my_app.py:calculate_results │ 0.4015 │ 2 │ 200.750 │
116
+ └──────────────────────────────┴────────────────┴───────┴─────────────────────┘
117
+
118
+ Logic Flow:
119
+ <module>
120
+ └── my_app.py:main (1x, 0.6025s)
121
+ └── my_app.py:process_data (2x, 0.6021s)
122
+ └── my_app.py:calculate_results (2x, 0.4015s)
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Why OracleTrace?
128
+
129
+ ### Problem
130
+
131
+ Performance regressions are hard to detect early.
132
+
133
+ ### Solution
134
+
135
+ OracleTrace compares execution traces and highlights what got slower.
136
+
137
+ ### How it works
138
+
139
+ 1. Run your script
140
+ 2. Generate a trace
141
+ 3. Compare results
142
+ 4. Identify slowdowns
143
+
144
+ ---
145
+
146
+ ## CI Integration
147
+
148
+ Fail your pipeline when performance degrades:
149
+
150
+ ```bash
151
+ oracletrace app.py --json current.json --compare baseline.json --fail-on-regression --threshold 25
152
+ ```
153
+ Add it to your CI to automatically fail on performance regressions.
154
+
155
+ ---
156
+
157
+ ## Key Features
158
+
159
+ * Detect slower and faster functions
160
+ * Identify new or removed functions
161
+ * Execution time and call count analysis
162
+ * Call graph visualization
163
+ * JSON and CSV export
164
+ * Regex-based filtering (`--ignore`)
165
+ * Top-N function focus (`--top`)
166
+ * CI regression gates
167
+
168
+ ---
169
+
170
+ ## CLI Reference
171
+
172
+ | Flag | Description |
173
+ | ---------------------- | -------------------------------------- |
174
+ | `--json` | Export trace to JSON |
175
+ | `--csv` | Export trace to CSV |
176
+ | `--compare` | Compare with another trace |
177
+ | `--fail-on-regression` | Exit with error if regression detected |
178
+ | `--threshold` | Regression percentage threshold |
179
+ | `--ignore` | Ignore functions/files via regex |
180
+ | `--top` | Show top N functions |
181
+
182
+ ---
183
+
184
+ ## Use Cases
185
+
186
+ ### Primary
187
+
188
+ * Detect performance regressions between runs
189
+
190
+ ### Secondary
191
+
192
+ * CI performance validation
193
+ * Execution trace inspection
194
+ * Call graph visualization
195
+
196
+ ---
197
+
198
+ ## How It Works
199
+
200
+ OracleTrace uses Python’s `sys.setprofile()` to intercept function calls and returns.
201
+
202
+ It measures execution time per function and records caller–callee relationships.
203
+
204
+ Filtering removes external/internal calls to focus on application code.
205
+
206
+ ---
207
+
208
+ ## Requirements
209
+
210
+ * Python >= 3.10
211
+ * rich
212
+
213
+ ---
214
+
215
+ ## Contributing
216
+
217
+ Contributions are welcome.
218
+
219
+ Please read the [Contributing Guide](CONTRIBUTING.md) for details on how to get started, coding standards, and the contribution process.
220
+
221
+ ---
222
+
223
+ ## Contributors
224
+
225
+ <a href="https://github.com/KaykCaputo/oracletrace/graphs/contributors">
226
+ <img src="https://contrib.rocks/image?repo=KaykCaputo/oracletrace" />
227
+ </a>
228
+
229
+ ---
230
+
231
+ ## ⭐ Support the Project
232
+
233
+ If OracleTrace is useful, consider giving it a star:
234
+
235
+ [GitHub Repository](https://github.com/KaykCaputo/oracletrace)
236
+
237
+ ---
238
+
239
+ ## Maintainers
240
+
241
+ * [Kayk Caputo](https://github.com/KaykCaputo)
242
+ * [André Gustavo](https://github.com/AndreXP1)
@@ -0,0 +1,206 @@
1
+ # OracleTrace — Detect Python Performance Regressions with Execution Diff
2
+
3
+ > Detect performance regressions between runs of your Python script in seconds.
4
+
5
+ <table><tr>
6
+ <td><img src="https://raw.githubusercontent.com/KaykCaputo/oracletrace/master/oracletracecat.png" alt="OracleTrace Logo" width="185"/></td>
7
+ <td>
8
+
9
+ **Fail your CI when performance regresses.**
10
+
11
+ OracleTrace is a **git diff for performance.**
12
+
13
+ **Run your script twice and instantly see what got slower — with function-level precision.**
14
+
15
+ </td>
16
+ </tr></table>
17
+
18
+ [![PyPI](https://img.shields.io/pypi/v/oracletrace?label=PyPI)](https://pypi.org/project/oracletrace)
19
+ [![PyPI Downloads](https://static.pepy.tech/personalized-badge/oracletrace?period=total\&units=INTERNATIONAL_SYSTEM\&left_color=BLACK\&right_color=GREEN\&left_text=downloads)](https://pepy.tech/projects/oracletrace)
20
+ [![GitHub Stars](https://img.shields.io/github/stars/KaykCaputo/oracletrace?style=social)](https://github.com/KaykCaputo/oracletrace/stargazers)
21
+ [![GitHub Forks](https://img.shields.io/github/forks/KaykCaputo/oracletrace?style=social)](https://github.com/KaykCaputo/oracletrace/network/members)
22
+ [![CI Tests](https://github.com/KaykCaputo/oracletrace/actions/workflows/tests.yml/badge.svg)](https://github.com/KaykCaputo/oracletrace/actions/workflows/tests.yml)
23
+
24
+ Documentation: [https://kaykcaputo.github.io/oracletrace/](https://kaykcaputo.github.io/oracletrace/)
25
+
26
+ **Featured in:** [PyCoder's Weekly #729](https://pycoders.com/issues/729) • [awesome-debugger](https://github.com/taowen/awesome-debugger) • [awesome-profiling](https://github.com/msaroufim/awesome-profiling)
27
+
28
+ ---
29
+ ### Installation
30
+ ```bash
31
+ pip install oracletrace
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ### 1. See where your program spends time instantly:
37
+ ```bash
38
+ oracletrace app.py
39
+ ```
40
+
41
+ ### 2. Compare runs and detect regressions:
42
+ ```bash
43
+ oracletrace app.py --json baseline.json
44
+ oracletrace app.py --json new.json --compare baseline.json
45
+ ```
46
+
47
+ ---
48
+
49
+ ## See it in action
50
+
51
+ See exactly which functions got slower between runs:
52
+
53
+ ![OracleTrace CLI demo](https://raw.githubusercontent.com/KaykCaputo/oracletrace/master/oracletrace-cli-demo.gif)
54
+
55
+ ---
56
+
57
+ ## Example Output
58
+
59
+ ```
60
+ Starting application...
61
+
62
+ Iteration 1:
63
+ > Processing data...
64
+ > Calculating results...
65
+
66
+ Iteration 2:
67
+ > Processing data...
68
+ > Calculating results...
69
+
70
+ Application finished.
71
+
72
+ Summary:
73
+ Top functions by Total Time
74
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┓
75
+ ┃ Function ┃ Total Time (s) ┃ Calls ┃ Avg. Time/Call (ms) ┃
76
+ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━┩
77
+ │ my_app.py:main │ 0.6025 │ 1 │ 602.510 │
78
+ │ my_app.py:process_data │ 0.6021 │ 2 │ 301.050 │
79
+ │ my_app.py:calculate_results │ 0.4015 │ 2 │ 200.750 │
80
+ └──────────────────────────────┴────────────────┴───────┴─────────────────────┘
81
+
82
+ Logic Flow:
83
+ <module>
84
+ └── my_app.py:main (1x, 0.6025s)
85
+ └── my_app.py:process_data (2x, 0.6021s)
86
+ └── my_app.py:calculate_results (2x, 0.4015s)
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Why OracleTrace?
92
+
93
+ ### Problem
94
+
95
+ Performance regressions are hard to detect early.
96
+
97
+ ### Solution
98
+
99
+ OracleTrace compares execution traces and highlights what got slower.
100
+
101
+ ### How it works
102
+
103
+ 1. Run your script
104
+ 2. Generate a trace
105
+ 3. Compare results
106
+ 4. Identify slowdowns
107
+
108
+ ---
109
+
110
+ ## CI Integration
111
+
112
+ Fail your pipeline when performance degrades:
113
+
114
+ ```bash
115
+ oracletrace app.py --json current.json --compare baseline.json --fail-on-regression --threshold 25
116
+ ```
117
+ Add it to your CI to automatically fail on performance regressions.
118
+
119
+ ---
120
+
121
+ ## Key Features
122
+
123
+ * Detect slower and faster functions
124
+ * Identify new or removed functions
125
+ * Execution time and call count analysis
126
+ * Call graph visualization
127
+ * JSON and CSV export
128
+ * Regex-based filtering (`--ignore`)
129
+ * Top-N function focus (`--top`)
130
+ * CI regression gates
131
+
132
+ ---
133
+
134
+ ## CLI Reference
135
+
136
+ | Flag | Description |
137
+ | ---------------------- | -------------------------------------- |
138
+ | `--json` | Export trace to JSON |
139
+ | `--csv` | Export trace to CSV |
140
+ | `--compare` | Compare with another trace |
141
+ | `--fail-on-regression` | Exit with error if regression detected |
142
+ | `--threshold` | Regression percentage threshold |
143
+ | `--ignore` | Ignore functions/files via regex |
144
+ | `--top` | Show top N functions |
145
+
146
+ ---
147
+
148
+ ## Use Cases
149
+
150
+ ### Primary
151
+
152
+ * Detect performance regressions between runs
153
+
154
+ ### Secondary
155
+
156
+ * CI performance validation
157
+ * Execution trace inspection
158
+ * Call graph visualization
159
+
160
+ ---
161
+
162
+ ## How It Works
163
+
164
+ OracleTrace uses Python’s `sys.setprofile()` to intercept function calls and returns.
165
+
166
+ It measures execution time per function and records caller–callee relationships.
167
+
168
+ Filtering removes external/internal calls to focus on application code.
169
+
170
+ ---
171
+
172
+ ## Requirements
173
+
174
+ * Python >= 3.10
175
+ * rich
176
+
177
+ ---
178
+
179
+ ## Contributing
180
+
181
+ Contributions are welcome.
182
+
183
+ Please read the [Contributing Guide](CONTRIBUTING.md) for details on how to get started, coding standards, and the contribution process.
184
+
185
+ ---
186
+
187
+ ## Contributors
188
+
189
+ <a href="https://github.com/KaykCaputo/oracletrace/graphs/contributors">
190
+ <img src="https://contrib.rocks/image?repo=KaykCaputo/oracletrace" />
191
+ </a>
192
+
193
+ ---
194
+
195
+ ## ⭐ Support the Project
196
+
197
+ If OracleTrace is useful, consider giving it a star:
198
+
199
+ [GitHub Repository](https://github.com/KaykCaputo/oracletrace)
200
+
201
+ ---
202
+
203
+ ## Maintainers
204
+
205
+ * [Kayk Caputo](https://github.com/KaykCaputo)
206
+ * [André Gustavo](https://github.com/AndreXP1)
@@ -0,0 +1,160 @@
1
+ import re
2
+ import sys
3
+ import os
4
+ import json
5
+ import runpy
6
+ import csv
7
+ from dataclasses import asdict
8
+ from typing import List, Dict, Any, Optional
9
+ from re import Pattern
10
+ from argparse import ArgumentParser, Namespace
11
+ from importlib.metadata import version
12
+ from .tracer import Tracer, TracerData
13
+ from .compare import compare_traces, ComparisonData
14
+ from .reporters import generate_html_report
15
+
16
+
17
+ def main() -> int:
18
+ module_name = __name__.split(".")[0]
19
+ parser: ArgumentParser = ArgumentParser(
20
+ prog=module_name,
21
+ description="OracleTrace - Lightweight execution tracer for Python projects"
22
+ )
23
+ parser.add_argument("target", help="Python script to trace")
24
+ parser.add_argument("--json", help="Export trace result to JSON file")
25
+ parser.add_argument("--compare", help="Compare against previous trace JSON")
26
+ parser.add_argument("--csv", help="Export trace result to CSV file")
27
+ parser.add_argument("--html", help="Export trace result to interactive HTML file")
28
+ parser.add_argument(
29
+ "--ignore",
30
+ metavar="REGEX",
31
+ nargs="+",
32
+ help="Space separated list of regex patterns for keys (file path and function name) to ignore."
33
+ )
34
+ parser.add_argument(
35
+ "--top",
36
+ metavar="NUMBER",
37
+ help="Limits the number of functions shown in the summary table"
38
+ )
39
+ parser.add_argument(
40
+ "--fail-on-regression",
41
+ action="store_true",
42
+ help="Exit with a non-zero code when regression exceeds threshold.",
43
+ )
44
+ parser.add_argument(
45
+ "--threshold",
46
+ type=float,
47
+ default=5.0,
48
+ help="Regression threshold percentage used with --fail-on-regression.",
49
+ )
50
+ parser.add_argument(
51
+ "--only-regressions",
52
+ action="store_true",
53
+ help="Hide functions which didn't run slower than baseline. Use with --compare"
54
+ )
55
+
56
+ parser.add_argument(
57
+ "--version",
58
+ action="version",
59
+ help="Print version information then exit with a zero code",
60
+ version=f"%(prog)s {version(module_name)}"
61
+ )
62
+ args: Namespace = parser.parse_args()
63
+
64
+ target: str = args.target
65
+
66
+ # Validate --top argument manually to ensure all paths are testable
67
+ if args.top is not None:
68
+ try:
69
+ args.top = int(args.top)
70
+ except ValueError:
71
+ print(f"argument --top: invalid int value: '{args.top}'", file=sys.stderr)
72
+ return 2
73
+ if args.top < 1:
74
+ print(f"--top must be a positive integer, got: {args.top}", file=sys.stderr)
75
+ return 1
76
+
77
+ if not os.path.exists(target):
78
+ print(f"Target not found: {target}", file=sys.stderr)
79
+ return 1
80
+
81
+ target = os.path.abspath(target)
82
+ root: str = os.getcwd()
83
+ target_dir: str = os.path.dirname(target)
84
+ # Setup paths so imports work correctly in the target script
85
+ sys.path.insert(0, target_dir)
86
+ ignored_args: List[str] = [] if args.ignore is None else args.ignore
87
+ ignore_patterns: List[Pattern] = []
88
+
89
+ for pattern in ignored_args:
90
+ try:
91
+ ignore_patterns.append(re.compile(pattern))
92
+ except re.error as e:
93
+ print(f"Regex error: {pattern} -> {e}", file=sys.stderr)
94
+ return 1
95
+
96
+ # Start tracing, run the script, then stop
97
+ tracer: Tracer = Tracer(root, ignore_patterns=ignore_patterns)
98
+ tracer.start()
99
+ try:
100
+ runpy.run_path(target, run_name="__main__")
101
+ finally:
102
+ tracer.stop()
103
+
104
+ data: TracerData = tracer.get_trace_data()
105
+
106
+ # Save json
107
+ if args.json:
108
+ with open(args.json, "w", encoding="utf-8") as f:
109
+ json.dump(asdict(data), f, indent=4)
110
+
111
+ # Display the analysis
112
+ if args.top is not None:
113
+ tracer.show_results(args.top)
114
+ else:
115
+ tracer.show_results(None)
116
+
117
+ # Export as csv
118
+ if args.csv:
119
+ with open(args.csv, "w", newline="", encoding="utf-8") as f:
120
+ writer: csv.DictWriter = csv.DictWriter(f, fieldnames=["function", "total_time", "calls", "avg_time"])
121
+ writer.writeheader()
122
+ for fn in data.functions:
123
+ writer.writerow({
124
+ "function": fn.name,
125
+ "total_time": fn.total_time,
126
+ "calls": fn.call_count,
127
+ "avg_time": fn.avg_time,
128
+ })
129
+
130
+ # Generare a report as html
131
+ if args.html:
132
+ generate_html_report(data, args.html)
133
+ print(f"HTML report generated: {os.path.abspath(args.html)}")
134
+
135
+ comparison_result: Optional[ComparisonData] = None
136
+
137
+ # Compare jsons
138
+ if args.compare:
139
+ if not os.path.exists(args.compare):
140
+ print(f"Compare file not found: {args.compare}", file=sys.stderr)
141
+ return 1
142
+
143
+ with open(args.compare, "r", encoding="utf-8") as f:
144
+ old_data: TracerData = TracerData.from_dict(json.load(f))
145
+
146
+ comparison_result = compare_traces(old_data, data, threshold=args.threshold, show_only_regressions=args.only_regressions)
147
+
148
+ if args.fail_on_regression and comparison_result.has_regression:
149
+ print(
150
+ f"Build failed: performance regression above {args.threshold:.2f}% detected.",
151
+ file=sys.stderr,
152
+ )
153
+ return 2
154
+
155
+
156
+ return 0
157
+
158
+
159
+ if __name__ == "__main__":
160
+ sys.exit(main())
@@ -0,0 +1,80 @@
1
+ from .tracer import TracerData, FunctionData
2
+ from rich import print
3
+ from typing import Any, Dict, List, Set, Optional
4
+ from dataclasses import dataclass
5
+
6
+ @dataclass
7
+ class RegressionData:
8
+ name: str
9
+ old_time: float
10
+ new_time: float
11
+ percent: float
12
+
13
+ @dataclass
14
+ class ComparisonData:
15
+ regressions: List[RegressionData]
16
+ has_regression: bool
17
+
18
+ def compare_traces(
19
+ old_data: TracerData,
20
+ new_data: TracerData,
21
+ threshold: float = 5.0,
22
+ show_only_regressions: bool = False
23
+ ) -> ComparisonData:
24
+ old_funcs: Dict[str, FunctionData] = {f.name: f for f in old_data.functions}
25
+ new_funcs: Dict[str, FunctionData] = {f.name: f for f in new_data.functions}
26
+
27
+ regressions: List[RegressionData] = []
28
+
29
+ print("\n[bold cyan]Comparison Results:[/]\n")
30
+
31
+ all_functions: Set[str] = set(old_funcs) | set(new_funcs)
32
+
33
+ for name in sorted(all_functions):
34
+ old: Optional[FunctionData] = old_funcs.get(name)
35
+ new: Optional[FunctionData] = new_funcs.get(name)
36
+
37
+ if not old:
38
+ print(f"[green]+ {name} (new function)[/]")
39
+ continue
40
+
41
+ if not new:
42
+ print(f"[red]- {name} (removed)[/]")
43
+ continue
44
+
45
+ old_time: float = old.total_time
46
+ new_time: float = new.total_time
47
+
48
+ if old_time == 0:
49
+ if new_time == 0:
50
+ print(f"{name} [yellow](no signal)[/]")
51
+ continue
52
+ print(f"{name} [yellow](no baseline)[/]")
53
+ continue
54
+
55
+ diff: float = new_time - old_time
56
+ percent: float = (diff / old_time) * 100
57
+
58
+ color: str = "red" if percent > threshold else "green" if percent < -threshold else "yellow"
59
+
60
+ print(
61
+ f"{name}\n"
62
+ f" total_time: {old_time:.4f}s → {new_time:.4f}s "
63
+ f"[{color}]({percent:+.2f}%)[/]\n"
64
+ ) if not show_only_regressions or percent > threshold else ...
65
+
66
+
67
+ if percent > threshold:
68
+ regressions.append(
69
+ RegressionData(
70
+ name = name,
71
+ new_time = new_time,
72
+ old_time = old_time,
73
+ percent = percent
74
+ )
75
+ )
76
+
77
+ return ComparisonData(
78
+ regressions = regressions,
79
+ has_regression = len(regressions) > 0
80
+ )
File without changes
@@ -0,0 +1,3 @@
1
+ from .html import generate_html_report
2
+
3
+ __all__ = ["generate_html_report"]