nk20/apps/permission/decorators.py

68 lines
2.1 KiB
Python

# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import sys
from functools import lru_cache
from time import time
from django.contrib.sessions.models import Session
from note_kfet.middlewares import get_current_session
def memoize(f):
"""
Memoize results and store in sessions
This decorator is useful for permissions: they are loaded once needed, then stored for next calls.
The storage is contained with sessions since it depends on the selected mask.
"""
sess_funs = {}
last_collect = time()
def collect():
"""
Clear cache of results when sessions are invalid, to flush useless data.
This function is called every minute.
"""
nonlocal sess_funs
new_sess_funs = {}
for sess_key in sess_funs:
if Session.objects.filter(session_key=sess_key).exists():
new_sess_funs[sess_key] = sess_funs[sess_key]
sess_funs = new_sess_funs
def func(*args, **kwargs):
# if settings.DEBUG:
# # Don't memoize in DEBUG mode
# return f(*args, **kwargs)
nonlocal last_collect
if "test" in sys.argv:
# In a test environment, don't memoize permissions
return f(*args, **kwargs)
if time() - last_collect > 60:
# Clear cache
collect()
last_collect = time()
# If there is no session, then we don't memoize anything.
sess = get_current_session()
if sess is None or sess.session_key is None:
return f(*args, **kwargs)
sess_key = sess.session_key
if sess_key not in sess_funs:
# lru_cache makes the job of memoization
# We store only the 512 latest data per session. It has to be enough.
sess_funs[sess_key] = lru_cache(512)(f)
try:
return sess_funs[sess_key](*args, **kwargs)
except TypeError: # For add permissions, objects are not hashable (not yet created). Don't memoize this case.
return f(*args, **kwargs)
func.func_name = f.__name__
return func