SessionCache is a cleaner and more robust mechanism for caching material attached...
[myslice.git] / unfold / sessioncache.py
1 import uuid
2
3 from manifold.util.singleton import Singleton
4
5 from myslice.settings import logger
6
7 # the key attached to the session object, where we store
8 # the uuid attached to that session in this cache
9 cache_key = 'cached_uuid'
10
11 class _SessionExtension(object):
12     """
13     This object holds all the data we need to attach to a django session object
14     """
15
16     def __init__(self):
17         self.metadata = None
18         self.auth = None
19
20     def __repr__(self):
21         result = "<SessionExtension"
22         if self.metadata: result += " .metadata"
23         if self.auth:     result += " .auth"
24         result += ">"
25         return result
26
27 class SessionCache(dict):
28     """
29     As of django1.7, the session object as attached to a django request
30     gets JSON-serialized instead of pickled
31     This breaks our previous or passing data from request to request across
32     a given session - in particular for metadata and auth/session keys
33     Not that the problem is more with metadata as this is a class instance
34     and JSON cannot handle that
35     So instead we decorate the session object with a UID and retrieve all the rest 
36     from the present - singleton - cache instance
37     """
38
39     __metaclass__ = Singleton
40
41     def get_auth(self, request):
42         """
43         Get the auth previously attached to the request's session, or None
44         """
45         result = self._get(request, 'auth')
46         logger.info('OUT {}'.format(result))
47         return result
48
49     def store_auth(self, request, auth):
50         """
51         Store the auth object attached to this request's session
52         create that extension if needed
53         """
54         logger.info('IN {}'.format(auth))
55         return self._store(request, 'auth', auth)
56
57     def get_metadata(self, request):
58         """
59         retrieve metadata attached to this request's session, or None
60         """
61         return self._get(request, 'metadata')
62
63     def store_metadata(self, request, metadata):
64         """
65         Store the metadata object attached to this request's session
66         create that extension if needed
67         """
68         return self._store(request, 'metadata', metadata)
69
70     def _get(self, request, key):
71         "internal - retrieve key - do not create anything"
72         session = request.session
73         logger.debug("sessioncache._get_{} session={}".format(key, SessionCache._debug_session(session)))
74 #        self._debug(request)
75         if cache_key not in session:
76             return None
77         cached_uuid = session[cache_key]
78         if cached_uuid not in self:
79             return None
80         extension = self[cached_uuid]
81         return getattr(extension, key)
82
83     def _store(self, request, key, value):
84         "internal - set key, attach and create extension if needed"
85         session = request.session
86         if cache_key not in session:
87             session[cache_key] = uuid.uuid1().int
88         cached_uuid = session[cache_key]
89         if cached_uuid not in self:
90             self[cached_uuid] = _SessionExtension()
91         extension = self[cached_uuid]
92         setattr(extension, key, value)
93         logger.debug("sessioncache._store_{} session={}".format(key, SessionCache._debug_session(session)))
94 #        self._debug(request)
95
96     def end_session(self, request):
97         """
98         Clear all data related to this request's session has we are logging out
99         This is for garbage collection
100         """
101         session = request.session
102         logger.debug("SessionCache.end_session() {}".format(self._debug_session(session)))
103         if cache_key not in session:
104             return
105         cached_uuid = session[cache_key]
106         if cached_uuid in self:
107             del self[cached_uuid]
108
109     def _debug(self, request):
110         session = request.session
111         logger.debug("SessionCache: ---------- with session {}".format(self._debug_session(session)))
112         for k,v in self.iteritems():
113             logger.debug("SessionCache {} -> {}".format(k,v))
114         if cache_key not in session:
115             return
116         cached_uuid = session[cache_key]
117         if cached_uuid not in self:
118             return
119         extension = self[cached_uuid]
120         logger.debug("SessionCache: found extension {}".format(extension))
121         logger.debug("SessionCache: ----------")
122         
123     @staticmethod
124     def _debug_session(session):
125         result = ""
126         result += "{} x {}".format(session, session.keys())
127         if cache_key in session:
128             result += " <{} = {}>".format(cache_key, session[cache_key])
129         return result