1 from __future__ import with_statement
2 import threading, time, pickle, random, os
5 __all__ = ["db", "get"]
10 ret += "%02X" % (ord(byte),)
15 for i in xrange(length):
16 nonce += chr(random.randint(0, 255))
19 class session(object):
20 def __init__(self, lock, expire = 86400 * 7):
21 self.id = hexencode(gennonce(16))
24 self.ctime = self.atime = self.mtime = int(time.time())
40 def __getitem__(self, key):
43 def get(self, key, default = None):
44 return self.dict.get(key, default)
46 def __setitem__(self, key, value):
47 self.dict[key] = value
48 if hasattr(value, "sessdirty"):
53 def __delitem__(self, key):
54 old = self.dict.pop(key)
59 def __contains__(self, key):
60 return key in self.dict
62 def __getstate__(self):
64 for k, v in self.__dict__.items():
65 if k == "lock": continue
69 def __setstate__(self, st):
72 # The proper lock is set by the thawer
75 return "<session %s>" % self.id
78 def __init__(self, backdb = None, cookiename = "wrwsess", path = "/"):
80 self.cookiename = cookiename
82 self.lock = threading.Lock()
84 self.freezetime = 3600
88 now = int(time.time())
90 clist = self.live.keys()
94 entry = self.live[sessid]
99 if entry[1] == "retired":
101 elif entry[1] is None:
105 if sess.atime + self.freezetime < now:
110 if sess.atime + sess.expire < now:
117 del self.live[sessid]
124 if len(self.live) == 0:
130 def _fetch(self, sessid):
132 now = int(time.time())
134 if sessid in self.live:
135 entry = self.live[sessid]
137 entry = self.live[sessid] = [threading.RLock(), None]
139 if isinstance(entry[1], session):
142 elif entry[1] == "retired":
144 elif entry[1] is None:
146 thawed = self.thaw(sessid)
147 if thawed.atime + thawed.expire < now:
149 thawed.lock = entry[0]
157 del self.live[sessid]
159 raise Exception("Illegal session entry: " + repr(entry[1]))
161 def checkclean(self):
163 if self.cthread is None:
164 self.cthread = threading.Thread(target = self.cleanloop)
165 self.cthread.setDaemon(True)
168 def mksession(self, req):
169 return session(threading.RLock())
171 def mkcookie(self, req, sess):
172 cookie.add(req, self.cookiename, sess.id,
174 expires=cookie.cdate(time.time() + sess.expire))
176 def fetch(self, req):
177 now = int(time.time())
178 sessid = cookie.get(req, self.cookiename)
183 sess = self._fetch(sessid)
185 sess = self.mksession(req)
191 self.mkcookie(req, sess)
193 self.live[sess.id] = [sess.lock, sess]
199 req.oncommit(ckfreeze)
202 def thaw(self, sessid):
203 if self.backdb is None:
205 data = self.backdb[sessid]
207 return pickle.loads(data)
211 def freeze(self, sess):
212 if self.backdb is None:
215 data = pickle.dumps(sess, -1)
216 self.backdb[sess.id] = data
220 return req.item(self.fetch)
222 class dirback(object):
223 def __init__(self, path):
226 def __getitem__(self, key):
228 with open(os.path.join(self.path, key)) as inf:
233 def __setitem__(self, key, value):
234 if not os.path.exists(self.path):
235 os.makedirs(self.path)
236 with open(os.path.join(self.path, key), "w") as out:
239 default = env.var(db(backdb = dirback(os.path.join("/tmp", "wrwsess-" + str(os.getuid())))))
242 return default.val.get(req)