mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
## What
`install_all()` (the `headroom.proxy_extension` loader) previously let
any exception from an extension's `install()` **propagate and abort
proxy startup** — one broken or version-incompatible third-party
extension took the whole proxy down, and every other extension with it.
This makes extension loading resilient:
- catch a failing `install()`, log it (with traceback), record it as
**skipped**
- continue installing the rest — a failure disables that one extension,
not the proxy
- print a `SKIPPED` line to the console (the startup banner lists
*enabled* extensions before install runs, so a skip would otherwise be
logging-config dependent)
## Why
Found while testing several proxy extensions together in a clean venv: a
plugin built against a newer core API raised `ModuleNotFoundError` from
`install()` and crashed the proxy at startup. An extension that fails
its own environment/auth check should disable itself — it should not
take the whole proxy down.
## Real behavior proof
Before — one extension failing in `install()`:
```
... proxy did NOT come up (/livez never answered)
```
After — same setup, one extension deliberately broken:
```
[headroom] proxy extensions SKIPPED: myorg_ext (install failed — running without them; see logs)
/livez: 200 healthy # proxy up; the other extensions installed
```
Loader unit check (fake failing extension):
```
returned installed: ['good_ext'] # bad one excluded
bad_ext skipped (not in installed): True
good_ext survived: True
warning logged for bad_ext: True
```
## Tests
- `mypy headroom/proxy/extensions.py` → `Success: no issues found`
- `ruff check headroom/proxy/extensions.py` → `All checks passed!`
- Verified in-process (catch/skip/continue + logging) and end-to-end
against a running proxy (`/livez` 200 with a deliberately failing
extension).
## Maintainer Follow-up
- Added `tests/test_proxy_extensions.py` covering skip-and-continue
behavior for a failed extension and the missing-extension warning path.
- Removed an informal implementation comment from
`headroom/proxy/extensions.py`.
- Validation on `fe5176db`: `uv run --frozen --extra dev python -m
pytest tests/test_proxy_extensions.py -q`, `uvx ruff==0.15.17 check
headroom/proxy/extensions.py tests/test_proxy_extensions.py
--output-format concise`, `git diff --check`, and commit hooks all
passed.
---------
Co-authored-by: JerrettDavis <mxjerrett@gmail.com>
46 lines
1.4 KiB
Python
46 lines
1.4 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import Any
|
|
|
|
from headroom.proxy import extensions
|
|
|
|
|
|
def test_install_all_skips_failed_extension_and_continues(
|
|
caplog,
|
|
capsys,
|
|
monkeypatch,
|
|
) -> None:
|
|
calls: list[str] = []
|
|
|
|
def good(app: Any, config: Any) -> None:
|
|
calls.append("good")
|
|
|
|
def bad(app: Any, config: Any) -> None:
|
|
calls.append("bad")
|
|
raise RuntimeError("missing optional dependency")
|
|
|
|
monkeypatch.setattr(
|
|
extensions,
|
|
"discover",
|
|
lambda: iter([("bad_ext", bad), ("good_ext", good)]),
|
|
)
|
|
|
|
with caplog.at_level(logging.WARNING, logger=extensions.log.name):
|
|
installed = extensions.install_all(object(), object(), enabled=["bad_ext", "good_ext"])
|
|
|
|
assert installed == ["good_ext"]
|
|
assert calls == ["bad", "good"]
|
|
assert "bad_ext" in capsys.readouterr().err
|
|
assert "failed to install and was skipped" in caplog.text
|
|
assert "proxy extensions skipped due to install errors: bad_ext" in caplog.text
|
|
|
|
|
|
def test_install_all_warns_for_missing_requested_extension(caplog, monkeypatch) -> None:
|
|
monkeypatch.setattr(extensions, "discover", lambda: iter([]))
|
|
|
|
with caplog.at_level(logging.WARNING, logger=extensions.log.name):
|
|
installed = extensions.install_all(object(), object(), enabled=["missing_ext"])
|
|
|
|
assert installed == []
|
|
assert "proxy extensions requested but not found: missing_ext" in caplog.text
|