globals.py 1.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
  1. from contextvars import ContextVar, Token
  2. from typing import Any, Dict
  3. from starlette.types import ASGIApp, Receive, Scope, Send
  4. class Globals:
  5. __slots__ = ("_vars", "_reset_tokens")
  6. _vars: Dict[str, ContextVar]
  7. _reset_tokens: Dict[str, Token]
  8. def __init__(self) -> None:
  9. object.__setattr__(self, '_vars', {})
  10. object.__setattr__(self, '_reset_tokens', {})
  11. def reset(self) -> None:
  12. for _name, var in self._vars.items():
  13. try:
  14. var.reset(self._reset_tokens[_name])
  15. # ValueError will be thrown if the reset() happens in
  16. # a different context compared to the original set().
  17. # Then just set to None for this new context.
  18. except ValueError:
  19. var.set(None)
  20. def _ensure_var(self, item: str) -> None:
  21. if item not in self._vars:
  22. self._vars[item] = ContextVar(f"globals:{item}", default=None)
  23. self._reset_tokens[item] = self._vars[item].set(None)
  24. def __getattr__(self, item: str) -> Any:
  25. self._ensure_var(item)
  26. return self._vars[item].get()
  27. def __setattr__(self, item: str, value: Any) -> None:
  28. self._ensure_var(item)
  29. self._vars[item].set(value)
  30. class GlobalsMiddleware:
  31. def __init__(self, app: ASGIApp) -> None:
  32. self.app = app
  33. async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
  34. g.reset()
  35. await self.app(scope, receive, send)
  36. g = Globals()