-
Notifications
You must be signed in to change notification settings - Fork 0
/
MyRamSession.py
141 lines (97 loc) · 3.49 KB
/
MyRamSession.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#!/usr/bin/env python
# -*- mode: python; indent-tabs-mode: nil; -*- coding: utf-8 -*-
"""
MyRamSession.py
Copyright 2014 by Marcello Perathoner
Distributable under the GNU General Public License Version 3 or newer.
A quick Python3 fix for the cherrypy RamSession. May be removed when
RamSession is fixed upstream.
Usage:
import MyRamSession
cherrypy.lib.sessions.RamSession = MyRamSession.FixedRamSession
cherrypy.lib.sessions.MyramSession = MyRamSession.MyRamSession
"""
import threading
import cherrypy
import cherrypy.lib.sessions
class Struct (object):
""" Data store. """
def __init__ (self):
self.expires = None
self.data = None
self.cache_lock = threading.Lock ()
class MyRamSession (cherrypy.lib.sessions.Session):
""" A cherrypy session kept in ram. """
cache = {}
# all inserts/deletes in cache must be guarded by this lock
# or we will get 'RuntimeError: dictionary changed size during iteration'
# because you cannot atomically iterate a dict in Python3
cache_lock = threading.Lock ()
def __init__ (self, id_ = None, **kwargs):
super (MyRamSession, self).__init__ (id_, **kwargs)
def clean_up (self):
"""Clean up expired sessions."""
now = self.now ()
def expired (x):
return x[1].expires <= now
with self.cache_lock:
for id_, s in list (filter (expired, self.cache.items ())):
self.cache.pop (id_, None)
def _exists (self):
return self.id in self.cache
def _load (self):
try:
s = self.cache[self.id]
return s.data, s.expires
except KeyError:
return None
def _save (self, expires):
s = self.cache.get (self.id, Struct ())
s.expires = expires
s.data = self._data
with self.cache_lock:
self.cache[self.id] = s
def _delete (self):
with self.cache_lock:
self.cache.pop (self.id, None)
def acquire_lock (self):
"""Acquire an exclusive lock on the currently-loaded session data."""
try:
self.cache[self.id].lock.acquire ()
self.locked = True
except KeyError:
pass
def release_lock (self):
"""Release the lock on the currently-loaded session data."""
try:
self.cache[self.id].lock.release ()
self.locked = False
except KeyError:
pass
def __len__ (self):
"""Return the number of active sessions."""
return len (self.cache)
from cherrypy._cpcompat import copyitems
class FixedRamSession (cherrypy.lib.sessions.RamSession):
def clean_up(self):
"""Clean up expired sessions."""
now = self.now()
try:
for id, (data, expiration_time) in copyitems(self.cache):
if expiration_time <= now:
try:
del self.cache[id]
except KeyError:
pass
try:
del self.locks[id]
except KeyError:
pass
# added to remove obsolete lock objects
for id in list(self.locks):
if id not in self.cache:
self.locks.pop(id, None)
except RuntimeError:
# RuntimeError: dictionary changed size during iteration
# Do nothig. Keep cleanup thread running and maybe next time lucky.
pass