timeo 0.2.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.
@@ -0,0 +1,31 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, dev]
6
+ pull_request:
7
+ branches: [main, dev]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ python-version: ["3.9", "3.10", "3.11", "3.12"]
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - uses: astral-sh/setup-uv@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install dependencies
25
+ run: uv sync --all-groups
26
+
27
+ - name: Run pre-commit
28
+ run: uv run pre-commit run --all-files
29
+
30
+ - name: Run tests
31
+ run: uv run pytest
@@ -0,0 +1,24 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published, released]
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ environment: pypi
12
+ permissions:
13
+ id-token: write # required for OIDC trusted publishing
14
+
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - uses: astral-sh/setup-uv@v5
19
+
20
+ - name: Build package
21
+ run: uv build
22
+
23
+ - name: Publish to PyPI
24
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,20 @@
1
+ name: release-please
2
+ on:
3
+ push:
4
+ branches:
5
+ - main
6
+ permissions:
7
+ contents: write # to push tags and changelog
8
+ issues: write # (optional) for any issue linkage
9
+ pull-requests: write # to create/modify the release PR
10
+ jobs:
11
+ release:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: googleapis/release-please-action@v4
16
+ with:
17
+ # Use a GitHub token or personal access token:
18
+ token: ${{ secrets.GITHUB_TOKEN }}
19
+ # A simple release strategy (no code version file):
20
+ release-type: python
timeo-0.2.0/.gitignore ADDED
@@ -0,0 +1,50 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.pyo
5
+ *.pyd
6
+ *.so
7
+
8
+ # Virtual environments
9
+ .venv/
10
+ venv/
11
+ env/
12
+
13
+ # Distribution / packaging
14
+ dist/
15
+ build/
16
+ *.egg-info/
17
+ *.egg
18
+ .eggs/
19
+
20
+ # uv
21
+ .uv/
22
+ uv.lock # optional — commit this if you want reproducible installs
23
+
24
+ # Testing
25
+ .pytest_cache/
26
+ .coverage
27
+ htmlcov/
28
+ .tox/
29
+
30
+ # Type checking
31
+ .mypy_cache/
32
+ .ruff_cache/
33
+
34
+ # Jupyter
35
+ .ipynb_checkpoints/
36
+
37
+ # Editors
38
+ .vscode/
39
+ .idea/
40
+ *.swp
41
+ *.swo
42
+
43
+ # OS
44
+ .DS_Store
45
+ Thumbs.db
46
+
47
+ # Environment variables
48
+ .env
49
+ .env.*
50
+ !.env.example
@@ -0,0 +1,11 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v2.3.0
4
+ hooks:
5
+ - id: check-yaml
6
+ - id: end-of-file-fixer
7
+ - id: trailing-whitespace
8
+ - repo: https://github.com/psf/black
9
+ rev: 22.10.0
10
+ hooks:
11
+ - id: black
@@ -0,0 +1 @@
1
+ 3.9
@@ -0,0 +1,61 @@
1
+ # Changelog
2
+
3
+ ## [0.2.0](https://github.com/wtewalt/timeo/compare/v0.1.0...v0.2.0) (2026-04-09)
4
+
5
+
6
+ ### Features
7
+
8
+ * add smoke test scripts for sequential, concurrent, and live con… ([92f52a8](https://github.com/wtewalt/timeo/commit/92f52a8f7d5a658afd17d9df0abb33ef95f479f7))
9
+ * add smoke test scripts for sequential, concurrent, and live context modes ([6f14c94](https://github.com/wtewalt/timeo/commit/6f14c94cc424ea4afd1885f674e21e395b5e3007))
10
+ * add test suite for task, hashing, cache, decorator, and learn mode ([754fd86](https://github.com/wtewalt/timeo/commit/754fd86a95280c2e7312ba0a02cb79162531d2aa))
11
+ * implement [@timeo](https://github.com/timeo).track decorator, advance(), and iter() ([8894dbf](https://github.com/wtewalt/timeo/commit/8894dbfe853b4e57ef35a4fed97202f4a07107d5))
12
+ * implement learn mode (learn=True) with EMA-driven progress bar ([946ea49](https://github.com/wtewalt/timeo/commit/946ea49b60fe3d512cf4a10f0922193b3479d9cc))
13
+ * implement learn mode (learn=True) with EMA-driven progress bar ([dda6553](https://github.com/wtewalt/timeo/commit/dda6553476345e62bea4dda0943e6392c14b0fda))
14
+ * implement ProgressManager singleton ([d352bb8](https://github.com/wtewalt/timeo/commit/d352bb87be0baddae592e02359bc90e8456c22d2))
15
+ * implement ProgressManager singleton ([83a9151](https://github.com/wtewalt/timeo/commit/83a91510eb4643f4ab7a0e78bfe4d7463af18fec))
16
+ * implement timing cache and function bytecode hashing ([9bf46fb](https://github.com/wtewalt/timeo/commit/9bf46fb491da12e616f9a5582fe5f3ee91d24012))
17
+ * implement timing cache and function bytecode hashing ([7942968](https://github.com/wtewalt/timeo/commit/7942968c7bbb73b5cd290af1b4258677d9550192))
18
+ * implement TrackedTask model ([b7421e3](https://github.com/wtewalt/timeo/commit/b7421e39146bbfdfcf3a9997a86341486cf8f994))
19
+ * scaffold timeo package structure ([fd042bb](https://github.com/wtewalt/timeo/commit/fd042bbb2f2e5e5c0ffca2d2affa9d09875b6cfe))
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * add future annotations import to task.py for Python 3.9 compat ([6fc84f3](https://github.com/wtewalt/timeo/commit/6fc84f318a5ab8c7ea58bbc608eb308e5832ac39))
25
+ * add future annotations import to task.py for Python 3.9 compat ([e365cf7](https://github.com/wtewalt/timeo/commit/e365cf793dde69fd8faf8aae6c3a640e2a970225))
26
+ * restore ProgressManager implementation on feat/track-decorator branch ([7366fc7](https://github.com/wtewalt/timeo/commit/7366fc7f823f9f890f8a50f3c26301a1b63e3ebc))
27
+ * updated script for initial test ([a02281d](https://github.com/wtewalt/timeo/commit/a02281de1bb0766b0d42d8cbeab4212a8ab11776))
28
+ * use endswith for qualname assertion in learn mode test ([893dda8](https://github.com/wtewalt/timeo/commit/893dda81955ba21af89fc91d318dac303b7673cb))
29
+
30
+
31
+ ### Documentation
32
+
33
+ * add CLAUDE.md with project overview and architecture ([369a524](https://github.com/wtewalt/timeo/commit/369a524e7f39c620018b337653b06c6d9b5bc267))
34
+ * add CONVENTIONS.md with commit, branch, pre-commit, and release standards ([2cdb283](https://github.com/wtewalt/timeo/commit/2cdb283ce5b0ca844eaaa6426973cf762e553ddf))
35
+ * add nix shell requirement to development environment section ([7a2b2ec](https://github.com/wtewalt/timeo/commit/7a2b2ec825e755c0490d1bfe7508fc2a5f90e7dc))
36
+ * add stepwise development plan in steps/ ([300f3b8](https://github.com/wtewalt/timeo/commit/300f3b8f2ca8964455c37285dee161af997a33cf))
37
+ * add timing-based progress estimation design to CLAUDE.md ([d6bcb9b](https://github.com/wtewalt/timeo/commit/d6bcb9bcd5c6ac891c55bd8a950b33720f5f8d84))
38
+ * document release-please, PyPI publishing, and pre-commit setup ([7d82fad](https://github.com/wtewalt/timeo/commit/7d82fad72d1682cf1aab0bfd54d14d001906e507))
39
+ * redesign README with badges, tables, and improved formatting ([bdb3f3f](https://github.com/wtewalt/timeo/commit/bdb3f3f22b644847c7423d68a6218276d01600be))
40
+ * resolve all open design questions in CLAUDE.md ([7acf12a](https://github.com/wtewalt/timeo/commit/7acf12a0861a29cb54f23cce5196b1ba4c7ec033))
41
+ * write README with usage examples for basic, learn, and concurrent modes ([87201b5](https://github.com/wtewalt/timeo/commit/87201b5abfa65d905e9b21f426df99a6ff38e35d))
42
+
43
+ ## 0.1.0 (2026-04-03)
44
+
45
+
46
+ ### Features
47
+
48
+ * added rich as a dependency ([262c826](https://github.com/wtewalt/timeo/commit/262c826123b46339d83457578962f8a3e08e2cf1))
49
+ * change to release config ([094f8ea](https://github.com/wtewalt/timeo/commit/094f8ea74b23f885fdfb4e9dd0b6ea10a53d6cdd))
50
+ * enabled pre-commit checks and release tracking ([4ab1608](https://github.com/wtewalt/timeo/commit/4ab160830194534141c411777242a81f188294eb))
51
+ * flake added ([826aac3](https://github.com/wtewalt/timeo/commit/826aac3f67c76fbff8bb85fb1db9f570db22929d))
52
+ * new flake ([0f0b1f9](https://github.com/wtewalt/timeo/commit/0f0b1f93b4da0cb7c1d8a0eef50ef48175756de2))
53
+ * new nix config ([858f3ff](https://github.com/wtewalt/timeo/commit/858f3ff2d2af71f401c9b769ff871487e3f37042))
54
+ * updates to release ([1690435](https://github.com/wtewalt/timeo/commit/1690435928b06eeff239a49b448a80aaed3b580f))
55
+ * working flake with uv and pre-commit hooks ([c49b388](https://github.com/wtewalt/timeo/commit/c49b3888c56d8386b07445c39aa1b849e2abaaad))
56
+
57
+
58
+ ### Bug Fixes
59
+
60
+ * added changelog ([59d9c6f](https://github.com/wtewalt/timeo/commit/59d9c6f138d6bd8299a8e8cf87b9d3b1f6a87d54))
61
+ * updates to allow release tracking ([76fa10f](https://github.com/wtewalt/timeo/commit/76fa10f46d56eb37b5b821edce96946b4b3e3dda))
timeo-0.2.0/CLAUDE.md ADDED
@@ -0,0 +1,206 @@
1
+ # Timeo — Project Guide
2
+
3
+ ## Overview
4
+
5
+ **Timeo** is a Python package that lets developers annotate functions with a decorator to automatically track and display their progress in the terminal. When a script runs, all decorated functions are rendered together as a collection of live progress bars using `rich`.
6
+
7
+ ## Core Concept
8
+
9
+ The user imports `timeo`, applies `@timeo.track` (or similar) to any function, and when the script executes, those functions are monitored and displayed as live progress bars in the terminal — all managed automatically without the user needing to manually wire up progress bar logic.
10
+
11
+ ### Example Usage
12
+
13
+ ```python
14
+ import timeo
15
+
16
+ @timeo.track
17
+ def process_files(files):
18
+ for file in files:
19
+ handle(file)
20
+ timeo.advance() # or automatic via iteration hooks
21
+
22
+ @timeo.track
23
+ def download_data(urls):
24
+ for url in urls:
25
+ fetch(url)
26
+ timeo.advance()
27
+
28
+ process_files(my_files)
29
+ download_data(my_urls)
30
+ ```
31
+
32
+ Terminal output (while running):
33
+ ```
34
+ Processing files ━━━━━━━━━━━━━━━━━━━━━━ 45% 0:00:12
35
+ Downloading data ━━━━━━━━━━━━━━━━━━━━━━━━━━ 72% 0:00:04
36
+ ```
37
+
38
+ ## Architecture
39
+
40
+ ### Key Components
41
+
42
+ - **`timeo/decorator.py`** — The `@timeo.track` decorator. Wraps a function, registers it with the progress manager, and starts/stops tracking around the function's execution.
43
+ - **`timeo/manager.py`** — The `ProgressManager` singleton. Owns the `rich.progress.Progress` instance, maintains the registry of tracked tasks, and controls the live display lifecycle.
44
+ - **`timeo/task.py`** — Represents a single tracked task (name, total, current progress, metadata).
45
+ - **`timeo/__init__.py`** — Public API surface. Exports `track`, `advance`, and any other user-facing symbols.
46
+
47
+ ### Design Principles
48
+
49
+ - **Minimal user friction** — the decorator should be the only required touchpoint.
50
+ - **Non-intrusive** — does not require users to change the internals of their functions beyond adding `timeo.advance()` calls (or wrapping iterables).
51
+ - **Composable** — multiple decorated functions should render together as a unified live display, not separate progress bars.
52
+ - **Graceful degradation** — if `total` is unknown, show a spinner or indeterminate bar rather than failing.
53
+
54
+ ## Dependencies
55
+
56
+ - **`rich`** — All progress bar rendering, formatting, and live display. Use `rich.progress.Progress` with `rich.live.Live` for the unified multi-bar display.
57
+
58
+ ## Releasing
59
+
60
+ ### Release Process
61
+
62
+ This repo uses **[release-please](https://github.com/googleapis/release-please)** to automate versioning and changelog generation. It parses conventional commits on `main` to determine the next version and opens a release PR automatically.
63
+
64
+ - Merge the release-please PR on `main` to cut a new release.
65
+ - On release, a GitHub Actions workflow automatically builds and publishes the package to **PyPI**.
66
+ - The version in `pyproject.toml` is the source of truth and is updated by release-please.
67
+
68
+ ### Conventional Commits
69
+
70
+ All commits must follow the [Conventional Commits](https://www.conventionalcommits.org/) spec since release-please relies on them to determine version bumps:
71
+
72
+ | Prefix | Effect |
73
+ |--------|--------|
74
+ | `fix:` | Patch bump |
75
+ | `feat:` | Minor bump |
76
+ | `feat!:` / `BREAKING CHANGE:` | Major bump |
77
+ | `docs:`, `chore:`, `refactor:`, etc. | No version bump |
78
+
79
+ ## Development
80
+
81
+ ### Setup
82
+
83
+ ```bash
84
+ uv sync # install dependencies
85
+ task dev # or however the dev environment is activated
86
+ ```
87
+
88
+ ### Code Quality
89
+
90
+ This repo uses **[pre-commit](https://pre-commit.com/)** to enforce code quality on every commit. Hooks are defined in `.pre-commit-config.yaml`.
91
+
92
+ Install hooks locally (one-time):
93
+ ```bash
94
+ pre-commit install
95
+ ```
96
+
97
+ Hooks enforced:
98
+ - **ruff** — linting and formatting
99
+ - **mypy** — static type checking
100
+ - TOML/YAML validation
101
+
102
+ Run manually against all files:
103
+ ```bash
104
+ pre-commit run --all-files
105
+ ```
106
+
107
+ ### Project Structure
108
+
109
+ ```
110
+ timeo/
111
+ ├── __init__.py # public API
112
+ ├── decorator.py # @timeo.track implementation
113
+ ├── manager.py # ProgressManager, rich.Progress integration
114
+ └── task.py # TrackedTask dataclass/model
115
+ ```
116
+
117
+ ## Timing-Based Progress Estimation
118
+
119
+ ### Overview
120
+
121
+ An opt-in mode (`@timeo.track(learn=True)`) that drives the progress bar using elapsed time against an expected duration gleaned from previous runs of that function. Rather than tracking discrete steps, the bar fills as `elapsed / expected_duration`.
122
+
123
+ ### Opt-In Behavior
124
+
125
+ - Default behavior is **unchanged** — `@timeo.track` with no arguments works as always (step-based or indeterminate).
126
+ - Time-based estimation is activated explicitly: `@timeo.track(learn=True)`.
127
+ - On the **first run** (no cached data yet), display an indeterminate progress bar with a "Learning timing..." label so the user knows data is being collected but no estimate is available yet.
128
+ - On subsequent runs, use the cached EMA estimate to render a determinate time-driven progress bar.
129
+
130
+ ### Local Timing Cache
131
+
132
+ - Stored at `~/.cache/timeo/timings.json` (use `platformdirs` for cross-platform path resolution).
133
+ - Each entry is keyed by a **hash of the function's bytecode** (`dis` or `inspect` + `hashlib`) rather than its name or module path. This ensures the cache automatically invalidates when the function's implementation changes — a refactored or updated function is treated as a new function with no prior data.
134
+ - Cache entry schema:
135
+ ```json
136
+ {
137
+ "<fn_hash>": {
138
+ "name": "my_module.process_files",
139
+ "ema_duration_seconds": 12.4,
140
+ "run_count": 7,
141
+ "last_updated": "2026-04-08T00:00:00Z"
142
+ }
143
+ }
144
+ ```
145
+ - `name` is stored for human readability/debugging only — it is never used as a lookup key.
146
+
147
+ ### EMA Strategy
148
+
149
+ - After each run completes, update the stored estimate using an **Exponential Moving Average**:
150
+ ```
151
+ ema = alpha * actual_duration + (1 - alpha) * previous_ema
152
+ ```
153
+ - Suggested default `alpha = 0.2` (weights recent runs moderately; can be tuned).
154
+ - On the very first run, `ema` is seeded with the actual duration directly (no prior value to blend with).
155
+ - The EMA converges quickly enough that estimates become useful within 3–5 runs.
156
+
157
+ ### Progress Bar Behavior
158
+
159
+ - The bar advances by `elapsed_time / ema_duration`, updated on a tick interval.
160
+ - If the function **overruns** the estimate, the bar stalls at ~99% rather than exceeding 100% or erroring — it completes to 100% only when the function actually returns.
161
+ - `rich` custom progress columns will be used to render this time-driven bar alongside any step-based bars in the same live display.
162
+
163
+ ### Function Change Detection
164
+
165
+ - At call time, hash the function's bytecode (`fn.__code__.co_code` or the full `code` object via `marshal`/`hashlib`).
166
+ - If the hash does not match any cached entry, treat it as a brand-new function (show "Learning timing..." and start fresh).
167
+ - Old/stale entries for the previous hash are not automatically deleted — they accumulate silently. A future cache cleanup utility can address this.
168
+
169
+ ## Design Decisions
170
+
171
+ ### `total` — Inferred from `Sized` args
172
+ `timeo` inspects the arguments passed to a decorated function at call time and looks for any that implement `__len__()`. The first `Sized` argument found is used as `total`. No user input required. If no `Sized` argument is found, the bar is indeterminate.
173
+
174
+ ### `advance()` — `contextvars.ContextVar` (under the hood)
175
+ `timeo.advance()` uses a `ContextVar` internally to know which task to update. The decorator pushes the current task onto the `ContextVar` before the function runs and pops it when it returns. This is fully transparent to the user — they only call `timeo.advance()`, no context management required. This also makes concurrent functions safe: threads and async tasks each see their own isolated `ContextVar` value.
176
+
177
+ ### Iteration wrapping — `timeo.iter()`
178
+ `timeo.iter(items)` is supported as a convenience wrapper that automatically calls `advance()` on each iteration, eliminating the need for manual `advance()` calls:
179
+
180
+ ```python
181
+ @timeo.track
182
+ def process_files(files):
183
+ for f in timeo.iter(files):
184
+ handle(f)
185
+ ```
186
+
187
+ ### Concurrent functions — Stacked bars with completed tasks collapsed
188
+ Multiple simultaneously-running tracked functions are each given their own row in the live display. When a function completes, its bar collapses to a single summary line with a checkmark and elapsed time. Only in-progress bars are shown in full:
189
+
190
+ ```
191
+ ✓ process_files 12.4s
192
+ download_data ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 72% 0:00:04
193
+ compress_output ━━━━━━━━━━ 20% 0:00:31
194
+ ```
195
+
196
+ ### Display lifecycle — Hybrid (automatic with optional explicit control)
197
+ By default, the live display starts automatically on the first decorated function call and tears down when the last tracked function finishes. Teardown is guaranteed via `try/finally` in the decorator so exceptions never leave the terminal in a broken state. A reference counter tracks how many tasks are active; the display is torn down when it reaches zero.
198
+
199
+ For complex scripts where automatic teardown is insufficient (e.g., conditional branching, multiprocessing), the user can take explicit control with a context manager:
200
+
201
+ ```python
202
+ with timeo.live():
203
+ process_files(my_files)
204
+ download_data(my_urls)
205
+ # display always tears down cleanly here
206
+ ```
@@ -0,0 +1,171 @@
1
+ # Conventions
2
+
3
+ Best practices and rules for making changes to this repository.
4
+
5
+ ---
6
+
7
+ ## Development Environment
8
+
9
+ This repo uses [Nix flakes](https://nixos.wiki/wiki/Flakes) to provide a fully reproducible development environment. **All development and testing must be done inside the Nix shell.** Tools like `uv`, `python`, `task`, and `pre-commit` are provided by the Nix environment and may not be available outside of it.
10
+
11
+ ### Entering the shell
12
+
13
+ ```bash
14
+ nix develop
15
+ ```
16
+
17
+ This drops you into a shell with all dependencies and tools available. Run this before doing anything else in the repo.
18
+
19
+ ### Why
20
+ Running commands outside the Nix shell (e.g., using a system Python or a globally installed `uv`) risks version mismatches and unreproducible behaviour. Always use the Nix shell.
21
+
22
+ ---
23
+
24
+ ## Commits
25
+
26
+ All commits must follow the [Conventional Commits](https://www.conventionalcommits.org/) spec. This is required — release-please parses commit messages on `main` to determine version bumps and generate the changelog automatically.
27
+
28
+ ### Format
29
+
30
+ ```
31
+ <type>[optional scope]: <short description>
32
+
33
+ [optional body]
34
+
35
+ [optional footer(s)]
36
+ ```
37
+
38
+ ### Types and version impact
39
+
40
+ | Type | When to use | Version bump |
41
+ |---|---|---|
42
+ | `feat` | A new user-facing feature | Minor (`0.x.0`) |
43
+ | `fix` | A bug fix | Patch (`0.0.x`) |
44
+ | `feat!` / `fix!` | Breaking change (also add `BREAKING CHANGE:` footer) | Major (`x.0.0`) |
45
+ | `refactor` | Code restructuring with no behaviour change | None |
46
+ | `docs` | Documentation only | None |
47
+ | `chore` | Maintenance, dependency updates, config | None |
48
+ | `test` | Adding or updating tests | None |
49
+ | `ci` | CI/CD workflow changes | None |
50
+ | `style` | Formatting, whitespace (no logic change) | None |
51
+ | `perf` | Performance improvement | None |
52
+
53
+ ### Examples
54
+
55
+ ```
56
+ feat: add timeo.iter() convenience wrapper
57
+ fix: reset ContextVar correctly on exception in track decorator
58
+ feat!: rename advance() to step()
59
+
60
+ BREAKING CHANGE: advance() has been renamed to step() for clarity
61
+ docs: add learn mode usage examples to README
62
+ chore: update rich to 14.4.0
63
+ test: add cache EMA update unit tests
64
+ ci: add Python 3.13 to test matrix
65
+ ```
66
+
67
+ ### Rules
68
+ - Use the **imperative mood** in the description ("add", "fix", "update" — not "added", "fixes", "updating").
69
+ - Keep the description under **72 characters**.
70
+ - Do not end the description with a period.
71
+ - If a commit addresses a GitHub issue, reference it in the footer: `Closes #42`.
72
+
73
+ ---
74
+
75
+ ## Branches
76
+
77
+ | Branch | Purpose |
78
+ |---|---|
79
+ | `main` | Production-ready code only. release-please runs here. |
80
+ | `dev` | Active development branch. All work happens here or in feature branches off `dev`. |
81
+ | `feat/<name>` | Optional feature branches for larger pieces of work, branched from `dev`. |
82
+
83
+ - Never commit directly to `main` — always go through a PR.
84
+ - PRs from `dev` → `main` should only be made when the code is ready to release.
85
+
86
+ ---
87
+
88
+ ## Pre-commit
89
+
90
+ This repo uses [pre-commit](https://pre-commit.com/) to enforce code quality automatically on every commit. Hooks are defined in `.pre-commit-config.yaml`.
91
+
92
+ ### Setup (one-time)
93
+
94
+ ```bash
95
+ pre-commit install
96
+ ```
97
+
98
+ This installs the hooks into `.git/hooks/` so they run automatically on `git commit`.
99
+
100
+ ### Hooks that run on every commit
101
+
102
+ | Hook | What it checks |
103
+ |---|---|
104
+ | `check-yaml` | YAML files are valid |
105
+ | `end-of-file-fixer` | All files end with a newline |
106
+ | `trailing-whitespace` | No trailing whitespace on any line |
107
+ | `black` | Python code is formatted with Black |
108
+
109
+ ### Running manually
110
+
111
+ To run all hooks against all files (useful before opening a PR):
112
+
113
+ ```bash
114
+ pre-commit run --all-files
115
+ ```
116
+
117
+ To run a specific hook:
118
+
119
+ ```bash
120
+ pre-commit run black --all-files
121
+ ```
122
+
123
+ ### Rules
124
+ - Never bypass hooks with `--no-verify` unless there is a documented, exceptional reason.
125
+ - If a hook fails, fix the issue and re-stage before committing — do **not** amend a previous commit to work around it.
126
+ - The CI pipeline also runs `pre-commit run --all-files` — a passing local run is required before pushing.
127
+
128
+ ---
129
+
130
+ ## Code Style
131
+
132
+ - **Formatter:** [Black](https://black.readthedocs.io/) (enforced by pre-commit).
133
+ - **Line length:** Black's default (88 characters).
134
+ - **Type annotations:** Required on all public functions and methods. mypy must pass with no errors.
135
+ - **Docstrings:** Use for all public modules, classes, and functions. Keep them concise.
136
+ - **Imports:** Standard library first, third-party second, local last — separated by blank lines.
137
+
138
+ ---
139
+
140
+ ## Testing
141
+
142
+ - All new features must be accompanied by tests in `tests/`.
143
+ - Tests live in `tests/` at the repo root and mirror the `timeo/` module structure (e.g., `timeo/cache.py` → `tests/test_cache.py`).
144
+ - Use `pytest`. Run with:
145
+ ```bash
146
+ uv run pytest
147
+ ```
148
+ - Do not write tests that render to the terminal or write to `~/.cache/timeo/` — mock `ProgressManager` and patch `timeo.cache._cache_path` in tests that touch the cache.
149
+ - Aim for >80% coverage on all modules.
150
+
151
+ ---
152
+
153
+ ## Releasing
154
+
155
+ Releases are fully automated — do not manually edit version numbers or create GitHub releases by hand.
156
+
157
+ 1. Merge commits following the Conventional Commits spec into `main`.
158
+ 2. release-please automatically opens a release PR updating `pyproject.toml` and `CHANGELOG.md`.
159
+ 3. Merge the release PR → a GitHub release is created → the publish workflow pushes the new version to PyPI.
160
+
161
+ See `CLAUDE.md` for full details on the release process.
162
+
163
+ ---
164
+
165
+ ## Dependencies
166
+
167
+ - Manage dependencies with `uv`. Never edit `uv.lock` by hand.
168
+ - To add a dependency: `uv add <package>`
169
+ - To add a dev dependency: `uv add --dev <package>`
170
+ - Keep `uv.lock` committed and up to date.
171
+ - Pin dependency versions loosely in `pyproject.toml` (e.g., `rich>=14.0`) and let `uv.lock` handle exact pinning.
timeo-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 WilliamTewalt
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.