remove custom caches
My RamSession and PostgresSession were needed because the CherryPy version we were using was not compatible with Python 3. Current versions are compatible, so these are not needed.onedrive
parent
64717ce0c2
commit
22e0522a37
|
@ -51,12 +51,6 @@ import Formatters
|
||||||
import RateLimiter
|
import RateLimiter
|
||||||
|
|
||||||
import Timer
|
import Timer
|
||||||
import MyRamSession
|
|
||||||
import PostgresSession
|
|
||||||
|
|
||||||
cherrypy.lib.sessions.RamSession = MyRamSession.FixedRamSession
|
|
||||||
cherrypy.lib.sessions.MyramSession = MyRamSession.MyRamSession
|
|
||||||
cherrypy.lib.sessions.PostgresSession = PostgresSession.PostgresSession
|
|
||||||
|
|
||||||
plugins.Timer = Timer.TimerPlugin
|
plugins.Timer = Timer.TimerPlugin
|
||||||
|
|
||||||
|
|
141
MyRamSession.py
141
MyRamSession.py
|
@ -1,141 +0,0 @@
|
||||||
#!/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
|
|
|
@ -1,151 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- mode: python; indent-tabs-mode: nil; -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
"""
|
|
||||||
PostgresSession.py
|
|
||||||
|
|
||||||
Copyright 2014 by Marcello Perathoner
|
|
||||||
|
|
||||||
Distributable under the GNU General Public License Version 3 or newer.
|
|
||||||
|
|
||||||
A rewrite of the cherrypy PostgresqlSession.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
import PostgresSession
|
|
||||||
cherrypy.lib.sessions.PostgresSession = PostgresSession.PostgresSession
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
import logging
|
|
||||||
import pickle
|
|
||||||
|
|
||||||
import cherrypy
|
|
||||||
import cherrypy.lib.sessions
|
|
||||||
|
|
||||||
|
|
||||||
class PostgresSession (cherrypy.lib.sessions.Session):
|
|
||||||
""" Implementation of the PostgreSQL backend for sessions. It assumes
|
|
||||||
a table like this::
|
|
||||||
|
|
||||||
create table <table_name> (
|
|
||||||
id varchar (40) primary key,
|
|
||||||
expires timestamp,
|
|
||||||
data bytea
|
|
||||||
)
|
|
||||||
|
|
||||||
You must provide your own `get_dbapi20_connection ()` function.
|
|
||||||
"""
|
|
||||||
|
|
||||||
pickle_protocol = pickle.HIGHEST_PROTOCOL
|
|
||||||
select = 'select expires, data from table_name where id=%(id)s for update'
|
|
||||||
|
|
||||||
|
|
||||||
def __init__ (self, id_ = None, **kwargs):
|
|
||||||
self.table_name = kwargs.get ('table', 'session')
|
|
||||||
# Session.__init__ () may need working connection
|
|
||||||
self.connection = self.get_dbapi20_connection ()
|
|
||||||
|
|
||||||
super (PostgresSession, self).__init__ (id_, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_dbapi20_connection ():
|
|
||||||
""" Return a dbapi 2.0 compatible connection. """
|
|
||||||
return cherrypy.engine.pool.connect ()
|
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setup (cls, **kwargs):
|
|
||||||
"""Set up the storage system for Postgres-based sessions.
|
|
||||||
|
|
||||||
This should only be called once per process; this will be done
|
|
||||||
automatically when using sessions.init (as the built-in Tool does).
|
|
||||||
"""
|
|
||||||
|
|
||||||
cherrypy.log ("Using PostgresSession",
|
|
||||||
context = 'SESSION', severity = logging.INFO)
|
|
||||||
|
|
||||||
for k, v in kwargs.items ():
|
|
||||||
setattr (cls, k, v)
|
|
||||||
|
|
||||||
|
|
||||||
def now (self):
|
|
||||||
"""Generate the session specific concept of 'now'.
|
|
||||||
|
|
||||||
Other session providers can override this to use alternative,
|
|
||||||
possibly timezone aware, versions of 'now'.
|
|
||||||
"""
|
|
||||||
return datetime.datetime.utcnow ()
|
|
||||||
|
|
||||||
|
|
||||||
def _exec (self, sql, **kwargs):
|
|
||||||
""" Internal helper to execute sql statements. """
|
|
||||||
|
|
||||||
kwargs['id'] = self.id
|
|
||||||
cursor = self.connection.cursor ()
|
|
||||||
cursor.execute (sql.replace ('table_name', self.table_name), kwargs)
|
|
||||||
return cursor
|
|
||||||
|
|
||||||
|
|
||||||
def _exists (self):
|
|
||||||
""" Return true if session data exists. """
|
|
||||||
cursor = self._exec (self.select)
|
|
||||||
return bool (cursor.fetchall ())
|
|
||||||
|
|
||||||
|
|
||||||
def _load (self):
|
|
||||||
""" Load the session data. """
|
|
||||||
|
|
||||||
cursor = self._exec (self.select)
|
|
||||||
rows = cursor.fetchall ()
|
|
||||||
if not rows:
|
|
||||||
return None
|
|
||||||
|
|
||||||
expires, pickled_data = rows[0]
|
|
||||||
data = pickle.loads (pickled_data)
|
|
||||||
return data, expires
|
|
||||||
|
|
||||||
|
|
||||||
def _save (self, expires):
|
|
||||||
""" Save the session data. """
|
|
||||||
|
|
||||||
pickled_data = pickle.dumps (self._data, self.pickle_protocol)
|
|
||||||
|
|
||||||
self._delete ()
|
|
||||||
self._exec (
|
|
||||||
"""\
|
|
||||||
insert into table_name (id, expires, data)
|
|
||||||
values (%(id)s, %(expires)s, %(data)s)
|
|
||||||
""",
|
|
||||||
data = pickled_data,
|
|
||||||
expires = expires
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _delete (self):
|
|
||||||
""" Delete the session data. """
|
|
||||||
self._exec ('delete from table_name where id=%(id)s')
|
|
||||||
|
|
||||||
|
|
||||||
def acquire_lock (self):
|
|
||||||
"""Acquire an exclusive lock on the currently-loaded session data."""
|
|
||||||
|
|
||||||
self._exec (self.select)
|
|
||||||
self.locked = True
|
|
||||||
|
|
||||||
|
|
||||||
def release_lock (self):
|
|
||||||
"""Release the lock on the currently-loaded session data."""
|
|
||||||
|
|
||||||
self.connection.commit ()
|
|
||||||
self.locked = False
|
|
||||||
|
|
||||||
|
|
||||||
def clean_up (self):
|
|
||||||
"""Clean up expired sessions."""
|
|
||||||
|
|
||||||
self._exec (
|
|
||||||
'delete from table_name where expires < %(now)s',
|
|
||||||
now = self.now ()
|
|
||||||
)
|
|
Loading…
Reference in New Issue