Files
http-latency-prober/docs/plans/2026-07-02-09-57-hxprobe-toolchain-modernization.md
Jan Novak f487a4b1bd docs: hxprobe plans, summaries, explanations, usage, and changelog
Plans (docs/plans/):
- 2026-07-01-23-47-py-hxprobe-httpx.md — initial httpx probe design
- 2026-07-02-09-32 through 14-05 — standalone project, toolchain, usage
  doc + Makefile, file input (-f), simplification pass, run-summary footer

Summaries (docs/summaries/): one per completed feature, recording what
was actually built, deviations from the plan, and verification steps

Explanations (docs/explanations/): two deep-dives written during review —
hxprobe concurrency model and worst-exit-code + render-loop analysis

Usage (docs/usage/hxprobe.md): overview with pointer to hxprobe/USAGE.md
for the full runnable reference

Walkthrough (docs/py-latprobe-walkthrough.md): narrative tour of the
latprobe Python package for interview / code-review context

CHANGELOG.md: entries for all hxprobe features (toolchain, usage doc,
file input, simplification, run-summary footer)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 13:22:59 +02:00

116 lines
6.0 KiB
Markdown

# Plan: modernize hxprobe's Python toolchain
## Context
`hxprobe` (`hxprobe/`) is a fully standalone Python project (own
`pyproject.toml`, own venv, own tests — see
`docs/summaries/2026-07-02-09-32-hxprobe-standalone-project.md`). Its
toolchain is currently bare-minimum: hand-rolled `python -m venv` + `pip
install -e .`, no linter/formatter, no type checker, no dependency lockfile,
and stdlib `unittest` with a filename-glob convention
(`test_[!i]*.py`/`test_integration.py`) to separate hermetic from live tests.
User confirmed direction (via AskUserQuestion): adopt **uv** for env/deps
(with a lockfile), add **ruff** for lint+format, **skip mypy** for now, and
**swap the test runner to pytest** (existing `unittest.TestCase` classes run
unchanged under pytest — no test-code rewrite) using a proper
`@pytest.mark.integration` marker instead of the filename-glob trick.
Confirmed via inspection: `uv` (v0.11.19) is already installed globally
(Homebrew); `ruff`/`mypy`/`pytest` only exist under an unrelated pyenv 3.12.3
shim, not on the 3.14 interpreter this project targets — `uv` sidesteps that
mismatch by installing everything into the project's own venv. Git remote is
a self-hosted Gitea instance, not GitHub, so no CI is being set up here.
## Changes
**`hxprobe/pyproject.toml`:**
- Add `[dependency-groups]` with `dev = ["pytest>=8.0", "ruff>=0.8"]` (PEP
735, the current uv-native way to declare dev-only deps — keeps the
install-as-a-library `dependencies` list clean).
- Add `[tool.pytest.ini_options]`: `testpaths = ["tests"]` and a registered
`integration` marker (avoids `PytestUnknownMarkWarning`).
- Add `[tool.ruff]`: `target-version = "py311"` (matches `requires-python`)
and `line-length = 100` (close to the codebase's existing longest lines,
~103 chars, to minimize reformatting churn).
- Leave `[build-system]`/`[tool.setuptools]` untouched — build backend
wasn't part of the discussion, setuptools works fine here.
**`hxprobe/.python-version`:** new file, `3.14`, so `uv sync`/`uv run` pin the
same interpreter the rest of the repo uses without relying on `$PATH` order.
**`hxprobe/uv.lock`:** generated by `uv lock` — first real lockfile pinning
`httpx`, `httpcore`, `h2`, `certifi`, and friends. Committed (not gitignored;
lockfiles belong in version control).
**Test changes (runner swap only, no rewrite):**
- `hxprobe/tests/test_integration.py`: add `pytestmark = pytest.mark.integration`
at module level. This replaces the "named `test_integration.py` so the
`test_[!i]*.py` glob skips it" convention — selection becomes `-m
"not integration"` / `-m integration`, independent of filename.
- `test_probe.py`/`test_cli.py`: no changes — they're already hermetic
(local `http.server` fixtures only) and need no marker.
- `unittest.TestCase` classes, `_NEEDS_NET = unittest.skipUnless(...)`, and
the `if __name__ == "__main__": unittest.main()` guards all stay exactly
as-is — pytest natively discovers and runs unittest-style tests and
respects `unittest.skip*` decorators with zero changes required.
**Lint fixes:** run `ruff check --fix` / `ruff format` over `hxprobe/` and
review the diff. One known pre-existing issue it will flag: `import sys` in
`cli.py` is unused (inherited from the original `latprobe/cli.py`) — remove
it. Otherwise expect mostly whitespace/quote-style normalization.
**`Makefile`:** replace the `hx-*` section to run through `uv` instead of a
hand-managed venv:
```makefile
HX_DIR := hxprobe # (drop HX_VENV / HX_VENV_PYTHON — uv owns this now)
hx-deps: cd $(HX_DIR) && uv sync
hx-run: (deps: hx-deps) cd $(HX_DIR) && uv run python -m hxprobe $(ARGS)
hx-lint: (deps: hx-deps) cd $(HX_DIR) && uv run ruff check .
hx-fmt: (deps: hx-deps) cd $(HX_DIR) && uv run ruff format .
hx-test: (deps: hx-deps) cd $(HX_DIR) && uv run pytest tests -m "not integration" -v $(ARGS)
hx-test-integration: (deps: hx-deps) cd $(HX_DIR) && uv run pytest tests -m integration -v $(ARGS)
hx-check: hx-lint hx-test (test gate now includes lint, mirroring go-check's fmt+vet+test bundling)
hx-clean: also removes .pytest_cache / .ruff_cache alongside __pycache__/egg-info
```
Umbrella `test`/`check`/`clean` targets keep delegating to `hx-test`/
`hx-check`/`hx-clean` unchanged.
**`hxprobe/README.md`:** new, minimal — since this project is meant to be
`cp -r`-able to its own repo, it should carry its own quick-start
(`uv sync`, `uv run python -m hxprobe <url>`, `uv run pytest`) rather than
relying on the monorepo's root docs.
**Docs:** update `docs/usage/hxprobe.md`'s "Setup" section (`uv sync`
instead of manual venv+pip) and "Makefile targets" section (add
`hx-lint`/`hx-fmt`, update test invocation description). New
`docs/plans/<timestamp>-hxprobe-toolchain-modernization.md` and
`docs/summaries/<timestamp>-hxprobe-toolchain-modernization.md` per
CLAUDE.md convention. New `CHANGELOG.md` entry.
**`.gitignore`:** already covers `.venv/`/`__pycache__/`/`*.egg-info/`
unanchored; add `.pytest_cache/` and `.ruff_cache/` (new caches these tools
create).
## Verification
1. `cd hxprobe && uv sync` — creates `.venv`, generates/uses `uv.lock`,
installs `httpx[http2]` + dev deps (`pytest`, `ruff`).
2. `make hx-run ARGS="-v http://github.com"` — same output as before
(HTTP/2, 1 redirect, IP/TLS/cert shown) — confirms the runtime behavior
is untouched by the toolchain swap.
3. `make hx-lint` — clean (after fixing whatever `ruff check` surfaces,
including the unused `import sys`).
4. `make hx-test` — all hermetic tests pass under pytest; confirm the
`integration`-marked tests are excluded (test count matches the current
28 hermetic tests).
5. `make hx-test-integration` — the 9 live tests run and pass under the
`integration` marker selection.
6. `make check` (top-level) — Go + latprobe + hxprobe (lint + test) all
green in one gate.
7. `grep -rn "latprobe" hxprobe/` — still zero matches (toolchain change
must not reintroduce coupling).
8. Confirm `python/latprobe/` is untouched (`git diff --stat python/` empty)
— this is a hxprobe-only change.