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>
116 lines
6.0 KiB
Markdown
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.
|