Repository navigation
v1.1.0
structguru v1.1.0
A stdlib-bridge lifecycle release. install_stdlib_bridge() gains a replace option that releases the previous managed bridge before installing the new one — last call wins — so applications whose logging setup legitimately runs more than once per process (multiple entrypoints, repeated setup in test suites) no longer need a hand-rolled scan-and-uninstall loop. The fail-fast default is unchanged; upgrade in place from any v1.0.x.
Features
-
replace=Trueoninstall_stdlib_bridge(). With an active bridge, the old one is released with fulluninstall_stdlib_bridge()semantics — detached from the root logger, closed, its existing-loggers snapshot restored — and then the new bridge installs. With no active bridge it behaves exactly like a plain install; with the defaultreplace=False, a second install still raisesRuntimeError.bridge = install_stdlib_bridge( level="INFO", suppress_loggers=("urllib3", "botocore"), replace=True, # last call wins instead of raising "already installed" )
-
STRUCTGURU_STDLIB_REPLACEbrings the same capability toinstall_stdlib_bridge_from_env(), parsed with the same strict boolean conventions as the otherSTRUCTGURU_STDLIB_*variables (1/0,true/false,yes/no,on/off; empty means unset; an invalid value raisesValueErrorbefore any logging state is changed). Defaultfalse. -
Contractual swap semantics. The release-and-install sequence runs in one critical section. A record logged by another thread during the swap is delivered at most once — rendered by the outgoing or incoming bridge, raw, or not at all — never twice, and never by raising. The replacing install re-resolves
disable_existing_loggers(explicit value → environment → preserve) and restores the old snapshot before applying the new policy.uninstall_stdlib_bridge()on a replaced handler is a documented no-op, and suppression levels applied by the earlier install are kept. -
The runnable example grew a replace scenario.
examples/stdlib_existing_loggers/main.pynow replaces the bridge from code and from the environment, showing that a logger created between the two installs counts as existing for the replacing install's policy — and it still runs as part of the test suite so it cannot rot.
Docs
- The README and the integrations guide document the replace option, the concurrent-swap delivery guarantees, the cumulative-suppression rule, and the new environment variable in the configuration table.
Test coverage
- 12 new tests pin the replace semantics: swap with exactly-once delivery, the unchanged fail-fast message, no-op and detached-bridge paths, both
disable_existing_loggerstransitions asserting restore-then-apply ordering, environment re-resolution on replace, cumulative suppression, stale-handler uninstall safety,STRUCTGURU_STDLIB_REPLACEset/unset/invalid, and a concurrency smoke test with a livelogger.add()sink — 463 Python tests in total; the Rust core is untouched.