"""Request/async tenant context (``TENANCY.md`` §3.2, §7). ``current_tenant`` is a ``ContextVar`` so the scoped managers can resolve the tenant without threading it through every call. It is bound by ``TenantMiddleware`` for requests and must be bound explicitly by Celery tasks and management commands. """ from __future__ import annotations from collections.abc import Iterator from contextlib import contextmanager from contextvars import ContextVar, Token from typing import TYPE_CHECKING, Any if TYPE_CHECKING: from .models import Tenant __all__ = [ "current_tenant", "get_current_tenant", "set_current_tenant", "reset_current_tenant", "tenant_context", ] current_tenant: ContextVar[Tenant | None] = ContextVar("current_tenant", default=None) def get_current_tenant() -> Tenant | None: return current_tenant.get() def set_current_tenant(tenant: Tenant | None) -> Token: return current_tenant.set(tenant) def reset_current_tenant(token: Token) -> None: current_tenant.reset(token) @contextmanager def tenant_context(tenant: Tenant | None) -> Iterator[None]: """Binds ``tenant`` for the duration of the block and always restores it.""" token = set_current_tenant(tenant) try: yield finally: reset_current_tenant(token) def tenant_id_or_none() -> Any: tenant = get_current_tenant() return tenant.pk if tenant is not None else None