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>
This commit is contained in:
83
docs/summaries/2026-07-02-12-05-hxprobe-simplification.md
Normal file
83
docs/summaries/2026-07-02-12-05-hxprobe-simplification.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# hxprobe simplification — summary
|
||||
|
||||
Plan: `docs/plans/2026-07-02-12-05-hxprobe-simplification.md` (five targeted
|
||||
removals of speculative scaffolding / redundant state / duplicated logic, no
|
||||
behavior change). Trigger: a question about a dead `file=` parameter on
|
||||
`_Parser.print_help`/`print_usage` in `hxprobe/hxprobe/cli.py`, which led to a
|
||||
broader pass over the whole package for the same pattern.
|
||||
|
||||
## What changed
|
||||
|
||||
1. **`hxprobe/hxprobe/cli.py` — deleted the `_Parser` subclass and `_ArgExit`
|
||||
exception** (~32 lines). It re-implemented, by hand, behavior the standard
|
||||
library already provides: `argparse.ArgumentParser` already writes help to
|
||||
`sys.stdout` and usage/errors to `sys.stderr`, and already raises
|
||||
`SystemExit` rather than hard-exiting. `run()` now builds a plain
|
||||
`argparse.ArgumentParser`, wraps the parse + validation calls in
|
||||
`contextlib.redirect_stdout(stdout)` / `redirect_stderr(stderr)`, and
|
||||
catches `SystemExit` at a single site:
|
||||
|
||||
```python
|
||||
except SystemExit as exc:
|
||||
return EXIT_OK if not exc.code else EXIT_USAGE
|
||||
```
|
||||
|
||||
Deviation from the original plan sketch: the plan's snippet used
|
||||
`EXIT_OK if not exc.code else EXIT_USAGE`, matching what was actually
|
||||
implemented (equivalent to, but slightly more defensive than, the
|
||||
`exc.code == 0` check first drafted, since argparse can in principle pass
|
||||
`None`).
|
||||
|
||||
2. **`hxprobe/hxprobe/probe.py` — trimmed `_TimingStream.get_extra_info`** to
|
||||
the single branch actually consumed on the request path (`ssl_object`).
|
||||
Verified by grepping the installed `httpcore`/`httpx` packages: the
|
||||
`server_addr`/`client_addr` branches were only ever queried by `httpx`'s own
|
||||
`_main.py` (the `httpx` CLI command), never by anything hxprobe's request
|
||||
path touches.
|
||||
|
||||
3. **`hxprobe/hxprobe/cli.py` — simplified `_load_urls`**: dropped the
|
||||
`line.split()[0]` "forward-compatible with future `url key=value`
|
||||
annotations" scaffolding; a stripped line is used directly. Docstring
|
||||
updated to describe only what the function does today.
|
||||
|
||||
4. **`hxprobe/hxprobe/probe.py` — removed `_dns_set`/`_connect_set`/
|
||||
`_tls_set`** from `_Trace`. These booleans duplicated `Phase.present` on
|
||||
`self.dns`/`self.connect`/`self.tls` (each starts as `Phase()`, i.e.
|
||||
`present=False`). Guards rewritten from `if not self._dns_set:` to
|
||||
`if not self.dns.present:` (and analogously for connect/tls) — identical
|
||||
first-hop-wins semantics for redirects, one less piece of parallel state.
|
||||
|
||||
5. **`hxprobe/hxprobe/cli.py` — extracted `_summarize_failures`**, a shared
|
||||
helper that dedupes `failed: list[Result]` into `[(phase, message, count),
|
||||
...]` in first-seen order. `_print_failure_summary` (text rendering) and
|
||||
`_build_json_entry` (JSON rendering) previously each carried an identical
|
||||
~12-line dedup loop; both now call the helper and only differ in how they
|
||||
format the tuple.
|
||||
|
||||
## Not changed (considered, kept — per the plan)
|
||||
|
||||
- The custom httpcore backend (`_TimingBackend`/`_TimingStream`/
|
||||
`_TimingTransport`) — this is the tool's actual reason to exist.
|
||||
- `ThreadPoolExecutor` concurrency — backs the shipped `-c/--concurrency` and
|
||||
multi-URL/`-f` features.
|
||||
- Explicit per-phase dataclass fields in `probe.py`/`aggregate.py`.
|
||||
- `CertInfo.sans` — unused in text output but real (emitted in JSON verbose
|
||||
output).
|
||||
|
||||
## Verification
|
||||
|
||||
- `hxprobe/.venv/bin/python -m pytest tests/test_cli.py tests/test_probe.py -q`
|
||||
→ **33 passed**, no test file edits.
|
||||
- `hxprobe/.venv/bin/ruff check hxprobe/` → **all checks passed**.
|
||||
- Manual smoke tests (`python -m hxprobe -h`, no-args, and a live
|
||||
`https://example.com -v` request):
|
||||
- `-h` → help text on stdout, exit `0`.
|
||||
- No args → usage + `no URLs given` error on **stderr only** (confirmed via
|
||||
separate stdout/stderr redirection to files — stdout was empty), exit `1`.
|
||||
- Live verbose request → correct 6-phase timing, resolved IP, HTTP/2
|
||||
protocol, TLS version/cipher, and certificate detail — confirms the
|
||||
`get_extra_info` trim didn't break `ssl_object` retrieval and the
|
||||
`_Trace` refactor didn't break phase/first-hop-wins tracking.
|
||||
|
||||
Net: `cli.py` and `probe.py` are shorter and carry less parallel/duplicated
|
||||
state; no observable behavior changed.
|
||||
Reference in New Issue
Block a user