Source code for rational.context
"""Per-request context: what server-side code may know about the caller."""
from __future__ import annotations
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from fastapi import Request
from .app import App
from .backend.database import Database
from .session import Session
_UNRESOLVED: Any = object()
[docs]
class Context:
"""Bound to one HTTP request (a page view or an event).
``session`` is created lazily, so pages that never touch it set no cookie.
"""
def __init__(self, app: App | None, request: Request | None = None, session: Session | None = None) -> None:
self.app = app
self.request = request
self._session = session
self.session_created = False
self._user: Any = _UNRESOLVED
@property
def session(self) -> Session:
if self._session is None:
if self.app is None:
from .session import Session
self._session = Session("detached")
else:
self._session = self.app.sessions.create()
self.session_created = True
return self._session
def peek_session(self) -> Session | None:
return self._session
@property
def user(self) -> Any:
"""The authenticated principal, or ``None``."""
if self._user is not _UNRESOLVED:
return self._user
session = self._session
return None if session is None else session.user
[docs]
def set_user(self, user: Any) -> None:
"""Used by custom authenticators (``App.authenticator``)."""
self._user = user
@property
def db(self) -> Database:
if self.app is None or self.app.db is None:
raise RuntimeError("no database configured; pass App(database=...)")
return self.app.db
def require_user(self) -> Any:
from .errors import Unauthorized
user = self.user
if user is None:
raise Unauthorized()
return user