timetracer 1.1.0__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.
- timetracer/__init__.py +29 -0
- timetracer/cassette/__init__.py +6 -0
- timetracer/cassette/io.py +421 -0
- timetracer/cassette/naming.py +69 -0
- timetracer/catalog/__init__.py +288 -0
- timetracer/cli/__init__.py +5 -0
- timetracer/cli/commands/__init__.py +1 -0
- timetracer/cli/main.py +692 -0
- timetracer/config.py +297 -0
- timetracer/constants.py +129 -0
- timetracer/context.py +93 -0
- timetracer/dashboard/__init__.py +14 -0
- timetracer/dashboard/generator.py +229 -0
- timetracer/dashboard/server.py +244 -0
- timetracer/dashboard/template.py +874 -0
- timetracer/diff/__init__.py +6 -0
- timetracer/diff/engine.py +311 -0
- timetracer/diff/report.py +113 -0
- timetracer/exceptions.py +113 -0
- timetracer/integrations/__init__.py +27 -0
- timetracer/integrations/fastapi.py +537 -0
- timetracer/integrations/flask.py +507 -0
- timetracer/plugins/__init__.py +42 -0
- timetracer/plugins/base.py +73 -0
- timetracer/plugins/httpx_plugin.py +413 -0
- timetracer/plugins/redis_plugin.py +297 -0
- timetracer/plugins/requests_plugin.py +333 -0
- timetracer/plugins/sqlalchemy_plugin.py +280 -0
- timetracer/policies/__init__.py +16 -0
- timetracer/policies/capture.py +64 -0
- timetracer/policies/redaction.py +165 -0
- timetracer/replay/__init__.py +6 -0
- timetracer/replay/engine.py +75 -0
- timetracer/replay/errors.py +9 -0
- timetracer/replay/matching.py +83 -0
- timetracer/session.py +390 -0
- timetracer/storage/__init__.py +18 -0
- timetracer/storage/s3.py +364 -0
- timetracer/timeline/__init__.py +6 -0
- timetracer/timeline/generator.py +150 -0
- timetracer/timeline/template.py +370 -0
- timetracer/types.py +197 -0
- timetracer/utils/__init__.py +6 -0
- timetracer/utils/hashing.py +68 -0
- timetracer/utils/time.py +106 -0
- timetracer-1.1.0.dist-info/METADATA +286 -0
- timetracer-1.1.0.dist-info/RECORD +51 -0
- timetracer-1.1.0.dist-info/WHEEL +5 -0
- timetracer-1.1.0.dist-info/entry_points.txt +2 -0
- timetracer-1.1.0.dist-info/licenses/LICENSE +21 -0
- timetracer-1.1.0.dist-info/top_level.txt +1 -0
timetracer/utils/time.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Time utilities for duration tracking.
|
|
3
|
+
|
|
4
|
+
Provides a Timer context manager for accurate timing.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import time
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class TimingResult:
|
|
13
|
+
"""Result of a timing operation."""
|
|
14
|
+
start_ns: int
|
|
15
|
+
end_ns: int
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def duration_ns(self) -> int:
|
|
19
|
+
"""Duration in nanoseconds."""
|
|
20
|
+
return self.end_ns - self.start_ns
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def duration_ms(self) -> float:
|
|
24
|
+
"""Duration in milliseconds."""
|
|
25
|
+
return self.duration_ns / 1_000_000
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def duration_s(self) -> float:
|
|
29
|
+
"""Duration in seconds."""
|
|
30
|
+
return self.duration_ns / 1_000_000_000
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Timer:
|
|
34
|
+
"""
|
|
35
|
+
A timer for measuring operation duration.
|
|
36
|
+
|
|
37
|
+
Can be used as a context manager or manually.
|
|
38
|
+
|
|
39
|
+
Usage:
|
|
40
|
+
# Context manager
|
|
41
|
+
with Timer() as t:
|
|
42
|
+
do_something()
|
|
43
|
+
print(f"Took {t.duration_ms}ms")
|
|
44
|
+
|
|
45
|
+
# Manual
|
|
46
|
+
timer = Timer()
|
|
47
|
+
timer.start()
|
|
48
|
+
do_something()
|
|
49
|
+
timer.stop()
|
|
50
|
+
print(f"Took {timer.duration_ms}ms")
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(self) -> None:
|
|
54
|
+
self._start_ns: int | None = None
|
|
55
|
+
self._end_ns: int | None = None
|
|
56
|
+
|
|
57
|
+
def start(self) -> None:
|
|
58
|
+
"""Start the timer."""
|
|
59
|
+
self._start_ns = time.perf_counter_ns()
|
|
60
|
+
self._end_ns = None
|
|
61
|
+
|
|
62
|
+
def stop(self) -> TimingResult:
|
|
63
|
+
"""Stop the timer and return result."""
|
|
64
|
+
if self._start_ns is None:
|
|
65
|
+
raise RuntimeError("Timer was not started")
|
|
66
|
+
|
|
67
|
+
self._end_ns = time.perf_counter_ns()
|
|
68
|
+
return TimingResult(self._start_ns, self._end_ns)
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def duration_ns(self) -> int:
|
|
72
|
+
"""Duration in nanoseconds (uses current time if not stopped)."""
|
|
73
|
+
if self._start_ns is None:
|
|
74
|
+
return 0
|
|
75
|
+
end = self._end_ns if self._end_ns is not None else time.perf_counter_ns()
|
|
76
|
+
return end - self._start_ns
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def duration_ms(self) -> float:
|
|
80
|
+
"""Duration in milliseconds."""
|
|
81
|
+
return self.duration_ns / 1_000_000
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def elapsed_ms(self) -> float:
|
|
85
|
+
"""Alias for duration_ms (more intuitive during ongoing timing)."""
|
|
86
|
+
return self.duration_ms
|
|
87
|
+
|
|
88
|
+
def __enter__(self) -> "Timer":
|
|
89
|
+
self.start()
|
|
90
|
+
return self
|
|
91
|
+
|
|
92
|
+
def __exit__(self, *args) -> None:
|
|
93
|
+
self.stop()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def get_offset_ms(start_time: float) -> float:
|
|
97
|
+
"""
|
|
98
|
+
Get milliseconds elapsed since a start time.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
start_time: Start time from time.perf_counter().
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Milliseconds elapsed.
|
|
105
|
+
"""
|
|
106
|
+
return (time.perf_counter() - start_time) * 1000
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: timetracer
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Time-travel debugging for FastAPI and Flask - record API cassettes, replay with mocked dependencies
|
|
5
|
+
Author: Timetracer Contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/usv240/timetracer
|
|
8
|
+
Project-URL: Documentation, https://github.com/usv240/timetracer#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/usv240/timetracer
|
|
10
|
+
Keywords: fastapi,flask,debugging,testing,api,replay,mocking,cassette
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Framework :: FastAPI
|
|
19
|
+
Classifier: Framework :: Flask
|
|
20
|
+
Classifier: Topic :: Software Development :: Testing
|
|
21
|
+
Classifier: Topic :: Software Development :: Debuggers
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Provides-Extra: fastapi
|
|
26
|
+
Requires-Dist: fastapi>=0.100.0; extra == "fastapi"
|
|
27
|
+
Requires-Dist: starlette>=0.27.0; extra == "fastapi"
|
|
28
|
+
Provides-Extra: flask
|
|
29
|
+
Requires-Dist: flask>=2.0.0; extra == "flask"
|
|
30
|
+
Provides-Extra: httpx
|
|
31
|
+
Requires-Dist: httpx>=0.24.0; extra == "httpx"
|
|
32
|
+
Provides-Extra: requests
|
|
33
|
+
Requires-Dist: requests>=2.28.0; extra == "requests"
|
|
34
|
+
Provides-Extra: sqlalchemy
|
|
35
|
+
Requires-Dist: sqlalchemy>=2.0.0; extra == "sqlalchemy"
|
|
36
|
+
Provides-Extra: redis
|
|
37
|
+
Requires-Dist: redis>=4.0.0; extra == "redis"
|
|
38
|
+
Provides-Extra: s3
|
|
39
|
+
Requires-Dist: boto3>=1.26.0; extra == "s3"
|
|
40
|
+
Provides-Extra: all
|
|
41
|
+
Requires-Dist: timetracer[fastapi,flask,httpx,redis,requests,s3,sqlalchemy]; extra == "all"
|
|
42
|
+
Provides-Extra: dev
|
|
43
|
+
Requires-Dist: timetracer[all]; extra == "dev"
|
|
44
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
45
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
46
|
+
Requires-Dist: uvicorn>=0.20.0; extra == "dev"
|
|
47
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
48
|
+
Requires-Dist: fakeredis>=2.0.0; extra == "dev"
|
|
49
|
+
Dynamic: license-file
|
|
50
|
+
|
|
51
|
+
# Timetracer
|
|
52
|
+
|
|
53
|
+
**Time-travel debugging for FastAPI and Flask** — Record API requests, replay with mocked dependencies.
|
|
54
|
+
|
|
55
|
+
[](https://github.com/usv240/timetracer/actions)
|
|
56
|
+
[](https://www.python.org/downloads/)
|
|
57
|
+
[](https://opensource.org/licenses/MIT)
|
|
58
|
+
[](https://pypi.org/project/timetracer/)
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## What is Timetracer?
|
|
63
|
+
|
|
64
|
+
Timetracer captures real API requests into portable **cassettes** and replays them with mocked dependencies (HTTP calls, database queries, Redis commands).
|
|
65
|
+
|
|
66
|
+
> Record once. Replay anywhere. Debug faster.
|
|
67
|
+
|
|
68
|
+
**Common use cases:**
|
|
69
|
+
|
|
70
|
+
- Reproduce production bugs locally without access to external services
|
|
71
|
+
- Build regression tests from real traffic patterns
|
|
72
|
+
- Run demos offline with pre-recorded data
|
|
73
|
+
- Detect performance regressions between releases
|
|
74
|
+
- Compare behavior between different code versions
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## How It Works
|
|
79
|
+
|
|
80
|
+
Timetracer acts as middleware that intercepts your app's external calls:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
RECORD MODE
|
|
84
|
+
┌──────────┐ ┌──────────────┐ ┌──────────────┐
|
|
85
|
+
│ Client │ ───► │ Your App │ ───► │ External │
|
|
86
|
+
│ (curl) │ │ + Timetracer │ │ APIs/DB │
|
|
87
|
+
└──────────┘ └──────────────┘ └──────────────┘
|
|
88
|
+
│
|
|
89
|
+
▼
|
|
90
|
+
┌───────────┐
|
|
91
|
+
│ Cassette │ (saves everything)
|
|
92
|
+
│ .json │
|
|
93
|
+
└───────────┘
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
REPLAY MODE
|
|
97
|
+
┌──────────┐ ┌──────────────┐ ╳ External APIs
|
|
98
|
+
│ Client │ ───► │ Your App │ ╳ (not called)
|
|
99
|
+
│ (curl) │ │ + Timetracer │
|
|
100
|
+
└──────────┘ └──────────────┘
|
|
101
|
+
▲
|
|
102
|
+
│
|
|
103
|
+
┌───────────┐
|
|
104
|
+
│ Cassette │ (replays from here)
|
|
105
|
+
│ .json │
|
|
106
|
+
└───────────┘
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Installation
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
pip install timetracer[all]
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Or install only what you need:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
pip install timetracer[fastapi,httpx] # FastAPI + HTTP
|
|
121
|
+
pip install timetracer[flask,requests] # Flask + HTTP
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Quick Start
|
|
127
|
+
|
|
128
|
+
### FastAPI
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from fastapi import FastAPI
|
|
132
|
+
from timetracer.integrations.fastapi import auto_setup
|
|
133
|
+
|
|
134
|
+
app = auto_setup(FastAPI())
|
|
135
|
+
|
|
136
|
+
@app.get("/users/{user_id}")
|
|
137
|
+
async def get_user(user_id: int):
|
|
138
|
+
async with httpx.AsyncClient() as client:
|
|
139
|
+
return (await client.get(f"https://api.example.com/users/{user_id}")).json()
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Flask
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from flask import Flask
|
|
146
|
+
from timetracer.integrations.flask import auto_setup
|
|
147
|
+
|
|
148
|
+
app = auto_setup(Flask(__name__))
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Record and Replay
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# Record mode - captures all external calls
|
|
155
|
+
TIMETRACER_MODE=record uvicorn app:app
|
|
156
|
+
curl http://localhost:8000/users/123
|
|
157
|
+
|
|
158
|
+
# Replay mode - mocks external calls from cassette
|
|
159
|
+
TIMETRACER_MODE=replay \
|
|
160
|
+
TIMETRACER_CASSETTE=./cassettes/2026-01-16/GET__users_{user_id}__abc.json \
|
|
161
|
+
uvicorn app:app
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Manual Setup
|
|
167
|
+
|
|
168
|
+
For more control over configuration:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
import httpx
|
|
172
|
+
from fastapi import FastAPI
|
|
173
|
+
from timetracer import TraceConfig
|
|
174
|
+
from timetracer.integrations.fastapi import TimeTraceMiddleware
|
|
175
|
+
from timetracer.plugins import enable_httpx
|
|
176
|
+
|
|
177
|
+
app = FastAPI()
|
|
178
|
+
|
|
179
|
+
config = TraceConfig(
|
|
180
|
+
mode="record",
|
|
181
|
+
cassette_dir="./my-cassettes",
|
|
182
|
+
errors_only=True,
|
|
183
|
+
)
|
|
184
|
+
app.add_middleware(TimeTraceMiddleware, config=config)
|
|
185
|
+
|
|
186
|
+
enable_httpx()
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Configuration
|
|
192
|
+
|
|
193
|
+
All settings are controlled via environment variables:
|
|
194
|
+
|
|
195
|
+
| Variable | Description | Default |
|
|
196
|
+
|----------|-------------|---------|
|
|
197
|
+
| `TIMETRACER_MODE` | `off`, `record`, `replay` | `off` |
|
|
198
|
+
| `TIMETRACER_DIR` | Cassette storage directory | `./cassettes` |
|
|
199
|
+
| `TIMETRACER_CASSETTE` | Path to cassette file (replay mode) | — |
|
|
200
|
+
| `TIMETRACER_SAMPLE_RATE` | Fraction of requests to record (0-1) | `1.0` |
|
|
201
|
+
| `TIMETRACER_ERRORS_ONLY` | Only record error responses | `false` |
|
|
202
|
+
| `TIMETRACER_MOCK_PLUGINS` | Plugins to mock during replay | all |
|
|
203
|
+
| `TIMETRACER_LIVE_PLUGINS` | Plugins to keep live during replay | none |
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Features
|
|
208
|
+
|
|
209
|
+
| Category | Supported |
|
|
210
|
+
|----------|-----------|
|
|
211
|
+
| **Frameworks** | FastAPI, Flask |
|
|
212
|
+
| **HTTP Clients** | httpx, requests |
|
|
213
|
+
| **Databases** | SQLAlchemy |
|
|
214
|
+
| **Cache** | Redis |
|
|
215
|
+
| **Storage** | Local filesystem, AWS S3 |
|
|
216
|
+
| **Tools** | CLI, diff engine, HTML timeline, **Dashboard** |
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## CLI
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
timetracer list --dir ./cassettes # List all cassettes
|
|
224
|
+
timetracer show ./cassettes/GET__users.json # Show cassette details
|
|
225
|
+
timetracer diff --a old.json --b new.json # Compare two cassettes
|
|
226
|
+
timetracer timeline ./cassettes/GET__users.json --open # Generate timeline
|
|
227
|
+
timetracer dashboard --dir ./cassettes --open # Generate interactive dashboard
|
|
228
|
+
timetracer serve --dir ./cassettes --open # Start live dashboard with replay
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Dashboard
|
|
234
|
+
|
|
235
|
+
Browse and debug all recorded cassettes with the interactive dashboard:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
# Static HTML dashboard (open in browser)
|
|
239
|
+
timetracer dashboard --dir ./cassettes --open
|
|
240
|
+
|
|
241
|
+
# Live dashboard with real-time replay
|
|
242
|
+
timetracer serve --dir ./cassettes --open
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Features:**
|
|
246
|
+
- 📊 **Sortable table** - Sort by time, method, status, duration
|
|
247
|
+
- 🔍 **Filters** - Filter by method, status, duration, time range
|
|
248
|
+
- ⚠️ **Error highlighting** - Errors shown in red with warning icons
|
|
249
|
+
- 📋 **Stack traces** - View exception details and Python tracebacks
|
|
250
|
+
- ▶️ **Replay** - One-click replay to see recorded request/response
|
|
251
|
+
- 📁 **Raw JSON** - Expandable view of full cassette data
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Security
|
|
256
|
+
|
|
257
|
+
Timetracer automatically protects sensitive data:
|
|
258
|
+
|
|
259
|
+
- Removes `Authorization`, `Cookie`, and `Set-Cookie` headers
|
|
260
|
+
- Masks sensitive fields like `password`, `token`, `api_key` in request/response bodies
|
|
261
|
+
- Enforces a 64KB body size limit to prevent large data captures
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Documentation
|
|
266
|
+
|
|
267
|
+
- [Why Timetracer?](docs/why-timetracer.md)
|
|
268
|
+
- [Quick Start Guide](docs/quickstart.md)
|
|
269
|
+
- [Configuration Reference](docs/configuration.md)
|
|
270
|
+
- [Dashboard Guide](docs/dashboard.md)
|
|
271
|
+
- [Plugin Guide](docs/plugins.md)
|
|
272
|
+
- [SQLAlchemy Integration](docs/sqlalchemy.md)
|
|
273
|
+
- [Flask Integration](docs/flask.md)
|
|
274
|
+
- [S3 Storage](docs/s3-storage.md)
|
|
275
|
+
- [Cassette Search](docs/search.md)
|
|
276
|
+
- [Security Best Practices](docs/security.md)
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## Contributing
|
|
281
|
+
|
|
282
|
+
Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
283
|
+
|
|
284
|
+
## License
|
|
285
|
+
|
|
286
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
timetracer/__init__.py,sha256=klGmsW7pY1kh5tVPxtLusff1_fEAG2v3eoPvUpaa6fo,676
|
|
2
|
+
timetracer/config.py,sha256=iX07nLAFp9NYgGGq1ZHp745VaL4urj0EVe24j95KBzE,11175
|
|
3
|
+
timetracer/constants.py,sha256=_I2tGijXG-JNkK-Ba_fSODu6JJ2SCJorNoslUgsbrdg,4703
|
|
4
|
+
timetracer/context.py,sha256=MN9EpUtyX9ZvaLhW71FSQdvOfTovhm_n_52q0ovu7RI,2368
|
|
5
|
+
timetracer/exceptions.py,sha256=bTo_A_-0DHOmCmX8XilFciNoQ0s3YtC7GajPfN6Hr04,3163
|
|
6
|
+
timetracer/session.py,sha256=e-TMLRGZGSZpO_k5ZxJPafUwMBsuMkyv-myiMwEZPTc,12072
|
|
7
|
+
timetracer/types.py,sha256=e3eGcgYSboXw7TIm6ftDoZiRzcjiFLfmvS81Kaj0RqA,5719
|
|
8
|
+
timetracer/cassette/__init__.py,sha256=JZ4XEK51WdD2iFV0yLpyDHgN0Xgb6--1qiXDslWJv58,274
|
|
9
|
+
timetracer/cassette/io.py,sha256=bhRGITVK2-v50oB0OGgdnVL8RX-xPqd1o7B_29-YlSI,12933
|
|
10
|
+
timetracer/cassette/naming.py,sha256=_wdaZR751s-stxDiQ1SkbrbMZ37Cj2MeOceOyFU_BBE,1680
|
|
11
|
+
timetracer/catalog/__init__.py,sha256=_IvevBJVUR15YaxyZQ3fTQRANOPAYPrURsGrzduXK38,8168
|
|
12
|
+
timetracer/cli/__init__.py,sha256=iIdNJFDOU-U77V2DN5x42g4VPoE0Swyp2H6MLd5UDyA,91
|
|
13
|
+
timetracer/cli/main.py,sha256=IpsKRuaQFLyLDQTwuPCRxmHHh3yRUG3bP5S1dMstW1M,20314
|
|
14
|
+
timetracer/cli/commands/__init__.py,sha256=FIIaAXFQd7YyXrzm9CVGePr0uLipPWIRC9rWu9JZMEc,31
|
|
15
|
+
timetracer/dashboard/__init__.py,sha256=sNQT3m-EMNLxiWr9jBOnyEKTlpFD0aLveOT2L-f4yeo,335
|
|
16
|
+
timetracer/dashboard/generator.py,sha256=280XMneIB91AYDLKiiGDYHIr7h-QyjkOwQjhRxEOcfU,6961
|
|
17
|
+
timetracer/dashboard/server.py,sha256=14LlR8uLV9XY7L_OlMgWmN2L903YvSDzslG62RmV12Q,9340
|
|
18
|
+
timetracer/dashboard/template.py,sha256=bNs0THiZezc6o2H8vO685PchrPBcFHhUewkeib1XcyE,31682
|
|
19
|
+
timetracer/diff/__init__.py,sha256=YvcuQcL8JhYUAFiZ_EFY6y8w3AW1hVySfsElGTLWoNQ,226
|
|
20
|
+
timetracer/diff/engine.py,sha256=9RZK-9-NyxnO-pqmG74G_VXvJJP3zfaHSToZ-jF4D0Q,8898
|
|
21
|
+
timetracer/diff/report.py,sha256=eVDhlOe7LdmPtXTdrGmFevAZlDIY026-jkJSLdTnJdw,3109
|
|
22
|
+
timetracer/integrations/__init__.py,sha256=U2gtm5ayknLJUY1llJMb11RbzRn56M29wPki9aiIFKs,745
|
|
23
|
+
timetracer/integrations/fastapi.py,sha256=-u3X1xMJv1LZRrNu8OBRVnk5hV0U3zdcf2tQ8Sx7z9c,17095
|
|
24
|
+
timetracer/integrations/flask.py,sha256=z9yAOx25bJkFYfBQT1OBlAd5FAqtQwR_ScGrexJHuh0,15938
|
|
25
|
+
timetracer/plugins/__init__.py,sha256=J7wzZT_eYfOEjKXdL33Bfo5MIPNOHUTBURkDQ1W1400,1253
|
|
26
|
+
timetracer/plugins/base.py,sha256=ZBZ9KohVIVKz9zwIv-sS2_xIS-0x25Diabw_Rxm_a48,1688
|
|
27
|
+
timetracer/plugins/httpx_plugin.py,sha256=nCK0ZYiKLYaH7nEd7Ul7Sl7CJa9PfWMPeaQRrEeNNAU,11975
|
|
28
|
+
timetracer/plugins/redis_plugin.py,sha256=Lzt6tqNi6foRsWyKGkOr7KsuCeEFOFMmlpiUZNX4Os8,8174
|
|
29
|
+
timetracer/plugins/requests_plugin.py,sha256=_wyfxhYTq4HWL-7ZfW8puMbqqPh4RrxVmNgazWo1WaE,9526
|
|
30
|
+
timetracer/plugins/sqlalchemy_plugin.py,sha256=-FEIZlrIRfYP0I7AfzJ5lP0TYUOJacFfegV4gPO5E_Q,7771
|
|
31
|
+
timetracer/policies/__init__.py,sha256=sGd2tEuLeW-h2CQ814ek8FfTb4MVkDtHVnSuRajiFtI,378
|
|
32
|
+
timetracer/policies/capture.py,sha256=D9QM16H_KfkYxglXUaXZ2P9-kHABpsTjrKa7dAu8nsc,1371
|
|
33
|
+
timetracer/policies/redaction.py,sha256=zOxYWBfj0eCUtWVljX6u4dmLwSv2HU3SivICDEMA0gc,4388
|
|
34
|
+
timetracer/replay/__init__.py,sha256=p-J2aHtdy_1PkYdtHNMR7Rm9CeWINtXoWHcHYMSkjP8,202
|
|
35
|
+
timetracer/replay/engine.py,sha256=mtNC4wM4utv4CK-r6X7Vyoa8LJUCDaBg2edEiGzV0tk,2270
|
|
36
|
+
timetracer/replay/errors.py,sha256=0nyTMxXeCNGdRZIbpkdRNpE8qocKn_3jY6Uo4iVhA4Y,189
|
|
37
|
+
timetracer/replay/matching.py,sha256=GP4Lm0zl4l0u9_hwA8RGlVy2qJqKA09fUeSxO-3hKOE,2339
|
|
38
|
+
timetracer/storage/__init__.py,sha256=IKUJaTjS_Cga_lDDpG06aAo33DAdHsiUCDv6d2lojfg,385
|
|
39
|
+
timetracer/storage/s3.py,sha256=rAIAYuK9cst__N-IoyYvBDs81B_GgWxt9XgE5HXsj6Q,9946
|
|
40
|
+
timetracer/timeline/__init__.py,sha256=WIXUSAfbBM4C5a0OUe295RQVt84VEf_yHFRg0-dpF-M,226
|
|
41
|
+
timetracer/timeline/generator.py,sha256=stiG-8CCSy4kobO3KkNu_TuEfVtgELmdFR5RBNie2vk,4093
|
|
42
|
+
timetracer/timeline/template.py,sha256=isHjUrDqk9gIx57s9ovD8lS2clnPm-wN7JdWGnvZxJE,10700
|
|
43
|
+
timetracer/utils/__init__.py,sha256=Gx5r4W_9DcUa6PMUThEf1f-oniNe3-aO0Hhkqn1m8AU,188
|
|
44
|
+
timetracer/utils/hashing.py,sha256=4yKZHfCguWcUYlA8PAx-sjGqZVSwIYElPVFbHO5TvJk,1638
|
|
45
|
+
timetracer/utils/time.py,sha256=XHVBmfgOgd1LmFCU2FCwFwRht4Su9nYLxq_8FRg__lQ,2609
|
|
46
|
+
timetracer-1.1.0.dist-info/licenses/LICENSE,sha256=ffV3XGF197Z-GTthIYyu18RzqviI7Fo2ZtpSbzBBD8Y,1079
|
|
47
|
+
timetracer-1.1.0.dist-info/METADATA,sha256=iNRbtMcYy1AFQi21YQ7xoyaIANUIoCEkbKM1cUF4HH0,9122
|
|
48
|
+
timetracer-1.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
49
|
+
timetracer-1.1.0.dist-info/entry_points.txt,sha256=wLj5_f4WLns9mY0cGUvtg6_ZW2SJM_LzG0k1Ta2aIh8,56
|
|
50
|
+
timetracer-1.1.0.dist-info/top_level.txt,sha256=WCH_j7MW48lvuI3rWt6mcLlbtn7MT9lYZq_lEyiw0Vw,11
|
|
51
|
+
timetracer-1.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Timetrace Contributors
|
|
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. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
timetracer
|