diff --git a/CherryPyApp.py b/CherryPyApp.py index c294f16..e767c3e 100644 --- a/CherryPyApp.py +++ b/CherryPyApp.py @@ -51,12 +51,6 @@ import Formatters import RateLimiter 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 diff --git a/MyRamSession.py b/MyRamSession.py deleted file mode 100644 index eeee719..0000000 --- a/MyRamSession.py +++ /dev/null @@ -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 diff --git a/PostgresSession.py b/PostgresSession.py deleted file mode 100644 index 9594a36..0000000 --- a/PostgresSession.py +++ /dev/null @@ -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 ( - 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 () - )