pytrackio 0.1.0__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.
- pytrackio-0.1.0/LICENSE +17 -0
- pytrackio-0.1.0/PKG-INFO +288 -0
- pytrackio-0.1.0/README.md +264 -0
- pytrackio-0.1.0/pyproject.toml +42 -0
- pytrackio-0.1.0/pytrackio/__init__.py +24 -0
- pytrackio-0.1.0/pytrackio/_instruments.py +66 -0
- pytrackio-0.1.0/pytrackio/_report.py +68 -0
- pytrackio-0.1.0/pytrackio/_tracker.py +88 -0
- pytrackio-0.1.0/pytrackio.egg-info/PKG-INFO +288 -0
- pytrackio-0.1.0/pytrackio.egg-info/SOURCES.txt +12 -0
- pytrackio-0.1.0/pytrackio.egg-info/dependency_links.txt +1 -0
- pytrackio-0.1.0/pytrackio.egg-info/top_level.txt +1 -0
- pytrackio-0.1.0/setup.cfg +4 -0
- pytrackio-0.1.0/tests/test_pytrackio.py +307 -0
pytrackio-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Deepanshu (danshu3007-lang)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
pytrackio-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pytrackio
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Zero-dependency Python performance tracker. Decorate, time, count — then report.
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/danshu3007-lang/pytrackio
|
|
7
|
+
Project-URL: Repository, https://github.com/danshu3007-lang/pytrackio
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/danshu3007-lang/pytrackio/issues
|
|
9
|
+
Keywords: metrics,performance,tracking,profiling,monitoring,decorator,timer,benchmarking
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Classifier: Topic :: System :: Monitoring
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
# pytrackio
|
|
26
|
+
|
|
27
|
+
> Zero-dependency Python performance tracker. Decorate, time, count — then report.
|
|
28
|
+
|
|
29
|
+
[](https://github.com/danshu3007-lang/pytrackio/actions)
|
|
30
|
+
[](https://pypi.org/project/pytrackio/)
|
|
31
|
+
[](https://www.python.org/)
|
|
32
|
+
[](LICENSE)
|
|
33
|
+
[](pyproject.toml)
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from pytrackio import track, timer, counter, report
|
|
37
|
+
|
|
38
|
+
@track
|
|
39
|
+
def fetch_user(user_id: int):
|
|
40
|
+
... # your existing code, unchanged
|
|
41
|
+
|
|
42
|
+
with timer("database_query"):
|
|
43
|
+
results = db.execute(query)
|
|
44
|
+
|
|
45
|
+
counter("api_calls").increment()
|
|
46
|
+
|
|
47
|
+
report()
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
╔════════════════════════════════════════════════════════════════════════╗
|
|
52
|
+
║ pytrackio — Performance Report ║
|
|
53
|
+
║ uptime: 4.21s ║
|
|
54
|
+
╠════════════════════════════════════════════════════════════════════════╣
|
|
55
|
+
║ Function / Block Calls Avg (ms) Min(ms) Max (ms) Errors ║
|
|
56
|
+
╠════════════════════════════════════════════════════════════════════════╣
|
|
57
|
+
║ fetch_user 42 120.34 98.10 310.50 — ║
|
|
58
|
+
║ database_query 18 45.20 40.10 89.30 — ║
|
|
59
|
+
╠════════════════════════════════════════════════════════════════════════╣
|
|
60
|
+
║ Counters ║
|
|
61
|
+
╠────────────────────────────────────────────────────────────────────────╣
|
|
62
|
+
║ api_calls: 42 ║
|
|
63
|
+
╚════════════════════════════════════════════════════════════════════════╝
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Why pytrackio?
|
|
69
|
+
|
|
70
|
+
Every other Python metrics tool requires Prometheus, StatsD, Grafana, or some external server. Setting them up takes hours.
|
|
71
|
+
|
|
72
|
+
**pytrackio works in 30 seconds:**
|
|
73
|
+
- ✅ Zero dependencies — pure Python stdlib only
|
|
74
|
+
- ✅ Zero configuration — no config files, no servers
|
|
75
|
+
- ✅ Zero changes to your logic — just add one decorator
|
|
76
|
+
- ✅ Thread-safe — works in concurrent applications
|
|
77
|
+
- ✅ Production-ready — proper error tracking, not just happy-path
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Installation
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pip install pytrackio
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Usage
|
|
90
|
+
|
|
91
|
+
### Track a function automatically
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from pytrackio import track
|
|
95
|
+
|
|
96
|
+
@track
|
|
97
|
+
def process_order(order_id: int):
|
|
98
|
+
# your code here — nothing else changes
|
|
99
|
+
...
|
|
100
|
+
|
|
101
|
+
# Call it normally
|
|
102
|
+
process_order(123)
|
|
103
|
+
process_order(456)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Custom metric name
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
@track(name="payment_gateway")
|
|
110
|
+
def charge_card(amount: float):
|
|
111
|
+
...
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Time any block of code
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from pytrackio import timer
|
|
118
|
+
|
|
119
|
+
with timer("image_resize"):
|
|
120
|
+
resized = resize_image(img, width=800)
|
|
121
|
+
|
|
122
|
+
with timer("send_email"):
|
|
123
|
+
mailer.send(to=user.email, body=html)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Count events
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from pytrackio import counter
|
|
130
|
+
|
|
131
|
+
counter("cache_hits").increment()
|
|
132
|
+
counter("cache_misses").increment()
|
|
133
|
+
counter("retries").increment(3) # increment by N
|
|
134
|
+
counter("queue_size").decrement() # decrement
|
|
135
|
+
|
|
136
|
+
# Read the value anywhere
|
|
137
|
+
print(counter("cache_hits").value)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Print the report
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from pytrackio import report
|
|
144
|
+
|
|
145
|
+
report() # print to stdout
|
|
146
|
+
report(show_counters=False) # hide counters section
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Access raw data programmatically
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from pytrackio import get_registry
|
|
153
|
+
|
|
154
|
+
registry = get_registry()
|
|
155
|
+
|
|
156
|
+
# Get summary for one metric
|
|
157
|
+
s = registry.summary("payment_gateway")
|
|
158
|
+
print(s.calls, s.avg_ms, s.error_rate)
|
|
159
|
+
|
|
160
|
+
# Get all summaries
|
|
161
|
+
for s in registry.all_summaries():
|
|
162
|
+
if s.error_rate > 5.0:
|
|
163
|
+
alert(f"{s.name} has {s.error_rate}% errors!")
|
|
164
|
+
|
|
165
|
+
# Reset between test runs
|
|
166
|
+
registry.reset()
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Real-world example
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
from pytrackio import track, timer, counter, report
|
|
175
|
+
import requests as http
|
|
176
|
+
|
|
177
|
+
@track
|
|
178
|
+
def get_weather(city: str) -> dict:
|
|
179
|
+
r = http.get(f"https://api.example.com/weather?city={city}", timeout=5)
|
|
180
|
+
r.raise_for_status()
|
|
181
|
+
counter("api_calls").increment()
|
|
182
|
+
return r.json()
|
|
183
|
+
|
|
184
|
+
@track
|
|
185
|
+
def process_weather(data: dict) -> str:
|
|
186
|
+
with timer("format_output"):
|
|
187
|
+
return f"{data['city']}: {data['temp']}°C"
|
|
188
|
+
|
|
189
|
+
# Run your app...
|
|
190
|
+
for city in ["Delhi", "London", "Tokyo"]:
|
|
191
|
+
data = get_weather(city)
|
|
192
|
+
print(process_weather(data))
|
|
193
|
+
|
|
194
|
+
# See how everything performed
|
|
195
|
+
report()
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## API Reference
|
|
201
|
+
|
|
202
|
+
### `@track`
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
@track
|
|
206
|
+
def my_func(): ...
|
|
207
|
+
|
|
208
|
+
@track(name="custom_name")
|
|
209
|
+
def my_func(): ...
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Tracks: call count, duration (min/max/avg), errors and error rate.
|
|
213
|
+
Never swallows exceptions — your original errors always propagate.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
### `timer(name)`
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
with timer("block_name"):
|
|
221
|
+
...
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Context manager. Records duration and any exceptions raised inside the block.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
### `counter(name)`
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
counter("name").increment() # +1
|
|
232
|
+
counter("name").increment(n) # +n
|
|
233
|
+
counter("name").decrement() # -1
|
|
234
|
+
counter("name").reset() # → 0
|
|
235
|
+
counter("name").value # read current value
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
### `report(stream, colour, show_counters)`
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
report()
|
|
244
|
+
report(show_counters=False)
|
|
245
|
+
report(colour=False) # force plain text
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Prints to stdout and returns the output as a string.
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
### `get_registry()`
|
|
253
|
+
|
|
254
|
+
Returns the global `MetricsRegistry` instance for programmatic access.
|
|
255
|
+
|
|
256
|
+
```python
|
|
257
|
+
registry = get_registry()
|
|
258
|
+
registry.all_summaries() # List[MetricSummary]
|
|
259
|
+
registry.all_counters() # List[CounterState]
|
|
260
|
+
registry.summary("name") # Optional[MetricSummary]
|
|
261
|
+
registry.reset() # clear everything
|
|
262
|
+
registry.uptime_seconds() # float
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Contributing
|
|
268
|
+
|
|
269
|
+
Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
270
|
+
|
|
271
|
+
**Ideas for contributions:**
|
|
272
|
+
- Async support (`@track` for `async def` functions)
|
|
273
|
+
- Export to JSON / CSV
|
|
274
|
+
- Histogram bucketing
|
|
275
|
+
- `@track` for class methods
|
|
276
|
+
- Minimum call threshold filter in report
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## Built by
|
|
281
|
+
|
|
282
|
+
**Deepanshu** — BCA Student at Chandigarh University, aspiring Data Analyst.
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## License
|
|
287
|
+
|
|
288
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# pytrackio
|
|
2
|
+
|
|
3
|
+
> Zero-dependency Python performance tracker. Decorate, time, count — then report.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/danshu3007-lang/pytrackio/actions)
|
|
6
|
+
[](https://pypi.org/project/pytrackio/)
|
|
7
|
+
[](https://www.python.org/)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](pyproject.toml)
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
from pytrackio import track, timer, counter, report
|
|
13
|
+
|
|
14
|
+
@track
|
|
15
|
+
def fetch_user(user_id: int):
|
|
16
|
+
... # your existing code, unchanged
|
|
17
|
+
|
|
18
|
+
with timer("database_query"):
|
|
19
|
+
results = db.execute(query)
|
|
20
|
+
|
|
21
|
+
counter("api_calls").increment()
|
|
22
|
+
|
|
23
|
+
report()
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
╔════════════════════════════════════════════════════════════════════════╗
|
|
28
|
+
║ pytrackio — Performance Report ║
|
|
29
|
+
║ uptime: 4.21s ║
|
|
30
|
+
╠════════════════════════════════════════════════════════════════════════╣
|
|
31
|
+
║ Function / Block Calls Avg (ms) Min(ms) Max (ms) Errors ║
|
|
32
|
+
╠════════════════════════════════════════════════════════════════════════╣
|
|
33
|
+
║ fetch_user 42 120.34 98.10 310.50 — ║
|
|
34
|
+
║ database_query 18 45.20 40.10 89.30 — ║
|
|
35
|
+
╠════════════════════════════════════════════════════════════════════════╣
|
|
36
|
+
║ Counters ║
|
|
37
|
+
╠────────────────────────────────────────────────────────────────────────╣
|
|
38
|
+
║ api_calls: 42 ║
|
|
39
|
+
╚════════════════════════════════════════════════════════════════════════╝
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Why pytrackio?
|
|
45
|
+
|
|
46
|
+
Every other Python metrics tool requires Prometheus, StatsD, Grafana, or some external server. Setting them up takes hours.
|
|
47
|
+
|
|
48
|
+
**pytrackio works in 30 seconds:**
|
|
49
|
+
- ✅ Zero dependencies — pure Python stdlib only
|
|
50
|
+
- ✅ Zero configuration — no config files, no servers
|
|
51
|
+
- ✅ Zero changes to your logic — just add one decorator
|
|
52
|
+
- ✅ Thread-safe — works in concurrent applications
|
|
53
|
+
- ✅ Production-ready — proper error tracking, not just happy-path
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install pytrackio
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Usage
|
|
66
|
+
|
|
67
|
+
### Track a function automatically
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from pytrackio import track
|
|
71
|
+
|
|
72
|
+
@track
|
|
73
|
+
def process_order(order_id: int):
|
|
74
|
+
# your code here — nothing else changes
|
|
75
|
+
...
|
|
76
|
+
|
|
77
|
+
# Call it normally
|
|
78
|
+
process_order(123)
|
|
79
|
+
process_order(456)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Custom metric name
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
@track(name="payment_gateway")
|
|
86
|
+
def charge_card(amount: float):
|
|
87
|
+
...
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Time any block of code
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from pytrackio import timer
|
|
94
|
+
|
|
95
|
+
with timer("image_resize"):
|
|
96
|
+
resized = resize_image(img, width=800)
|
|
97
|
+
|
|
98
|
+
with timer("send_email"):
|
|
99
|
+
mailer.send(to=user.email, body=html)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Count events
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from pytrackio import counter
|
|
106
|
+
|
|
107
|
+
counter("cache_hits").increment()
|
|
108
|
+
counter("cache_misses").increment()
|
|
109
|
+
counter("retries").increment(3) # increment by N
|
|
110
|
+
counter("queue_size").decrement() # decrement
|
|
111
|
+
|
|
112
|
+
# Read the value anywhere
|
|
113
|
+
print(counter("cache_hits").value)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Print the report
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from pytrackio import report
|
|
120
|
+
|
|
121
|
+
report() # print to stdout
|
|
122
|
+
report(show_counters=False) # hide counters section
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Access raw data programmatically
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from pytrackio import get_registry
|
|
129
|
+
|
|
130
|
+
registry = get_registry()
|
|
131
|
+
|
|
132
|
+
# Get summary for one metric
|
|
133
|
+
s = registry.summary("payment_gateway")
|
|
134
|
+
print(s.calls, s.avg_ms, s.error_rate)
|
|
135
|
+
|
|
136
|
+
# Get all summaries
|
|
137
|
+
for s in registry.all_summaries():
|
|
138
|
+
if s.error_rate > 5.0:
|
|
139
|
+
alert(f"{s.name} has {s.error_rate}% errors!")
|
|
140
|
+
|
|
141
|
+
# Reset between test runs
|
|
142
|
+
registry.reset()
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Real-world example
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from pytrackio import track, timer, counter, report
|
|
151
|
+
import requests as http
|
|
152
|
+
|
|
153
|
+
@track
|
|
154
|
+
def get_weather(city: str) -> dict:
|
|
155
|
+
r = http.get(f"https://api.example.com/weather?city={city}", timeout=5)
|
|
156
|
+
r.raise_for_status()
|
|
157
|
+
counter("api_calls").increment()
|
|
158
|
+
return r.json()
|
|
159
|
+
|
|
160
|
+
@track
|
|
161
|
+
def process_weather(data: dict) -> str:
|
|
162
|
+
with timer("format_output"):
|
|
163
|
+
return f"{data['city']}: {data['temp']}°C"
|
|
164
|
+
|
|
165
|
+
# Run your app...
|
|
166
|
+
for city in ["Delhi", "London", "Tokyo"]:
|
|
167
|
+
data = get_weather(city)
|
|
168
|
+
print(process_weather(data))
|
|
169
|
+
|
|
170
|
+
# See how everything performed
|
|
171
|
+
report()
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## API Reference
|
|
177
|
+
|
|
178
|
+
### `@track`
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
@track
|
|
182
|
+
def my_func(): ...
|
|
183
|
+
|
|
184
|
+
@track(name="custom_name")
|
|
185
|
+
def my_func(): ...
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Tracks: call count, duration (min/max/avg), errors and error rate.
|
|
189
|
+
Never swallows exceptions — your original errors always propagate.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
### `timer(name)`
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
with timer("block_name"):
|
|
197
|
+
...
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Context manager. Records duration and any exceptions raised inside the block.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
### `counter(name)`
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
counter("name").increment() # +1
|
|
208
|
+
counter("name").increment(n) # +n
|
|
209
|
+
counter("name").decrement() # -1
|
|
210
|
+
counter("name").reset() # → 0
|
|
211
|
+
counter("name").value # read current value
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
### `report(stream, colour, show_counters)`
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
report()
|
|
220
|
+
report(show_counters=False)
|
|
221
|
+
report(colour=False) # force plain text
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Prints to stdout and returns the output as a string.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
### `get_registry()`
|
|
229
|
+
|
|
230
|
+
Returns the global `MetricsRegistry` instance for programmatic access.
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
registry = get_registry()
|
|
234
|
+
registry.all_summaries() # List[MetricSummary]
|
|
235
|
+
registry.all_counters() # List[CounterState]
|
|
236
|
+
registry.summary("name") # Optional[MetricSummary]
|
|
237
|
+
registry.reset() # clear everything
|
|
238
|
+
registry.uptime_seconds() # float
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Contributing
|
|
244
|
+
|
|
245
|
+
Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
246
|
+
|
|
247
|
+
**Ideas for contributions:**
|
|
248
|
+
- Async support (`@track` for `async def` functions)
|
|
249
|
+
- Export to JSON / CSV
|
|
250
|
+
- Histogram bucketing
|
|
251
|
+
- `@track` for class methods
|
|
252
|
+
- Minimum call threshold filter in report
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Built by
|
|
257
|
+
|
|
258
|
+
**Deepanshu** — BCA Student at Chandigarh University, aspiring Data Analyst.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## License
|
|
263
|
+
|
|
264
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pytrackio"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Zero-dependency Python performance tracker. Decorate, time, count — then report."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
dependencies = []
|
|
13
|
+
|
|
14
|
+
keywords = [
|
|
15
|
+
"metrics", "performance", "tracking", "profiling",
|
|
16
|
+
"monitoring", "decorator", "timer", "benchmarking"
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 4 - Beta",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"License :: OSI Approved :: MIT License",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
28
|
+
"Topic :: System :: Monitoring",
|
|
29
|
+
"Typing :: Typed",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Homepage = "https://github.com/danshu3007-lang/pytrackio"
|
|
34
|
+
Repository = "https://github.com/danshu3007-lang/pytrackio"
|
|
35
|
+
"Bug Tracker" = "https://github.com/danshu3007-lang/pytrackio/issues"
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.packages.find]
|
|
38
|
+
where = ["."]
|
|
39
|
+
include = ["pytrackio*"]
|
|
40
|
+
|
|
41
|
+
[tool.pytest.ini_options]
|
|
42
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pytrackio
|
|
3
|
+
=========
|
|
4
|
+
Zero-dependency Python performance tracker.
|
|
5
|
+
|
|
6
|
+
from pytrackio import track, timer, counter, report
|
|
7
|
+
|
|
8
|
+
@track
|
|
9
|
+
def my_function(): ...
|
|
10
|
+
|
|
11
|
+
with timer("block"): do_something()
|
|
12
|
+
|
|
13
|
+
counter("events").increment()
|
|
14
|
+
report()
|
|
15
|
+
"""
|
|
16
|
+
from ._instruments import counter, timer, track
|
|
17
|
+
from ._report import report
|
|
18
|
+
from ._tracker import MetricSummary, get_registry
|
|
19
|
+
|
|
20
|
+
__version__ = "0.1.0"
|
|
21
|
+
__author__ = "Deepanshu"
|
|
22
|
+
__license__ = "MIT"
|
|
23
|
+
|
|
24
|
+
__all__ = ["track","timer","counter","report","get_registry","MetricSummary","__version__"]
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import functools, time
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
from typing import Any, Callable, Generator, Optional, TypeVar
|
|
5
|
+
from ._tracker import CounterState, get_registry
|
|
6
|
+
|
|
7
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
|
8
|
+
|
|
9
|
+
def track(func=None, *, name=None):
|
|
10
|
+
"""Decorator: track calls, duration and errors for any function.
|
|
11
|
+
|
|
12
|
+
Usage::
|
|
13
|
+
|
|
14
|
+
@track
|
|
15
|
+
def my_func(): ...
|
|
16
|
+
|
|
17
|
+
@track(name="custom")
|
|
18
|
+
def my_func(): ...
|
|
19
|
+
"""
|
|
20
|
+
if func is None:
|
|
21
|
+
def decorator(f): return _wrap(f, name)
|
|
22
|
+
return decorator
|
|
23
|
+
return _wrap(func, name)
|
|
24
|
+
|
|
25
|
+
def _wrap(func, custom_name):
|
|
26
|
+
metric_name = custom_name or f"{func.__module__}.{func.__qualname__}"
|
|
27
|
+
registry = get_registry()
|
|
28
|
+
@functools.wraps(func)
|
|
29
|
+
def wrapper(*args, **kwargs):
|
|
30
|
+
start = time.perf_counter()
|
|
31
|
+
try:
|
|
32
|
+
result = func(*args, **kwargs)
|
|
33
|
+
registry.record(metric_name, (time.perf_counter()-start)*1000, True)
|
|
34
|
+
return result
|
|
35
|
+
except Exception as exc:
|
|
36
|
+
registry.record(metric_name, (time.perf_counter()-start)*1000, False, type(exc).__name__)
|
|
37
|
+
raise
|
|
38
|
+
return wrapper
|
|
39
|
+
|
|
40
|
+
@contextmanager
|
|
41
|
+
def timer(name: str) -> Generator[None, None, None]:
|
|
42
|
+
"""Context manager: track duration of any code block.
|
|
43
|
+
|
|
44
|
+
Usage::
|
|
45
|
+
|
|
46
|
+
with timer("db_query"):
|
|
47
|
+
results = db.execute(query)
|
|
48
|
+
"""
|
|
49
|
+
registry = get_registry()
|
|
50
|
+
start = time.perf_counter()
|
|
51
|
+
try:
|
|
52
|
+
yield
|
|
53
|
+
registry.record(name, (time.perf_counter()-start)*1000, True)
|
|
54
|
+
except Exception as exc:
|
|
55
|
+
registry.record(name, (time.perf_counter()-start)*1000, False, type(exc).__name__)
|
|
56
|
+
raise
|
|
57
|
+
|
|
58
|
+
def counter(name: str) -> CounterState:
|
|
59
|
+
"""Get a named counter (created on first access).
|
|
60
|
+
|
|
61
|
+
Usage::
|
|
62
|
+
|
|
63
|
+
counter("hits").increment()
|
|
64
|
+
counter("hits").value
|
|
65
|
+
"""
|
|
66
|
+
return get_registry().get_counter(name)
|