Configuration¶
Process-wide setup and optional FastAPI/Starlette wiring via a single entry point.
setup¶
def setup(
app: object | None = None,
*,
settings: PjxSettings | None = None,
context_factory: Callable[[Any], object | None] | None = None,
components_root: Path | str | None = ..., # unset sentinel
static_root: Path | str | None = ..., # unset sentinel
**kwargs: Any,
) -> PjxSettings
reactive_dev, inject_htmx, and any other PjxSettings field are passed through **kwargs, not declared as their own keywords — an unrecognized keyword raises TypeError. components_root and static_root default to an unset sentinel rather than None, so a caller can pass every field through unconditionally without needing to say anything about the ones it was never given; explicitly passing None is a real value and does override.
components_root triggers component discovery (build_registry()) over that directory; it works with or without an app. static_root mounts a StaticFiles app at /static (name "static") and therefore requires an app — passing it with app=None raises TypeError. When omitted, both are no-ops, and the static mount is covered by the idempotency guard, so a second setup() won't double-mount.
Single call for typical web apps:
from fastapi import FastAPI
from pyjinhx import setup
app = FastAPI()
setup(app, context_factory=lambda req: AppLoadContext(db=get_db(req)))
app |
Behavior |
|---|---|
| FastAPI/Starlette app | Chain lifespan (preserve existing) + register registry middleware |
None |
Process config only (configure_pyjinhx) — tests, scripts |
Idempotent: a second setup(app, ...) on the same app is a no-op.
Lifespan chaining¶
When app is provided, pyjinhx wraps app.router.lifespan_context:
configure_pyjinhx(settings)— publish settings for the process, apply reactive dev- Your existing lifespan startup (if any)
- Serve traffic
- Your existing lifespan shutdown
shutdown_pyjinhx()
Does not compose deprecated @app.on_event("startup") handlers — use the lifespan API.
PjxSettings¶
@dataclass(frozen=True)
class PjxSettings:
reactive_dev: bool = False
inject_htmx: bool = True
components_root: Path | str | None = None
static_root: Path | str | None = None
reactive_dev— enables reactive dev guardrails when true.inject_htmx— stored only; how it maps onto the session's asset modes is pending design.components_root— directory walked for component discovery;Noneis a no-op.static_root— directory mounted as static assets whensetup(app, ...)is given an app;Noneis a no-op.
from_env¶
| Variable | Default | Effect |
|---|---|---|
PJX_REACTIVE_DEV |
off | Sets reactive_dev; accepts 1/true/yes/on (and 0/false/no/off) |
PJX_INJECT_HTMX |
on | Sets inject_htmx; same boolean parsing as above |
PJX_COMPONENTS_ROOT |
unset | Sets components_root from a filesystem path |
PJX_STATIC_ROOT |
unset | Sets static_root from a filesystem path |
configure_pyjinhx / shutdown_pyjinhx¶
Lower-level process hooks used by setup() and tests:
configure_pyjinhx publishes settings as the process's current configuration (readable via current_settings()) and applies reactive_dev by toggling pyjinhx.dev, if that module is importable. shutdown_pyjinhx resets the process back to default PjxSettings() and disables reactive dev the same way.