0.8.4 — OTel partial-install hardening
httpware 0.8.4 — OTel partial-install no longer crashes a live request
Patch release. Defensive fix. No API change. Closes the two paired audit findings tracking the OpenTelemetry partial-install hazard.
The gap
httpware's observability layer treats opentelemetry-api as an optional extra. It detects whether the extra is installed via find_spec("opentelemetry") at module load time, then takes the OTel branch in _emit_event only if the flag is True.
Two flaws in that gate let a partial install crash a live request:
opentelemetryis a PEP 420 native namespace package. Anyopentelemetry-instrumentation-*package creates theopentelemetry/directory, sofind_spec("opentelemetry")returns a non-None spec even whenopentelemetry-apiis absent.- The lazy
from opentelemetry import traceinside_emit_eventwas not wrapped intry/except. With the false-positive flag from (1), the import then raisedImportErrormid-emit, crashing the middleware calling_emit_event—AsyncRetry,Retry,AsyncBulkhead,Bulkhead— in the middle of a live HTTP request.
The audit's chunk-2 finding named both halves of the hole; this release closes both.
The fix
Two changes:
import_checker.is_otel_installednow probes viaimportlib.metadata.distribution("opentelemetry-api")(inside a try/exceptPackageNotFoundErrorblock). This checks the package registry directly: True only when theopentelemetry-apidistribution is actually installed, regardless of whether some other package created theopentelemetry/namespace directory. Note: the obvious alternative —find_spec("opentelemetry.trace")— was rejected because CPython resolves submodule probes by importing the parent namespace package, which would have broken the existing transitive-import isolation guarantee enforced bytests/test_optional_extras_isolation.py. The metadata probe has nosys.modulesside effects._emit_eventwraps the lazyfrom opentelemetry import traceintry/except ImportError. On failure (corrupt install, future namespace surprise, monkey-patchedsys.modules), emission degrades to log-only — the structured log record fires unconditionally; the OTeladd_eventcall is skipped.
We catch ImportError specifically, not bare Exception. Misconfigured-tracer crashes (RuntimeError, AttributeError out of trace.get_current_span().add_event(...)) still surface; only the install-gate-is-wrong case is in scope.
Upgrade
uv add httpware==0.8.4
# or
pip install -U 'httpware==0.8.4'No import changes. No API surface changes. No behavior change on the happy path (api package installed and importable). The only observable change is "no longer crashes" on partial installs.