timerun 0.6.2__tar.gz → 0.7.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.
- timerun-0.7.0/PKG-INFO +163 -0
- timerun-0.7.0/README.md +132 -0
- timerun-0.7.0/pyproject.toml +258 -0
- timerun-0.7.0/timerun.egg-info/PKG-INFO +163 -0
- {timerun-0.6.2 → timerun-0.7.0}/timerun.egg-info/SOURCES.txt +1 -2
- timerun-0.7.0/timerun.py +505 -0
- timerun-0.6.2/PKG-INFO +0 -138
- timerun-0.6.2/README.md +0 -109
- timerun-0.6.2/pyproject.toml +0 -110
- timerun-0.6.2/timerun.egg-info/PKG-INFO +0 -138
- timerun-0.6.2/timerun.egg-info/zip-safe +0 -1
- timerun-0.6.2/timerun.py +0 -469
- {timerun-0.6.2 → timerun-0.7.0}/LICENSE +0 -0
- {timerun-0.6.2 → timerun-0.7.0}/setup.cfg +0 -0
- {timerun-0.6.2 → timerun-0.7.0}/timerun.egg-info/dependency_links.txt +0 -0
- {timerun-0.6.2 → timerun-0.7.0}/timerun.egg-info/top_level.txt +0 -0
timerun-0.7.0/PKG-INFO
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: timerun
|
|
3
|
+
Version: 0.7.0
|
|
4
|
+
Summary: Structured wall-clock & CPU timing for Python (stdlib only). Zero dependencies, nanosecond precision, and ready for your logs, metrics, and OpenTelemetry.
|
|
5
|
+
Author-email: HH-MWB <h.hong@mail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://hh-mwb.github.io/timerun/
|
|
8
|
+
Project-URL: Documentation, https://hh-mwb.github.io/timerun/
|
|
9
|
+
Project-URL: Repository, https://github.com/HH-MWB/timerun
|
|
10
|
+
Project-URL: Issues, https://github.com/HH-MWB/timerun/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/HH-MWB/timerun/releases
|
|
12
|
+
Project-URL: PyPI, https://pypi.org/project/timerun
|
|
13
|
+
Keywords: time,timing,measurement,measurements,elapsed,stopwatch,timer,performance,latency,profiler,benchmarking,telemetry,opentelemetry,wall-clock,cpu-time,asyncio,fastapi
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Classifier: Topic :: System :: Monitoring
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
<p align="center">
|
|
33
|
+
<a href="https://github.com/HH-MWB/timerun">
|
|
34
|
+
<img src="https://github.com/user-attachments/assets/00f08873-3ee5-4ee4-a41f-f9eda37fc30d" alt="TimeRun">
|
|
35
|
+
</a>
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
<p align="center">
|
|
39
|
+
<strong>Structured, zero-dependency Python timing for humans and telemetry.</strong><br>
|
|
40
|
+
Instantly isolate wall-clock latency from CPU burn across sync, async, and generators.
|
|
41
|
+
</p>
|
|
42
|
+
|
|
43
|
+
<p align="center">
|
|
44
|
+
<a href="https://pypi.org/project/timerun/"><img alt="PyPI Version" src="https://img.shields.io/pypi/v/timerun.svg"></a>
|
|
45
|
+
<a href="https://codecov.io/gh/HH-MWB/timerun"><img alt="Coverage" src="https://codecov.io/gh/HH-MWB/timerun/branch/main/graph/badge.svg"></a>
|
|
46
|
+
<a href="https://github.com/HH-MWB/timerun/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/pypi/l/timerun.svg"></a>
|
|
47
|
+
<a href="https://pepy.tech/project/timerun"><img alt="Total Downloads" src="https://static.pepy.tech/badge/timerun"></a>
|
|
48
|
+
</p>
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Quickstart
|
|
53
|
+
|
|
54
|
+
### 1. Measure Code Blocks (Wall vs. CPU)
|
|
55
|
+
|
|
56
|
+
Wrap any block in `with Timer() as m:` (or `async with Timer() as m:`). On exit, `m.wall_time` and `m.cpu_time` are immediately available:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
import time
|
|
60
|
+
from timerun import Timer
|
|
61
|
+
|
|
62
|
+
with Timer() as m:
|
|
63
|
+
time.sleep(0.05) # Simulates network I/O or database wait
|
|
64
|
+
total = sum(i * i for i in range(100_000)) # Simulates CPU computation
|
|
65
|
+
|
|
66
|
+
print("Elapsed (Wall):", m.wall_time.timedelta) # e.g., 0:00:00.055120
|
|
67
|
+
print("Compute (CPU): ", m.cpu_time.timedelta) # e.g., 0:00:00.005110
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
> **Why both?** If wall time is 2 seconds but CPU time is 5 milliseconds, your code spent 99.7% of its time waiting on I/O. If both are 2 seconds, you have a CPU bottleneck.
|
|
71
|
+
|
|
72
|
+
### 2. Time Functions and Generators
|
|
73
|
+
|
|
74
|
+
Use `@Timer()` on sync or async functions. The wrapped function retains a bounded deque of measurements:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from timerun import Timer
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@Timer(maxlen=100)
|
|
81
|
+
def process_batch(items):
|
|
82
|
+
return [x * 2 for x in items]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
process_batch([1, 2, 3])
|
|
86
|
+
|
|
87
|
+
last = process_batch.measurements[-1]
|
|
88
|
+
print("Wall time:", last.wall_time.timedelta)
|
|
89
|
+
print("CPU time: ", last.cpu_time.timedelta)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 3. Stream to Logs & Observability
|
|
93
|
+
|
|
94
|
+
Use `on_end` to ship timings directly to your logs, OpenTelemetry, Prometheus, or Datadog without adding vendor dependencies to your core logic:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
import logging
|
|
98
|
+
from timerun import Timer
|
|
99
|
+
|
|
100
|
+
logger = logging.getLogger("app")
|
|
101
|
+
|
|
102
|
+
with Timer(
|
|
103
|
+
metadata={"service": "checkout", "user_id": "u-123"},
|
|
104
|
+
on_end=lambda m: logger.info(
|
|
105
|
+
"checkout_completed",
|
|
106
|
+
extra={
|
|
107
|
+
"wall_ms": m.wall_time.duration / 1e6,
|
|
108
|
+
"cpu_ms": m.cpu_time.duration / 1e6,
|
|
109
|
+
**m.metadata,
|
|
110
|
+
},
|
|
111
|
+
),
|
|
112
|
+
):
|
|
113
|
+
pass # your operation
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Installation
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
pip install timerun
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
*Requires Python 3.10+.*
|
|
123
|
+
|
|
124
|
+
## Why TimeRun?
|
|
125
|
+
|
|
126
|
+
When code runs slowly, **is it waiting on network/disk I/O, or burning CPU?**
|
|
127
|
+
|
|
128
|
+
- `time.perf_counter()` only gives a raw float of elapsed wall time. Measuring CPU time requires manual `time.thread_time()` calls and boilerplate.
|
|
129
|
+
- Profilers (`cProfile`, `py-spy`) and APM agents (`Datadog`, `New Relic`) introduce binary dependencies, sampling overhead, or vendor lock-in.
|
|
130
|
+
- Naive timing patterns risk race conditions and crosstalk in concurrent `asyncio` tasks or multi-threaded workers.
|
|
131
|
+
|
|
132
|
+
TimeRun provides clean, structured timing with sub-microsecond overhead:
|
|
133
|
+
|
|
134
|
+
| Feature | Manual `time.perf_counter()` | Profilers (`cProfile`, `py-spy`) | APM Agents (`Datadog`, `New Relic`) | **TimeRun** |
|
|
135
|
+
| :--- | :---: | :---: | :---: | :---: |
|
|
136
|
+
| **Dependencies** | None | Extra tooling | Large binary agents | **Zero (stdlib only)** |
|
|
137
|
+
| **Wall vs. CPU Breakdown** | ❌ Manual boilerplate | ⚠️ Profiling overhead | ❌ Usually wall only | **✅ Built-in & precise** |
|
|
138
|
+
| **Structured Output** | ❌ Raw floats | ❌ Binary trace files | ⚠️ Proprietary format | **✅ Structured `Measurement`** |
|
|
139
|
+
| **Asyncio & Thread Safe** | ❌ Race condition risk | ⚠️ Global runtime cost | ✅ Handled by agent | **✅ Task & Thread isolated** |
|
|
140
|
+
| **Telemetry Hooks** | ❌ Build it yourself | ❌ Offline analysis only | ⚠️ Vendor locked | **✅ Agnostic (`on_end`)** |
|
|
141
|
+
| **Footprint** | 0 LOC | Heavy | Large background daemon | **Single file (~500 LOC)** |
|
|
142
|
+
|
|
143
|
+
## Key Capabilities
|
|
144
|
+
|
|
145
|
+
- **Zero Supply-Chain Risk:** Pure Python standard library only (`time`, `dataclasses`, `contextvars`); vendorable as a single file (`timerun.py`).
|
|
146
|
+
- **Context Isolation:** Native `ContextVar` stacks ensure concurrent `asyncio` tasks and threads never mix up measurements.
|
|
147
|
+
- **Sub-Microsecond Overhead:** Slotted dataclasses and integer nanoseconds keep internal overhead under ~0.4 µs.
|
|
148
|
+
- **Full Async & Generator Support:** Accurately times coroutines, synchronous generators, and asynchronous streaming generators across full iteration.
|
|
149
|
+
- **100% Test Coverage:** Verified with comprehensive Behavior-Driven Development (BDD) test suites.
|
|
150
|
+
|
|
151
|
+
## Documentation
|
|
152
|
+
|
|
153
|
+
- **[Documentation Site](https://hh-mwb.github.io/timerun/)** — Tutorials, concepts, and guides.
|
|
154
|
+
- **[API Guide](https://hh-mwb.github.io/timerun/guide/)** — In-depth reference for `Timer`, `Measurement`, and `TimeSpan`.
|
|
155
|
+
- **[Cookbooks](https://hh-mwb.github.io/timerun/cookbook/)** — Real-world recipes for FastAPI, OpenTelemetry, Structlog, and LLM streaming.
|
|
156
|
+
|
|
157
|
+
## Contributing
|
|
158
|
+
|
|
159
|
+
Contributions are welcome! See [CONTRIBUTING.md](https://github.com/HH-MWB/timerun/blob/main/CONTRIBUTING.md) for setup, testing, and pull request guidelines.
|
|
160
|
+
|
|
161
|
+
## License
|
|
162
|
+
|
|
163
|
+
This project is licensed under the MIT License — see the [LICENSE](https://github.com/HH-MWB/timerun/blob/main/LICENSE) file for details.
|
timerun-0.7.0/README.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://github.com/HH-MWB/timerun">
|
|
3
|
+
<img src="https://github.com/user-attachments/assets/00f08873-3ee5-4ee4-a41f-f9eda37fc30d" alt="TimeRun">
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>Structured, zero-dependency Python timing for humans and telemetry.</strong><br>
|
|
9
|
+
Instantly isolate wall-clock latency from CPU burn across sync, async, and generators.
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<a href="https://pypi.org/project/timerun/"><img alt="PyPI Version" src="https://img.shields.io/pypi/v/timerun.svg"></a>
|
|
14
|
+
<a href="https://codecov.io/gh/HH-MWB/timerun"><img alt="Coverage" src="https://codecov.io/gh/HH-MWB/timerun/branch/main/graph/badge.svg"></a>
|
|
15
|
+
<a href="https://github.com/HH-MWB/timerun/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/pypi/l/timerun.svg"></a>
|
|
16
|
+
<a href="https://pepy.tech/project/timerun"><img alt="Total Downloads" src="https://static.pepy.tech/badge/timerun"></a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Quickstart
|
|
22
|
+
|
|
23
|
+
### 1. Measure Code Blocks (Wall vs. CPU)
|
|
24
|
+
|
|
25
|
+
Wrap any block in `with Timer() as m:` (or `async with Timer() as m:`). On exit, `m.wall_time` and `m.cpu_time` are immediately available:
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
import time
|
|
29
|
+
from timerun import Timer
|
|
30
|
+
|
|
31
|
+
with Timer() as m:
|
|
32
|
+
time.sleep(0.05) # Simulates network I/O or database wait
|
|
33
|
+
total = sum(i * i for i in range(100_000)) # Simulates CPU computation
|
|
34
|
+
|
|
35
|
+
print("Elapsed (Wall):", m.wall_time.timedelta) # e.g., 0:00:00.055120
|
|
36
|
+
print("Compute (CPU): ", m.cpu_time.timedelta) # e.g., 0:00:00.005110
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
> **Why both?** If wall time is 2 seconds but CPU time is 5 milliseconds, your code spent 99.7% of its time waiting on I/O. If both are 2 seconds, you have a CPU bottleneck.
|
|
40
|
+
|
|
41
|
+
### 2. Time Functions and Generators
|
|
42
|
+
|
|
43
|
+
Use `@Timer()` on sync or async functions. The wrapped function retains a bounded deque of measurements:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from timerun import Timer
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@Timer(maxlen=100)
|
|
50
|
+
def process_batch(items):
|
|
51
|
+
return [x * 2 for x in items]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
process_batch([1, 2, 3])
|
|
55
|
+
|
|
56
|
+
last = process_batch.measurements[-1]
|
|
57
|
+
print("Wall time:", last.wall_time.timedelta)
|
|
58
|
+
print("CPU time: ", last.cpu_time.timedelta)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 3. Stream to Logs & Observability
|
|
62
|
+
|
|
63
|
+
Use `on_end` to ship timings directly to your logs, OpenTelemetry, Prometheus, or Datadog without adding vendor dependencies to your core logic:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
import logging
|
|
67
|
+
from timerun import Timer
|
|
68
|
+
|
|
69
|
+
logger = logging.getLogger("app")
|
|
70
|
+
|
|
71
|
+
with Timer(
|
|
72
|
+
metadata={"service": "checkout", "user_id": "u-123"},
|
|
73
|
+
on_end=lambda m: logger.info(
|
|
74
|
+
"checkout_completed",
|
|
75
|
+
extra={
|
|
76
|
+
"wall_ms": m.wall_time.duration / 1e6,
|
|
77
|
+
"cpu_ms": m.cpu_time.duration / 1e6,
|
|
78
|
+
**m.metadata,
|
|
79
|
+
},
|
|
80
|
+
),
|
|
81
|
+
):
|
|
82
|
+
pass # your operation
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Installation
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pip install timerun
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
*Requires Python 3.10+.*
|
|
92
|
+
|
|
93
|
+
## Why TimeRun?
|
|
94
|
+
|
|
95
|
+
When code runs slowly, **is it waiting on network/disk I/O, or burning CPU?**
|
|
96
|
+
|
|
97
|
+
- `time.perf_counter()` only gives a raw float of elapsed wall time. Measuring CPU time requires manual `time.thread_time()` calls and boilerplate.
|
|
98
|
+
- Profilers (`cProfile`, `py-spy`) and APM agents (`Datadog`, `New Relic`) introduce binary dependencies, sampling overhead, or vendor lock-in.
|
|
99
|
+
- Naive timing patterns risk race conditions and crosstalk in concurrent `asyncio` tasks or multi-threaded workers.
|
|
100
|
+
|
|
101
|
+
TimeRun provides clean, structured timing with sub-microsecond overhead:
|
|
102
|
+
|
|
103
|
+
| Feature | Manual `time.perf_counter()` | Profilers (`cProfile`, `py-spy`) | APM Agents (`Datadog`, `New Relic`) | **TimeRun** |
|
|
104
|
+
| :--- | :---: | :---: | :---: | :---: |
|
|
105
|
+
| **Dependencies** | None | Extra tooling | Large binary agents | **Zero (stdlib only)** |
|
|
106
|
+
| **Wall vs. CPU Breakdown** | ❌ Manual boilerplate | ⚠️ Profiling overhead | ❌ Usually wall only | **✅ Built-in & precise** |
|
|
107
|
+
| **Structured Output** | ❌ Raw floats | ❌ Binary trace files | ⚠️ Proprietary format | **✅ Structured `Measurement`** |
|
|
108
|
+
| **Asyncio & Thread Safe** | ❌ Race condition risk | ⚠️ Global runtime cost | ✅ Handled by agent | **✅ Task & Thread isolated** |
|
|
109
|
+
| **Telemetry Hooks** | ❌ Build it yourself | ❌ Offline analysis only | ⚠️ Vendor locked | **✅ Agnostic (`on_end`)** |
|
|
110
|
+
| **Footprint** | 0 LOC | Heavy | Large background daemon | **Single file (~500 LOC)** |
|
|
111
|
+
|
|
112
|
+
## Key Capabilities
|
|
113
|
+
|
|
114
|
+
- **Zero Supply-Chain Risk:** Pure Python standard library only (`time`, `dataclasses`, `contextvars`); vendorable as a single file (`timerun.py`).
|
|
115
|
+
- **Context Isolation:** Native `ContextVar` stacks ensure concurrent `asyncio` tasks and threads never mix up measurements.
|
|
116
|
+
- **Sub-Microsecond Overhead:** Slotted dataclasses and integer nanoseconds keep internal overhead under ~0.4 µs.
|
|
117
|
+
- **Full Async & Generator Support:** Accurately times coroutines, synchronous generators, and asynchronous streaming generators across full iteration.
|
|
118
|
+
- **100% Test Coverage:** Verified with comprehensive Behavior-Driven Development (BDD) test suites.
|
|
119
|
+
|
|
120
|
+
## Documentation
|
|
121
|
+
|
|
122
|
+
- **[Documentation Site](https://hh-mwb.github.io/timerun/)** — Tutorials, concepts, and guides.
|
|
123
|
+
- **[API Guide](https://hh-mwb.github.io/timerun/guide/)** — In-depth reference for `Timer`, `Measurement`, and `TimeSpan`.
|
|
124
|
+
- **[Cookbooks](https://hh-mwb.github.io/timerun/cookbook/)** — Real-world recipes for FastAPI, OpenTelemetry, Structlog, and LLM streaming.
|
|
125
|
+
|
|
126
|
+
## Contributing
|
|
127
|
+
|
|
128
|
+
Contributions are welcome! See [CONTRIBUTING.md](https://github.com/HH-MWB/timerun/blob/main/CONTRIBUTING.md) for setup, testing, and pull request guidelines.
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
This project is licensed under the MIT License — see the [LICENSE](https://github.com/HH-MWB/timerun/blob/main/LICENSE) file for details.
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# ============================================================================
|
|
2
|
+
# Build System & Package Metadata (PEP 517 / PEP 621 / PEP 735)
|
|
3
|
+
# ============================================================================
|
|
4
|
+
|
|
5
|
+
[build-system]
|
|
6
|
+
requires = ["setuptools"]
|
|
7
|
+
build-backend = "setuptools.build_meta"
|
|
8
|
+
|
|
9
|
+
[project]
|
|
10
|
+
name = "timerun"
|
|
11
|
+
description = "Structured wall-clock & CPU timing for Python (stdlib only). Zero dependencies, nanosecond precision, and ready for your logs, metrics, and OpenTelemetry."
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.10"
|
|
14
|
+
license = { text = "MIT" }
|
|
15
|
+
authors = [{ name = "HH-MWB", email = "h.hong@mail.com" }]
|
|
16
|
+
keywords = [
|
|
17
|
+
"time",
|
|
18
|
+
"timing",
|
|
19
|
+
"measurement",
|
|
20
|
+
"measurements",
|
|
21
|
+
"elapsed",
|
|
22
|
+
"stopwatch",
|
|
23
|
+
"timer",
|
|
24
|
+
"performance",
|
|
25
|
+
"latency",
|
|
26
|
+
"profiler",
|
|
27
|
+
"benchmarking",
|
|
28
|
+
"telemetry",
|
|
29
|
+
"opentelemetry",
|
|
30
|
+
"wall-clock",
|
|
31
|
+
"cpu-time",
|
|
32
|
+
"asyncio",
|
|
33
|
+
"fastapi",
|
|
34
|
+
]
|
|
35
|
+
classifiers = [
|
|
36
|
+
"Development Status :: 4 - Beta",
|
|
37
|
+
"Intended Audience :: Developers",
|
|
38
|
+
"License :: OSI Approved :: MIT License",
|
|
39
|
+
"Operating System :: OS Independent",
|
|
40
|
+
"Programming Language :: Python :: 3",
|
|
41
|
+
"Programming Language :: Python :: 3.10",
|
|
42
|
+
"Programming Language :: Python :: 3.11",
|
|
43
|
+
"Programming Language :: Python :: 3.12",
|
|
44
|
+
"Programming Language :: Python :: 3.13",
|
|
45
|
+
"Programming Language :: Python :: 3.14",
|
|
46
|
+
"Programming Language :: Python :: 3.15",
|
|
47
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
48
|
+
"Topic :: System :: Monitoring",
|
|
49
|
+
]
|
|
50
|
+
dynamic = ["version"]
|
|
51
|
+
|
|
52
|
+
[project.urls]
|
|
53
|
+
Homepage = "https://hh-mwb.github.io/timerun/"
|
|
54
|
+
Documentation = "https://hh-mwb.github.io/timerun/"
|
|
55
|
+
Repository = "https://github.com/HH-MWB/timerun"
|
|
56
|
+
Issues = "https://github.com/HH-MWB/timerun/issues"
|
|
57
|
+
Changelog = "https://github.com/HH-MWB/timerun/releases"
|
|
58
|
+
PyPI = "https://pypi.org/project/timerun"
|
|
59
|
+
|
|
60
|
+
[dependency-groups]
|
|
61
|
+
dev = [
|
|
62
|
+
{ include-group = "test" },
|
|
63
|
+
{ include-group = "audit" },
|
|
64
|
+
"pre-commit",
|
|
65
|
+
]
|
|
66
|
+
test = [
|
|
67
|
+
"behave",
|
|
68
|
+
"coverage",
|
|
69
|
+
]
|
|
70
|
+
audit = [
|
|
71
|
+
"pip-audit",
|
|
72
|
+
]
|
|
73
|
+
docs = [
|
|
74
|
+
"zensical",
|
|
75
|
+
]
|
|
76
|
+
build = [
|
|
77
|
+
"build",
|
|
78
|
+
"twine",
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
# ============================================================================
|
|
82
|
+
# Build Backend
|
|
83
|
+
# ============================================================================
|
|
84
|
+
|
|
85
|
+
[tool.setuptools]
|
|
86
|
+
py-modules = ["timerun"]
|
|
87
|
+
license-files = ["LICENSE"]
|
|
88
|
+
|
|
89
|
+
[tool.setuptools.dynamic]
|
|
90
|
+
version = { attr = "timerun.__version__" }
|
|
91
|
+
|
|
92
|
+
# ============================================================================
|
|
93
|
+
# Linting & Formatting
|
|
94
|
+
# ============================================================================
|
|
95
|
+
|
|
96
|
+
[tool.ruff]
|
|
97
|
+
target-version = "py310"
|
|
98
|
+
line-length = 79
|
|
99
|
+
fix = true
|
|
100
|
+
force-exclude = true
|
|
101
|
+
|
|
102
|
+
[tool.ruff.lint]
|
|
103
|
+
select = ["ALL"]
|
|
104
|
+
ignore = [
|
|
105
|
+
"CPY001", # Missing copyright notice at top of file
|
|
106
|
+
"D203", # Incompatible with D211 (no-blank-line-before-class)
|
|
107
|
+
"D213", # Incompatible with D212 (multi-line-summary-first-line)
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
[tool.ruff.lint.per-file-ignores]
|
|
111
|
+
"features/steps/*.py" = ["S101"]
|
|
112
|
+
|
|
113
|
+
[tool.ruff.lint.isort]
|
|
114
|
+
combine-as-imports = true
|
|
115
|
+
known-first-party = ["timerun"]
|
|
116
|
+
|
|
117
|
+
[tool.ruff.lint.mccabe]
|
|
118
|
+
max-complexity = 5
|
|
119
|
+
|
|
120
|
+
[tool.ruff.lint.pydocstyle]
|
|
121
|
+
convention = "numpy"
|
|
122
|
+
|
|
123
|
+
[tool.ruff.lint.flake8-type-checking]
|
|
124
|
+
strict = true
|
|
125
|
+
|
|
126
|
+
[tool.pylint.main]
|
|
127
|
+
py-version = "3.10"
|
|
128
|
+
fail-under = 10.0
|
|
129
|
+
load-plugins = ["pylint.extensions.mccabe"]
|
|
130
|
+
|
|
131
|
+
[tool.pylint.messages_control]
|
|
132
|
+
disable = [
|
|
133
|
+
"not-callable", # false positive for behave's when/then decorators
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
[tool.pylint.format]
|
|
137
|
+
max-line-length = 79
|
|
138
|
+
|
|
139
|
+
[tool.pylint.imports]
|
|
140
|
+
known-first-party = ["timerun"]
|
|
141
|
+
|
|
142
|
+
[tool.pylint.design]
|
|
143
|
+
max-args = 4
|
|
144
|
+
max-attributes = 5
|
|
145
|
+
max-complexity = 5
|
|
146
|
+
max-returns = 3
|
|
147
|
+
max-branches = 4
|
|
148
|
+
max-statements = 20
|
|
149
|
+
|
|
150
|
+
# ============================================================================
|
|
151
|
+
# Type Checking
|
|
152
|
+
# ============================================================================
|
|
153
|
+
|
|
154
|
+
[tool.mypy]
|
|
155
|
+
python_version = "3.10"
|
|
156
|
+
strict = true
|
|
157
|
+
extra_checks = true
|
|
158
|
+
local_partial_types = true
|
|
159
|
+
|
|
160
|
+
# Stricter checks beyond default strict mode
|
|
161
|
+
disallow_any_decorated = true
|
|
162
|
+
disallow_any_explicit = true
|
|
163
|
+
disallow_any_unimported = true
|
|
164
|
+
strict_equality_for_none = true
|
|
165
|
+
warn_unreachable = true
|
|
166
|
+
|
|
167
|
+
# Extended error codes
|
|
168
|
+
enable_error_code = [
|
|
169
|
+
"deprecated",
|
|
170
|
+
"exhaustive-match",
|
|
171
|
+
"explicit-override",
|
|
172
|
+
"ignore-without-code",
|
|
173
|
+
"possibly-undefined",
|
|
174
|
+
"redundant-expr",
|
|
175
|
+
"redundant-self",
|
|
176
|
+
"truthy-bool",
|
|
177
|
+
"truthy-function",
|
|
178
|
+
"truthy-iterable",
|
|
179
|
+
"unimported-reveal",
|
|
180
|
+
"unused-awaitable",
|
|
181
|
+
]
|
|
182
|
+
|
|
183
|
+
# Output formatting
|
|
184
|
+
show_column_numbers = true
|
|
185
|
+
show_error_codes = true
|
|
186
|
+
show_error_context = true
|
|
187
|
+
pretty = true
|
|
188
|
+
|
|
189
|
+
[[tool.mypy.overrides]]
|
|
190
|
+
module = [
|
|
191
|
+
"behave",
|
|
192
|
+
"behave.*",
|
|
193
|
+
]
|
|
194
|
+
ignore_missing_imports = true
|
|
195
|
+
|
|
196
|
+
[[tool.mypy.overrides]]
|
|
197
|
+
module = "features.*"
|
|
198
|
+
ignore_errors = true
|
|
199
|
+
|
|
200
|
+
[tool.pyright]
|
|
201
|
+
pythonVersion = "3.10"
|
|
202
|
+
venvPath = "."
|
|
203
|
+
venv = ".venv"
|
|
204
|
+
|
|
205
|
+
include = ["timerun.py"]
|
|
206
|
+
exclude = ["features"]
|
|
207
|
+
|
|
208
|
+
typeCheckingMode = "strict"
|
|
209
|
+
deprecateTypingAliases = true
|
|
210
|
+
|
|
211
|
+
# Diagnostic rules elevated to error beyond strict mode
|
|
212
|
+
reportCallInDefaultInitializer = "error"
|
|
213
|
+
reportImplicitOverride = "error"
|
|
214
|
+
reportImplicitStringConcatenation = "error"
|
|
215
|
+
reportImportCycles = "error"
|
|
216
|
+
reportMissingSuperCall = "error"
|
|
217
|
+
reportPropertyTypeMismatch = "error"
|
|
218
|
+
reportUninitializedInstanceVariable = "error"
|
|
219
|
+
reportUnnecessaryCast = "error"
|
|
220
|
+
reportUnnecessaryComparison = "error"
|
|
221
|
+
reportUnnecessaryContains = "error"
|
|
222
|
+
reportUnnecessaryIsInstance = "error"
|
|
223
|
+
reportUnnecessaryTypeIgnoreComment = "error"
|
|
224
|
+
reportUnusedCallResult = "error"
|
|
225
|
+
|
|
226
|
+
# ============================================================================
|
|
227
|
+
# Security Analysis
|
|
228
|
+
# ============================================================================
|
|
229
|
+
|
|
230
|
+
[tool.bandit]
|
|
231
|
+
exclude_dirs = ["features/steps"]
|
|
232
|
+
|
|
233
|
+
[tool.bandit.try_except_pass]
|
|
234
|
+
check_typed_exception = true
|
|
235
|
+
|
|
236
|
+
# ============================================================================
|
|
237
|
+
# Spelling & Quality
|
|
238
|
+
# ============================================================================
|
|
239
|
+
|
|
240
|
+
[tool.codespell]
|
|
241
|
+
check-filenames = true
|
|
242
|
+
check-hidden = true
|
|
243
|
+
|
|
244
|
+
# ============================================================================
|
|
245
|
+
# Testing & Coverage
|
|
246
|
+
# ============================================================================
|
|
247
|
+
|
|
248
|
+
[tool.behave]
|
|
249
|
+
show_snippets = false
|
|
250
|
+
show_skipped = false
|
|
251
|
+
|
|
252
|
+
[tool.coverage.run]
|
|
253
|
+
source = ["timerun"]
|
|
254
|
+
relative_files = true
|
|
255
|
+
|
|
256
|
+
[tool.coverage.report]
|
|
257
|
+
fail_under = 100
|
|
258
|
+
show_missing = true
|