Source code for pantra.session_storage
from __future__ import annotations
import typing
import time
from abc import ABC, abstractmethod
from typing import NamedTuple
from weakref import WeakValueDictionary
from pantra.settings import config
if typing.TYPE_CHECKING:
from typing import Any, Mapping
from .session import Session
[docs]
class SessionStorage(ABC):
"""Abstract session storage.
Allows to keep user values between sessions.
"""
__slots__ = ("_binding_dicts", "_binding_keys", "_session")
def __init__(self, session: Session):
self._binding_dicts: WeakValueDictionary[str, dict[str, Any]] = WeakValueDictionary()
self._binding_keys: dict[str, str] = {}
self._session = session
@abstractmethod
def _load(self) -> dict[str, Any]:
"""Load session data from the specific storage"""
@abstractmethod
def _put(self, data: dict[str, Any]):
"""Put session data to the specific storage"""
[docs]
def add_binding(self, name: str, dict_ref: dict[str, Any], key: str):
"""Bind storage record to the specific key of the dictionary
Arguments:
name: The name of the record
dict_ref: The dictionary to bind
key: The key (of `dict_ref`) to bind
"""
if name not in self._binding_dicts:
self._binding_dicts[name] = dict_ref
self._binding_keys[name] = key
if name in self._session.state:
dict_ref[key] = self._session[name]
[docs]
def gather(self):
"""Gather all bound values from dictionaries to set session state"""
for name, binding in list(self._binding_dicts.items()):
self._session[name] = binding[self._binding_keys[name]]
[docs]
def distribute(self):
"""Distribute values from session state to bound dictionaries"""
for name, binging in list(self._binding_dicts.items()):
binging[self._binding_keys[name]] = self._session[name]
[docs]
def reload(self):
"""Reload session state"""
self._session.state.update(self._load())
[docs]
def sync(self):
"""Sync session state"""
self._put({k: v for k, v in self._session.state.items() if k in self._binding_dicts})
[docs]
class NullSessionStorage(SessionStorage):
"""Default session storage behavior - just keep in session state"""
def _load(self) -> dict[str, typing.Any]:
return {}
def _put(self, data: dict[str, typing.Any]):
pass
[docs]
class ShelveSessionStorage(SessionStorage):
"""Use `shelve <https://docs.python.org/3/library/shelve.html>`__ to keep user data"""
def __init__(self, session: Session):
super().__init__(session)
session_path = config.APPS_PATH / session.app / "storage"
if not session_path.exists():
session_path.mkdir()
def _load(self) -> dict[str, typing.Any]:
import shelve
try:
from dbm.gnu import error
exc = error
except ModuleNotFoundError:
exc = Exception
filename = config.APPS_PATH / self._session.app / "storage" / (self._session.user or "common")
while True:
try:
with shelve.open(str(filename)) as db:
return dict(db.items())
break
except exc:
time.sleep(1)
def _put(self, data: dict[str, typing.Any]):
import shelve
try:
from dbm.gnu import error
exc = error
except ModuleNotFoundError:
exc = Exception
filename = config.APPS_PATH / self._session.app / "storage" / (self._session.user or "common")
while True:
try:
with shelve.open(str(filename)) as db:
for name, value in data.items():
db[name] = value
break
except exc:
time.sleep(1)