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.
Files changed (51) hide show
  1. timetracer/__init__.py +29 -0
  2. timetracer/cassette/__init__.py +6 -0
  3. timetracer/cassette/io.py +421 -0
  4. timetracer/cassette/naming.py +69 -0
  5. timetracer/catalog/__init__.py +288 -0
  6. timetracer/cli/__init__.py +5 -0
  7. timetracer/cli/commands/__init__.py +1 -0
  8. timetracer/cli/main.py +692 -0
  9. timetracer/config.py +297 -0
  10. timetracer/constants.py +129 -0
  11. timetracer/context.py +93 -0
  12. timetracer/dashboard/__init__.py +14 -0
  13. timetracer/dashboard/generator.py +229 -0
  14. timetracer/dashboard/server.py +244 -0
  15. timetracer/dashboard/template.py +874 -0
  16. timetracer/diff/__init__.py +6 -0
  17. timetracer/diff/engine.py +311 -0
  18. timetracer/diff/report.py +113 -0
  19. timetracer/exceptions.py +113 -0
  20. timetracer/integrations/__init__.py +27 -0
  21. timetracer/integrations/fastapi.py +537 -0
  22. timetracer/integrations/flask.py +507 -0
  23. timetracer/plugins/__init__.py +42 -0
  24. timetracer/plugins/base.py +73 -0
  25. timetracer/plugins/httpx_plugin.py +413 -0
  26. timetracer/plugins/redis_plugin.py +297 -0
  27. timetracer/plugins/requests_plugin.py +333 -0
  28. timetracer/plugins/sqlalchemy_plugin.py +280 -0
  29. timetracer/policies/__init__.py +16 -0
  30. timetracer/policies/capture.py +64 -0
  31. timetracer/policies/redaction.py +165 -0
  32. timetracer/replay/__init__.py +6 -0
  33. timetracer/replay/engine.py +75 -0
  34. timetracer/replay/errors.py +9 -0
  35. timetracer/replay/matching.py +83 -0
  36. timetracer/session.py +390 -0
  37. timetracer/storage/__init__.py +18 -0
  38. timetracer/storage/s3.py +364 -0
  39. timetracer/timeline/__init__.py +6 -0
  40. timetracer/timeline/generator.py +150 -0
  41. timetracer/timeline/template.py +370 -0
  42. timetracer/types.py +197 -0
  43. timetracer/utils/__init__.py +6 -0
  44. timetracer/utils/hashing.py +68 -0
  45. timetracer/utils/time.py +106 -0
  46. timetracer-1.1.0.dist-info/METADATA +286 -0
  47. timetracer-1.1.0.dist-info/RECORD +51 -0
  48. timetracer-1.1.0.dist-info/WHEEL +5 -0
  49. timetracer-1.1.0.dist-info/entry_points.txt +2 -0
  50. timetracer-1.1.0.dist-info/licenses/LICENSE +21 -0
  51. timetracer-1.1.0.dist-info/top_level.txt +1 -0
@@ -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
+ [![CI](https://github.com/usv240/timetracer/actions/workflows/ci.yml/badge.svg)](https://github.com/usv240/timetracer/actions)
56
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
57
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
58
+ [![PyPI](https://img.shields.io/pypi/v/timetracer.svg)](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,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ timetracer = timetracer.cli.main:main
@@ -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