summaryrefslogtreecommitdiffstats
path: root/module/lib/beaker
diff options
context:
space:
mode:
Diffstat (limited to 'module/lib/beaker')
-rw-r--r--module/lib/beaker/cache.py160
-rw-r--r--module/lib/beaker/container.py100
-rw-r--r--module/lib/beaker/crypto/pbkdf2.py42
-rw-r--r--module/lib/beaker/crypto/pycrypto.py6
-rw-r--r--module/lib/beaker/crypto/util.py4
-rw-r--r--module/lib/beaker/ext/database.py20
-rw-r--r--module/lib/beaker/ext/google.py22
-rw-r--r--module/lib/beaker/ext/memcached.py16
-rw-r--r--module/lib/beaker/middleware.py46
-rw-r--r--module/lib/beaker/session.py168
-rw-r--r--module/lib/beaker/synchronization.py54
-rw-r--r--module/lib/beaker/util.py41
12 files changed, 343 insertions, 336 deletions
diff --git a/module/lib/beaker/cache.py b/module/lib/beaker/cache.py
index f59c1e44d..4a96537ff 100644
--- a/module/lib/beaker/cache.py
+++ b/module/lib/beaker/cache.py
@@ -7,7 +7,7 @@ specifying an alternate type when used.
Advanced users can add new backends in beaker.backends
"""
-
+
import warnings
import beaker.container as container
@@ -63,53 +63,54 @@ try:
2)
except ImportError:
pass
-
+
def cache_region(region, *deco_args):
"""Decorate a function to cache itself using a cache region
-
+
The region decorator requires arguments if there are more than
2 of the same named function, in the same module. This is
because the namespace used for the functions cache is based on
the functions name and the module.
-
+
+
Example::
-
+
# Add cache region settings to beaker:
beaker.cache.cache_regions.update(dict_of_config_region_options))
-
+
@cache_region('short_term', 'some_data')
def populate_things(search_term, limit, offset):
return load_the_data(search_term, limit, offset)
-
+
return load('rabbits', 20, 0)
-
+
.. note::
-
+
The function being decorated must only be called with
positional arguments.
-
+
"""
cache = [None]
-
+
def decorate(func):
namespace = util.func_namespace(func)
def cached(*args):
reg = cache_regions[region]
if not reg.get('enabled', True):
return func(*args)
-
+
if not cache[0]:
if region not in cache_regions:
raise BeakerException('Cache region not configured: %s' % region)
cache[0] = Cache._get_cache(namespace, reg)
-
+
cache_key = " ".join(map(str, deco_args + args))
def go():
return func(*args)
-
+
return cache[0].get_value(cache_key, createfunc=go)
cached._arg_namespace = namespace
cached._arg_region = region
@@ -119,37 +120,37 @@ def cache_region(region, *deco_args):
def region_invalidate(namespace, region, *args):
"""Invalidate a cache region namespace or decorated function
-
+
This function only invalidates cache spaces created with the
cache_region decorator.
-
+
:param namespace: Either the namespace of the result to invalidate, or the
cached function reference
-
+
:param region: The region the function was cached to. If the function was
cached to a single region then this argument can be None
-
+
:param args: Arguments that were used to differentiate the cached
function as well as the arguments passed to the decorated
function
Example::
-
+
# Add cache region settings to beaker:
beaker.cache.cache_regions.update(dict_of_config_region_options))
-
+
def populate_things(invalidate=False):
-
+
@cache_region('short_term', 'some_data')
def load(search_term, limit, offset):
return load_the_data(search_term, limit, offset)
-
+
# If the results should be invalidated first
if invalidate:
region_invalidate(load, None, 'some_data',
'rabbits', 20, 0)
return load('rabbits', 20, 0)
-
+
"""
if callable(namespace):
if not region:
@@ -161,7 +162,7 @@ def region_invalidate(namespace, region, *args):
"namespace is required")
else:
region = cache_regions[region]
-
+
cache = Cache._get_cache(namespace, region)
cache_key = " ".join(str(x) for x in args)
cache.remove_value(cache_key)
@@ -179,7 +180,7 @@ class Cache(object):
:param expiretime: seconds to keep cached data (legacy support)
:param starttime: time when cache was cache was
-
+
"""
def __init__(self, namespace, type='memory', expiretime=None,
starttime=None, expire=None, **nsargs):
@@ -189,12 +190,12 @@ class Cache(object):
raise cls
except KeyError:
raise TypeError("Unknown cache implementation %r" % type)
-
+
self.namespace = cls(namespace, **nsargs)
self.expiretime = expiretime or expire
self.starttime = starttime
self.nsargs = nsargs
-
+
@classmethod
def _get_cache(cls, namespace, kw):
key = namespace + str(kw)
@@ -203,16 +204,16 @@ class Cache(object):
except KeyError:
cache_managers[key] = cache = cls(namespace, **kw)
return cache
-
+
def put(self, key, value, **kw):
self._get_value(key, **kw).set_value(value)
set_value = put
-
+
def get(self, key, **kw):
"""Retrieve a cached value from the container"""
return self._get_value(key, **kw).get_value()
get_value = get
-
+
def remove_value(self, key, **kw):
mycontainer = self._get_value(key, **kw)
if mycontainer.has_current_value():
@@ -228,9 +229,9 @@ class Cache(object):
kw.setdefault('expiretime', self.expiretime)
kw.setdefault('starttime', self.starttime)
-
+
return container.Value(key, self.namespace, **kw)
-
+
@util.deprecated("Specifying a "
"'type' and other namespace configuration with cache.get()/put()/etc. "
"is deprecated. Specify 'type' and other namespace configuration to "
@@ -244,24 +245,24 @@ class Cache(object):
c = Cache(self.namespace.namespace, type=type, **kwargs)
return c._get_value(key, expiretime=expiretime, createfunc=createfunc,
starttime=starttime)
-
+
def clear(self):
"""Clear all the values from the namespace"""
self.namespace.remove()
-
+
# dict interface
def __getitem__(self, key):
return self.get(key)
-
+
def __contains__(self, key):
return self._get_value(key).has_current_value()
-
+
def has_key(self, key):
return key in self
-
+
def __delitem__(self, key):
self.remove_value(key)
-
+
def __setitem__(self, key, value):
self.put(key, value)
@@ -269,91 +270,94 @@ class Cache(object):
class CacheManager(object):
def __init__(self, **kwargs):
"""Initialize a CacheManager object with a set of options
-
+
Options should be parsed with the
:func:`~beaker.util.parse_cache_config_options` function to
ensure only valid options are used.
-
+
"""
self.kwargs = kwargs
self.regions = kwargs.pop('cache_regions', {})
-
+
# Add these regions to the module global
cache_regions.update(self.regions)
-
+
def get_cache(self, name, **kwargs):
kw = self.kwargs.copy()
kw.update(kwargs)
return Cache._get_cache(name, kw)
-
+
def get_cache_region(self, name, region):
if region not in self.regions:
raise BeakerException('Cache region not configured: %s' % region)
kw = self.regions[region]
return Cache._get_cache(name, kw)
-
+
def region(self, region, *args):
"""Decorate a function to cache itself using a cache region
-
+
The region decorator requires arguments if there are more than
2 of the same named function, in the same module. This is
because the namespace used for the functions cache is based on
the functions name and the module.
-
+
+
Example::
-
+
# Assuming a cache object is available like:
cache = CacheManager(dict_of_config_options)
-
+
+
def populate_things():
-
+
@cache.region('short_term', 'some_data')
def load(search_term, limit, offset):
return load_the_data(search_term, limit, offset)
-
+
return load('rabbits', 20, 0)
-
+
.. note::
-
+
The function being decorated must only be called with
positional arguments.
-
+
"""
return cache_region(region, *args)
def region_invalidate(self, namespace, region, *args):
"""Invalidate a cache region namespace or decorated function
-
+
This function only invalidates cache spaces created with the
cache_region decorator.
-
+
:param namespace: Either the namespace of the result to invalidate, or the
name of the cached function
-
+
:param region: The region the function was cached to. If the function was
cached to a single region then this argument can be None
-
+
:param args: Arguments that were used to differentiate the cached
function as well as the arguments passed to the decorated
function
Example::
-
+
# Assuming a cache object is available like:
cache = CacheManager(dict_of_config_options)
-
+
def populate_things(invalidate=False):
-
+
@cache.region('short_term', 'some_data')
def load(search_term, limit, offset):
return load_the_data(search_term, limit, offset)
-
+
# If the results should be invalidated first
if invalidate:
cache.region_invalidate(load, None, 'some_data',
'rabbits', 20, 0)
return load('rabbits', 20, 0)
-
+
+
"""
return region_invalidate(namespace, region, *args)
if callable(namespace):
@@ -366,7 +370,7 @@ class CacheManager(object):
"namespace is required")
else:
region = self.regions[region]
-
+
cache = self.get_cache(namespace, **region)
cache_key = " ".join(str(x) for x in args)
cache.remove_value(cache_key)
@@ -383,24 +387,25 @@ class CacheManager(object):
# Assuming a cache object is available like:
cache = CacheManager(dict_of_config_options)
-
+
+
def populate_things():
-
+
@cache.cache('mycache', expire=15)
def load(search_term, limit, offset):
return load_the_data(search_term, limit, offset)
-
+
return load('rabbits', 20, 0)
-
+
.. note::
-
+
The function being decorated must only be called with
positional arguments.
"""
cache = [None]
key = " ".join(str(x) for x in args)
-
+
def decorate(func):
namespace = util.func_namespace(func)
def cached(*args):
@@ -416,12 +421,12 @@ class CacheManager(object):
def invalidate(self, func, *args, **kwargs):
"""Invalidate a cache decorated function
-
+
This function only invalidates cache spaces created with the
cache decorator.
-
+
:param func: Decorated function to invalidate
-
+
:param args: Used to make the key unique for this function, as in region()
above.
@@ -430,21 +435,22 @@ class CacheManager(object):
function
Example::
-
+
# Assuming a cache object is available like:
cache = CacheManager(dict_of_config_options)
-
+
+
def populate_things(invalidate=False):
-
+
@cache.cache('mycache', type="file", expire=15)
def load(search_term, limit, offset):
return load_the_data(search_term, limit, offset)
-
+
# If the results should be invalidated first
if invalidate:
cache.invalidate(load, 'mycache', 'rabbits', 20, 0, type="file")
return load('rabbits', 20, 0)
-
+
"""
namespace = func._arg_namespace
diff --git a/module/lib/beaker/container.py b/module/lib/beaker/container.py
index e7a938c6e..515e97af6 100644
--- a/module/lib/beaker/container.py
+++ b/module/lib/beaker/container.py
@@ -28,34 +28,34 @@ else:
class NamespaceManager(object):
"""Handles dictionary operations and locking for a namespace of
values.
-
+
The implementation for setting and retrieving the namespace data is
handled by subclasses.
-
+
NamespaceManager may be used alone, or may be privately accessed by
one or more Container objects. Container objects provide per-key
services like expiration times and automatic recreation of values.
-
+
Multiple NamespaceManagers created with a particular name will all
share access to the same underlying datasource and will attempt to
synchronize against a common mutex object. The scope of this
sharing may be within a single process or across multiple
processes, depending on the type of NamespaceManager used.
-
+
The NamespaceManager itself is generally threadsafe, except in the
case of the DBMNamespaceManager in conjunction with the gdbm dbm
implementation.
"""
-
+
@classmethod
def _init_dependencies(cls):
pass
-
+
def __init__(self, namespace):
self._init_dependencies()
self.namespace = namespace
-
+
def get_creation_lock(self, key):
raise NotImplementedError()
@@ -79,37 +79,37 @@ class NamespaceManager(object):
def __getitem__(self, key):
raise NotImplementedError()
-
+
def __setitem__(self, key, value):
raise NotImplementedError()
-
+
def set_value(self, key, value, expiretime=None):
"""Optional set_value() method called by Value.
-
+
Allows an expiretime to be passed, for namespace
implementations which can prune their collections
using expiretime.
-
+
"""
self[key] = value
-
+
def __contains__(self, key):
raise NotImplementedError()
def __delitem__(self, key):
raise NotImplementedError()
-
+
def keys(self):
raise NotImplementedError()
-
+
def remove(self):
self.do_remove()
-
+
class OpenResourceNamespaceManager(NamespaceManager):
"""A NamespaceManager where read/write operations require opening/
closing of a resource which is possibly mutexed.
-
+
"""
def __init__(self, namespace):
NamespaceManager.__init__(self, namespace)
@@ -133,13 +133,13 @@ class OpenResourceNamespaceManager(NamespaceManager):
except:
self.access_lock.release_read_lock()
raise
-
+
def release_read_lock(self):
try:
self.close(checkcount = True)
finally:
self.access_lock.release_read_lock()
-
+
def acquire_write_lock(self, wait=True):
r = self.access_lock.acquire_write_lock(wait)
try:
@@ -149,7 +149,7 @@ class OpenResourceNamespaceManager(NamespaceManager):
except:
self.access_lock.release_write_lock()
raise
-
+
def release_write_lock(self):
try:
self.close(checkcount=True)
@@ -258,7 +258,7 @@ class Value(object):
except KeyError:
# guard against un-mutexed backends raising KeyError
has_value = False
-
+
if not self.createfunc:
raise KeyError(self.key)
finally:
@@ -349,29 +349,29 @@ class Value(object):
class AbstractDictionaryNSManager(NamespaceManager):
"""A subclassable NamespaceManager that places data in a dictionary.
-
+
Subclasses should provide a "dictionary" attribute or descriptor
which returns a dict-like object. The dictionary will store keys
that are local to the "namespace" attribute of this manager, so
ensure that the dictionary will not be used by any other namespace.
e.g.::
-
+
import collections
cached_data = collections.defaultdict(dict)
-
+
class MyDictionaryManager(AbstractDictionaryNSManager):
def __init__(self, namespace):
AbstractDictionaryNSManager.__init__(self, namespace)
self.dictionary = cached_data[self.namespace]
-
+
The above stores data in a global dictionary called "cached_data",
which is structured as a dictionary of dictionaries, keyed
first on namespace name to a sub-dictionary, then on actual
cache key to value.
-
+
"""
-
+
def get_creation_lock(self, key):
return NameLock(
identifier="memorynamespace/funclock/%s/%s" % (self.namespace, key),
@@ -386,19 +386,19 @@ class AbstractDictionaryNSManager(NamespaceManager):
def has_key(self, key):
return self.dictionary.__contains__(key)
-
+
def __setitem__(self, key, value):
self.dictionary[key] = value
-
+
def __delitem__(self, key):
del self.dictionary[key]
def do_remove(self):
self.dictionary.clear()
-
+
def keys(self):
return self.dictionary.keys()
-
+
class MemoryNamespaceManager(AbstractDictionaryNSManager):
namespaces = util.SyncDict()
@@ -411,7 +411,7 @@ class DBMNamespaceManager(OpenResourceNamespaceManager):
def __init__(self, namespace, dbmmodule=None, data_dir=None,
dbm_dir=None, lock_dir=None, digest_filenames=True, **kwargs):
self.digest_filenames = digest_filenames
-
+
if not dbm_dir and not data_dir:
raise MissingCacheParameter("data_dir or dbm_dir is required")
elif dbm_dir:
@@ -419,7 +419,7 @@ class DBMNamespaceManager(OpenResourceNamespaceManager):
else:
self.dbm_dir = data_dir + "/container_dbm"
util.verify_directory(self.dbm_dir)
-
+
if not lock_dir and not data_dir:
raise MissingCacheParameter("data_dir or lock_dir is required")
elif lock_dir:
@@ -437,14 +437,14 @@ class DBMNamespaceManager(OpenResourceNamespaceManager):
identifiers=[self.namespace],
extension='.dbm',
digest_filenames=self.digest_filenames)
-
+
debug("data file %s", self.file)
self._checkfile()
def get_access_lock(self):
return file_synchronizer(identifier=self.namespace,
lock_dir=self.lock_dir)
-
+
def get_creation_lock(self, key):
return file_synchronizer(
identifier = "dbmcontainer/funclock/%s" % self.namespace,
@@ -458,19 +458,19 @@ class DBMNamespaceManager(OpenResourceNamespaceManager):
for ext in ('db', 'dat', 'pag', 'dir'):
if os.access(file + os.extsep + ext, os.F_OK):
return True
-
+
return False
-
+
def _checkfile(self):
if not self.file_exists(self.file):
g = self.dbmmodule.open(self.file, 'c')
g.close()
-
+
def get_filenames(self):
list = []
if os.access(self.file, os.F_OK):
list.append(self.file)
-
+
for ext in ('pag', 'dir', 'db', 'dat'):
if os.access(self.file + os.extsep + ext, os.F_OK):
list.append(self.file + os.extsep + ext)
@@ -488,17 +488,17 @@ class DBMNamespaceManager(OpenResourceNamespaceManager):
if self.dbm is not None:
debug("closing dbm file %s", self.file)
self.dbm.close()
-
+
def do_remove(self):
for f in self.get_filenames():
os.remove(f)
-
+
def __getitem__(self, key):
return cPickle.loads(self.dbm[key])
def __contains__(self, key):
return self.dbm.has_key(key)
-
+
def __setitem__(self, key, value):
self.dbm[key] = cPickle.dumps(value)
@@ -513,7 +513,7 @@ class FileNamespaceManager(OpenResourceNamespaceManager):
def __init__(self, namespace, data_dir=None, file_dir=None, lock_dir=None,
digest_filenames=True, **kwargs):
self.digest_filenames = digest_filenames
-
+
if not file_dir and not data_dir:
raise MissingCacheParameter("data_dir or file_dir is required")
elif file_dir:
@@ -536,19 +536,19 @@ class FileNamespaceManager(OpenResourceNamespaceManager):
extension='.cache',
digest_filenames=self.digest_filenames)
self.hash = {}
-
+
debug("data file %s", self.file)
def get_access_lock(self):
return file_synchronizer(identifier=self.namespace,
lock_dir=self.lock_dir)
-
+
def get_creation_lock(self, key):
return file_synchronizer(
identifier = "filecontainer/funclock/%s" % self.namespace,
lock_dir = self.lock_dir
)
-
+
def file_exists(self, file):
return os.access(file, os.F_OK)
@@ -562,7 +562,7 @@ class FileNamespaceManager(OpenResourceNamespaceManager):
fh.close()
self.flags = flags
-
+
def do_close(self):
if self.flags == 'c' or self.flags == 'w':
fh = open(self.file, 'wb')
@@ -571,7 +571,7 @@ class FileNamespaceManager(OpenResourceNamespaceManager):
self.hash = {}
self.flags = None
-
+
def do_remove(self):
try:
os.remove(self.file)
@@ -580,13 +580,13 @@ class FileNamespaceManager(OpenResourceNamespaceManager):
# but client code has asked for a clear() operation...
pass
self.hash = {}
-
+
def __getitem__(self, key):
return self.hash[key]
def __contains__(self, key):
return self.hash.has_key(key)
-
+
def __setitem__(self, key, value):
self.hash[key] = value
@@ -602,7 +602,7 @@ class FileNamespaceManager(OpenResourceNamespaceManager):
namespace_classes = {}
ContainerContext = dict
-
+
class ContainerMeta(type):
def __init__(cls, classname, bases, dict_):
namespace_classes[cls] = cls.namespace_class
diff --git a/module/lib/beaker/crypto/pbkdf2.py b/module/lib/beaker/crypto/pbkdf2.py
index 5f40a5c78..96dc5fbb2 100644
--- a/module/lib/beaker/crypto/pbkdf2.py
+++ b/module/lib/beaker/crypto/pbkdf2.py
@@ -78,8 +78,8 @@ def strxor(a, b):
return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b)])
class PBKDF2(object):
- """PBKDF2.py: PKCS#5 v2.0 Password-Based Key Derivation
-
+ """PBKDF2.py : PKCS#5 v2.0 Password-Based Key Derivation
+
This implementation takes a passphrase and a salt (and optionally an
iteration count, a digest module, and a MAC module) and provides a
file-like object from which an arbitrarily-sized key can be read.
@@ -89,10 +89,10 @@ class PBKDF2(object):
The idea behind PBKDF2 is to derive a cryptographic key from a
passphrase and a salt.
-
+
PBKDF2 may also be used as a strong salted password hash. The
'crypt' function is provided for that purpose.
-
+
Remember: Keys generated using PBKDF2 are only as strong as the
passphrases they are derived from.
"""
@@ -109,7 +109,7 @@ class PBKDF2(object):
"""Pseudorandom function. e.g. HMAC-SHA1"""
return self.__macmodule(key=key, msg=msg,
digestmod=self.__digestmodule).digest()
-
+
def read(self, bytes):
"""Read the specified number of key bytes."""
if self.closed:
@@ -131,7 +131,7 @@ class PBKDF2(object):
self.__buf = buf[bytes:]
self.__blockNum = i
return retval
-
+
def __f(self, i):
# i must fit within 32 bits
assert (1 <= i <= 0xffffffff)
@@ -141,7 +141,7 @@ class PBKDF2(object):
U = self.__prf(self.__passphrase, U)
result = strxor(result, U)
return result
-
+
def hexread(self, octets):
"""Read the specified number of octets. Return them as hexadecimal.
@@ -151,7 +151,7 @@ class PBKDF2(object):
def _setup(self, passphrase, salt, iterations, prf):
# Sanity checks:
-
+
# passphrase and salt must be str or unicode (in the latter
# case, we convert to UTF-8)
if isinstance(passphrase, unicode):
@@ -168,7 +168,7 @@ class PBKDF2(object):
raise TypeError("iterations must be an integer")
if iterations < 1:
raise ValueError("iterations must be at least 1")
-
+
# prf must be callable
if not callable(prf):
raise TypeError("prf must be callable")
@@ -180,7 +180,7 @@ class PBKDF2(object):
self.__blockNum = 0
self.__buf = ""
self.closed = False
-
+
def close(self):
"""Close the stream."""
if not self.closed:
@@ -194,13 +194,13 @@ class PBKDF2(object):
def crypt(word, salt=None, iterations=None):
"""PBKDF2-based unix crypt(3) replacement.
-
+
The number of iterations specified in the salt overrides the 'iterations'
parameter.
The effective hash length is 192 bits.
"""
-
+
# Generate a (pseudo-)random salt if the user hasn't provided one.
if salt is None:
salt = _makesalt()
@@ -229,7 +229,7 @@ def crypt(word, salt=None, iterations=None):
iterations = converted
if not (iterations >= 1):
raise ValueError("Invalid salt")
-
+
# Make sure the salt matches the allowed character set
allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
for ch in salt:
@@ -251,7 +251,7 @@ PBKDF2.crypt = staticmethod(crypt)
def _makesalt():
"""Return a 48-bit pseudorandom salt for crypt().
-
+
This function is not suitable for generating cryptographic secrets.
"""
binarysalt = "".join([pack("@H", randint(0, 0xffff)) for i in range(3)])
@@ -260,7 +260,7 @@ def _makesalt():
def test_pbkdf2():
"""Module self-test"""
from binascii import a2b_hex
-
+
#
# Test vectors from RFC 3962
#
@@ -284,18 +284,18 @@ def test_pbkdf2():
"c5ec59f1a452f5cc9ad940fea0598ed1")
if result != expected:
raise RuntimeError("self-test failed")
-
+
# Test 4
result = PBKDF2("X"*65, "pass phrase exceeds block size", 1200).hexread(32)
expected = ("9ccad6d468770cd51b10e6a68721be61"
"1a8b4d282601db3b36be9246915ec82a")
if result != expected:
raise RuntimeError("self-test failed")
-
+
#
# Other test vectors
#
-
+
# Chunked read
f = PBKDF2("kickstart", "workbench", 256)
result = f.read(17)
@@ -306,7 +306,7 @@ def test_pbkdf2():
expected = PBKDF2("kickstart", "workbench", 256).read(40)
if result != expected:
raise RuntimeError("self-test failed")
-
+
#
# crypt() test vectors
#
@@ -316,7 +316,7 @@ def test_pbkdf2():
expected = '$p5k2$$exec$r1EWMCMk7Rlv3L/RNcFXviDefYa0hlql'
if result != expected:
raise RuntimeError("self-test failed")
-
+
# crypt 2
result = crypt("gnu", '$p5k2$c$u9HvcT4d$.....')
expected = '$p5k2$c$u9HvcT4d$Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g'
@@ -328,7 +328,7 @@ def test_pbkdf2():
expected = "$p5k2$d$tUsch7fU$nqDkaxMDOFBeJsTSfABsyn.PYUXilHwL"
if result != expected:
raise RuntimeError("self-test failed")
-
+
# crypt 4 (unicode)
result = crypt(u'\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2',
'$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ')
diff --git a/module/lib/beaker/crypto/pycrypto.py b/module/lib/beaker/crypto/pycrypto.py
index 1939555ce..a3eb4d9db 100644
--- a/module/lib/beaker/crypto/pycrypto.py
+++ b/module/lib/beaker/crypto/pycrypto.py
@@ -9,16 +9,16 @@ try:
def aesEncrypt(data, key):
cipher = aes.AES(key)
return cipher.process(data)
-
+
# magic.
aesDecrypt = aesEncrypt
-
+
except ImportError:
from Crypto.Cipher import AES
def aesEncrypt(data, key):
cipher = AES.new(key)
-
+
data = data + (" " * (16 - (len(data) % 16)))
return cipher.encrypt(data)
diff --git a/module/lib/beaker/crypto/util.py b/module/lib/beaker/crypto/util.py
index 7f96ac856..d97e8ce6f 100644
--- a/module/lib/beaker/crypto/util.py
+++ b/module/lib/beaker/crypto/util.py
@@ -6,9 +6,9 @@ try:
# Use PyCrypto (if available)
from Crypto.Hash import HMAC as hmac, SHA as hmac_sha1
sha1 = hmac_sha1.new
-
+
except ImportError:
-
+
# PyCrypto not available. Use the Python standard library.
import hmac
diff --git a/module/lib/beaker/ext/database.py b/module/lib/beaker/ext/database.py
index 7e3db6899..701e6f7d2 100644
--- a/module/lib/beaker/ext/database.py
+++ b/module/lib/beaker/ext/database.py
@@ -30,12 +30,12 @@ class DatabaseNamespaceManager(OpenResourceNamespaceManager):
except ImportError:
raise InvalidCacheBackendError("Database cache backend requires "
"the 'sqlalchemy' library")
-
+
def __init__(self, namespace, url=None, sa_opts=None, optimistic=False,
table_name='beaker_cache', data_dir=None, lock_dir=None,
**params):
"""Creates a database namespace manager
-
+
``url``
SQLAlchemy compliant db url
``sa_opts``
@@ -49,7 +49,7 @@ class DatabaseNamespaceManager(OpenResourceNamespaceManager):
The table name to use in the database for the cache.
"""
OpenResourceNamespaceManager.__init__(self, namespace)
-
+
if sa_opts is None:
sa_opts = params
@@ -59,7 +59,7 @@ class DatabaseNamespaceManager(OpenResourceNamespaceManager):
self.lock_dir = data_dir + "/container_db_lock"
if self.lock_dir:
verify_directory(self.lock_dir)
-
+
# Check to see if the table's been created before
url = url or sa_opts['sa.url']
table_key = url + table_name
@@ -90,7 +90,7 @@ class DatabaseNamespaceManager(OpenResourceNamespaceManager):
self._is_new = False
self.loaded = False
self.cache = DatabaseNamespaceManager.tables.get(table_key, make_cache)
-
+
def get_access_lock(self):
return null_synchronizer()
@@ -104,7 +104,7 @@ class DatabaseNamespaceManager(OpenResourceNamespaceManager):
if self.loaded:
self.flags = flags
return
-
+
cache = self.cache
result = sa.select([cache.c.data],
cache.c.namespace==self.namespace
@@ -123,7 +123,7 @@ class DatabaseNamespaceManager(OpenResourceNamespaceManager):
self._is_new = True
self.flags = flags
self.loaded = True
-
+
def do_close(self):
if self.flags is not None and (self.flags == 'c' or self.flags == 'w'):
cache = self.cache
@@ -136,12 +136,12 @@ class DatabaseNamespaceManager(OpenResourceNamespaceManager):
cache.update(cache.c.namespace==self.namespace).execute(
data=self.hash, accessed=datetime.now())
self.flags = None
-
+
def do_remove(self):
cache = self.cache
cache.delete(cache.c.namespace==self.namespace).execute()
self.hash = {}
-
+
# We can retain the fact that we did a load attempt, but since the
# file is gone this will be a new namespace should it be saved.
self._is_new = True
@@ -151,7 +151,7 @@ class DatabaseNamespaceManager(OpenResourceNamespaceManager):
def __contains__(self, key):
return self.hash.has_key(key)
-
+
def __setitem__(self, key, value):
self.hash[key] = value
diff --git a/module/lib/beaker/ext/google.py b/module/lib/beaker/ext/google.py
index b6d617d6b..dd8380d7f 100644
--- a/module/lib/beaker/ext/google.py
+++ b/module/lib/beaker/ext/google.py
@@ -23,11 +23,11 @@ class GoogleNamespaceManager(OpenResourceNamespaceManager):
except ImportError:
raise InvalidCacheBackendError("Datastore cache backend requires the "
"'google.appengine.ext' library")
-
+
def __init__(self, namespace, table_name='beaker_cache', **params):
"""Creates a datastore namespace manager"""
OpenResourceNamespaceManager.__init__(self, namespace)
-
+
def make_cache():
table_dict = dict(created=db.DateTimeProperty(),
accessed=db.DateTimeProperty(),
@@ -40,11 +40,11 @@ class GoogleNamespaceManager(OpenResourceNamespaceManager):
self._is_new = False
self.loaded = False
self.log_debug = logging.DEBUG >= log.getEffectiveLevel()
-
+
# Google wants namespaces to start with letters, change the namespace
# to start with a letter
self.namespace = 'p%s' % self.namespace
-
+
def get_access_lock(self):
return null_synchronizer()
@@ -57,9 +57,9 @@ class GoogleNamespaceManager(OpenResourceNamespaceManager):
if self.loaded:
self.flags = flags
return
-
+
item = self.cache.get_by_key_name(self.namespace)
-
+
if not item:
self._is_new = True
self.hash = {}
@@ -74,7 +74,7 @@ class GoogleNamespaceManager(OpenResourceNamespaceManager):
self._is_new = True
self.flags = flags
self.loaded = True
-
+
def do_close(self):
if self.flags is not None and (self.flags == 'c' or self.flags == 'w'):
if self._is_new:
@@ -90,12 +90,12 @@ class GoogleNamespaceManager(OpenResourceNamespaceManager):
item.accessed = datetime.now()
item.put()
self.flags = None
-
+
def do_remove(self):
item = self.cache.get_by_key_name(self.namespace)
item.delete()
self.hash = {}
-
+
# We can retain the fact that we did a load attempt, but since the
# file is gone this will be a new namespace should it be saved.
self._is_new = True
@@ -105,7 +105,7 @@ class GoogleNamespaceManager(OpenResourceNamespaceManager):
def __contains__(self, key):
return self.hash.has_key(key)
-
+
def __setitem__(self, key, value):
self.hash[key] = value
@@ -114,7 +114,7 @@ class GoogleNamespaceManager(OpenResourceNamespaceManager):
def keys(self):
return self.hash.keys()
-
+
class GoogleContainer(Container):
namespace_class = GoogleNamespaceManager
diff --git a/module/lib/beaker/ext/memcached.py b/module/lib/beaker/ext/memcached.py
index 60360ae0a..96516953f 100644
--- a/module/lib/beaker/ext/memcached.py
+++ b/module/lib/beaker/ext/memcached.py
@@ -8,7 +8,7 @@ memcache = None
class MemcachedNamespaceManager(NamespaceManager):
clients = SyncDict()
-
+
@classmethod
def _init_dependencies(cls):
global memcache
@@ -27,25 +27,25 @@ class MemcachedNamespaceManager(NamespaceManager):
except ImportError:
raise InvalidCacheBackendError("Memcached cache backend requires either "
"the 'memcache' or 'cmemcache' library")
-
+
def __init__(self, namespace, url=None, data_dir=None, lock_dir=None, **params):
NamespaceManager.__init__(self, namespace)
-
+
if not url:
raise MissingCacheParameter("url is required")
-
+
if lock_dir:
self.lock_dir = lock_dir
elif data_dir:
self.lock_dir = data_dir + "/container_mcd_lock"
if self.lock_dir:
verify_directory(self.lock_dir)
-
+
self.mc = MemcachedNamespaceManager.clients.get(url, memcache.Client, url.split(';'))
def get_creation_lock(self, key):
return file_synchronizer(
- identifier="memcachedcontainer/funclock/%s" % self.namespace, lock_dir = self.lock_dir)
+ identifier="memcachedcontainer/funclock/%s" % self.namespace,lock_dir = self.lock_dir)
def _format_key(self, key):
return self.namespace + '_' + key.replace(' ', '\302\267')
@@ -68,13 +68,13 @@ class MemcachedNamespaceManager(NamespaceManager):
def __setitem__(self, key, value):
self.set_value(key, value)
-
+
def __delitem__(self, key):
self.mc.delete(self._format_key(key))
def do_remove(self):
self.mc.flush_all()
-
+
def keys(self):
raise NotImplementedError("Memcache caching does not support iteration of all cache keys")
diff --git a/module/lib/beaker/middleware.py b/module/lib/beaker/middleware.py
index 785d25377..7ba88b37d 100644
--- a/module/lib/beaker/middleware.py
+++ b/module/lib/beaker/middleware.py
@@ -16,15 +16,15 @@ from beaker.util import coerce_cache_params, coerce_session_params, \
class CacheMiddleware(object):
cache = beaker_cache
-
+
def __init__(self, app, config=None, environ_key='beaker.cache', **kwargs):
"""Initialize the Cache Middleware
-
+
The Cache middleware will make a Cache instance available
every request under the ``environ['beaker.cache']`` key by
default. The location in environ can be changed by setting
``environ_key``.
-
+
``config``
dict All settings should be prefixed by 'cache.'. This
method of passing variables is intended for Paste and other
@@ -32,11 +32,11 @@ class CacheMiddleware(object):
single dictionary. If config contains *no cache. prefixed
args*, then *all* of the config options will be used to
intialize the Cache objects.
-
+
``environ_key``
Location where the Cache instance will keyed in the WSGI
environ
-
+
``**kwargs``
All keyword arguments are assumed to be cache settings and
will override any settings found in ``config``
@@ -44,26 +44,26 @@ class CacheMiddleware(object):
"""
self.app = app
config = config or {}
-
+
self.options = {}
-
+
# Update the options with the parsed config
self.options.update(parse_cache_config_options(config))
-
+
# Add any options from kwargs, but leave out the defaults this
# time
self.options.update(
parse_cache_config_options(kwargs, include_defaults=False))
-
+
# Assume all keys are intended for cache if none are prefixed with
# 'cache.'
if not self.options and config:
self.options = config
-
+
self.options.update(kwargs)
self.cache_manager = CacheManager(**self.options)
self.environ_key = environ_key
-
+
def __call__(self, environ, start_response):
if environ.get('paste.registry'):
if environ['paste.registry'].reglist:
@@ -75,16 +75,16 @@ class CacheMiddleware(object):
class SessionMiddleware(object):
session = beaker_session
-
+
def __init__(self, wrap_app, config=None, environ_key='beaker.session',
**kwargs):
"""Initialize the Session Middleware
-
+
The Session middleware will make a lazy session instance
available every request under the ``environ['beaker.session']``
key by default. The location in environ can be changed by
setting ``environ_key``.
-
+
``config``
dict All settings should be prefixed by 'session.'. This
method of passing variables is intended for Paste and other
@@ -92,18 +92,18 @@ class SessionMiddleware(object):
single dictionary. If config contains *no cache. prefixed
args*, then *all* of the config options will be used to
intialize the Cache objects.
-
+
``environ_key``
Location where the Session instance will keyed in the WSGI
environ
-
+
``**kwargs``
All keyword arguments are assumed to be session settings and
will override any settings found in ``config``
"""
config = config or {}
-
+
# Load up the default params
self.options = dict(invalidate_corrupt=True, type=None,
data_dir=None, key='beaker.session.id',
@@ -120,19 +120,19 @@ class SessionMiddleware(object):
warnings.warn('Session options should start with session. '
'instead of session_.', DeprecationWarning, 2)
self.options[key[8:]] = val
-
+
# Coerce and validate session params
coerce_session_params(self.options)
-
+
# Assume all keys are intended for cache if none are prefixed with
# 'cache.'
if not self.options and config:
self.options = config
-
+
self.options.update(kwargs)
self.wrap_app = wrap_app
self.environ_key = environ_key
-
+
def __call__(self, environ, start_response):
session = SessionObject(environ, **self.options)
if environ.get('paste.registry'):
@@ -140,7 +140,7 @@ class SessionMiddleware(object):
environ['paste.registry'].register(self.session, session)
environ[self.environ_key] = session
environ['beaker.get_session'] = self._get_session
-
+
def session_start_response(status, headers, exc_info = None):
if session.accessed():
session.persist()
@@ -150,7 +150,7 @@ class SessionMiddleware(object):
headers.append(('Set-cookie', cookie))
return start_response(status, headers, exc_info)
return self.wrap_app(environ, session_start_response)
-
+
def _get_session(self):
return Session({}, use_cookies=False, **self.options)
diff --git a/module/lib/beaker/session.py b/module/lib/beaker/session.py
index a1461ebdd..7d465530b 100644
--- a/module/lib/beaker/session.py
+++ b/module/lib/beaker/session.py
@@ -3,7 +3,7 @@ import os
import random
import time
from datetime import datetime, timedelta
-
+
from beaker.crypto import hmac as HMAC, hmac_sha1 as SHA1, md5
from beaker.util import pickle
@@ -15,32 +15,32 @@ from base64 import b64encode, b64decode
__all__ = ['SignedCookie', 'Session']
-getpid = hasattr(os, 'getpid') and os.getpid or (lambda: '')
+getpid = hasattr(os, 'getpid') and os.getpid or (lambda : '')
class SignedCookie(Cookie.BaseCookie):
"""Extends python cookie to give digital signature support"""
def __init__(self, secret, input=None):
self.secret = secret
Cookie.BaseCookie.__init__(self, input)
-
+
def value_decode(self, val):
val = val.strip('"')
sig = HMAC.new(self.secret, val[40:], SHA1).hexdigest()
-
+
# Avoid timing attacks
invalid_bits = 0
input_sig = val[:40]
if len(sig) != len(input_sig):
return None, val
-
+
for a, b in zip(sig, input_sig):
invalid_bits += a != b
-
+
if invalid_bits:
return None, val
else:
return val[40:], val
-
+
def value_encode(self, val):
sig = HMAC.new(self.secret, val, SHA1).hexdigest()
return str(val), ("%s%s" % (sig, val))
@@ -48,7 +48,7 @@ class SignedCookie(Cookie.BaseCookie):
class Session(dict):
"""Session object that uses container package for storage.
-
+
``key``
The name the cookie should be set to.
``timeout``
@@ -76,15 +76,15 @@ class Session(dict):
self.namespace_class = namespace_class or clsmap[self.type]
self.namespace_args = namespace_args
-
+
self.request = request
self.data_dir = data_dir
self.key = key
-
+
self.timeout = timeout
self.use_cookies = use_cookies
self.cookie_expires = cookie_expires
-
+
# Default cookie domain/path
self._domain = cookie_domain
self._path = '/'
@@ -93,7 +93,7 @@ class Session(dict):
self.secure = secure
self.id = id
self.accessed_dict = {}
-
+
if self.use_cookies:
cookieheader = request.get('cookie', '')
if secret:
@@ -103,10 +103,10 @@ class Session(dict):
self.cookie = SignedCookie(secret, input=None)
else:
self.cookie = Cookie.SimpleCookie(input=cookieheader)
-
+
if not self.id and self.key in self.cookie:
self.id = self.cookie[self.key].value
-
+
self.is_new = self.id is None
if self.is_new:
self._create_id()
@@ -119,7 +119,7 @@ class Session(dict):
self.invalidate()
else:
raise
-
+
def _create_id(self):
self.id = md5(
md5("%f%s%f%s" % (time.time(), id({}), random.random(),
@@ -148,31 +148,31 @@ class Session(dict):
expires.strftime("%a, %d-%b-%Y %H:%M:%S GMT" )
self.request['cookie_out'] = self.cookie[self.key].output(header='')
self.request['set_cookie'] = False
-
+
def created(self):
return self['_creation_time']
created = property(created)
-
+
def _set_domain(self, domain):
self['_domain'] = domain
self.cookie[self.key]['domain'] = domain
self.request['cookie_out'] = self.cookie[self.key].output(header='')
self.request['set_cookie'] = True
-
+
def _get_domain(self):
return self._domain
-
+
domain = property(_get_domain, _set_domain)
-
+
def _set_path(self, path):
self['_path'] = path
self.cookie[self.key]['path'] = path
self.request['cookie_out'] = self.cookie[self.key].output(header='')
self.request['set_cookie'] = True
-
+
def _get_path(self):
return self._path
-
+
path = property(_get_path, _set_path)
def _delete_cookie(self):
@@ -203,7 +203,7 @@ class Session(dict):
self.was_invalidated = True
self._create_id()
self.load()
-
+
def load(self):
"Loads the data from this session from persistent storage"
self.namespace = self.namespace_class(self.id,
@@ -211,7 +211,7 @@ class Session(dict):
**self.namespace_args)
now = time.time()
self.request['set_cookie'] = True
-
+
self.namespace.acquire_read_lock()
timed_out = False
try:
@@ -233,7 +233,7 @@ class Session(dict):
'_accessed_time':now
}
self.is_new = True
-
+
if self.timeout is not None and \
now - session_data['_accessed_time'] > self.timeout:
timed_out= True
@@ -244,7 +244,7 @@ class Session(dict):
self.last_accessed = None
else:
self.last_accessed = session_data['_accessed_time']
-
+
# Update the current _accessed_time
session_data['_accessed_time'] = now
self.update(session_data)
@@ -253,34 +253,34 @@ class Session(dict):
self.namespace.release_read_lock()
if timed_out:
self.invalidate()
-
+
def save(self, accessed_only=False):
"""Saves the data for this session to persistent storage
-
+
If accessed_only is True, then only the original data loaded
at the beginning of the request will be saved, with the updated
last accessed time.
-
+
"""
# Look to see if its a new session that was only accessed
# Don't save it under that case
if accessed_only and self.is_new:
return None
-
+
if not hasattr(self, 'namespace'):
self.namespace = self.namespace_class(
self.id,
data_dir=self.data_dir,
digest_filenames=False,
**self.namespace_args)
-
+
self.namespace.acquire_write_lock()
try:
if accessed_only:
data = dict(self.accessed_dict.items())
else:
data = dict(self.items())
-
+
# Save the data
if not data and 'session' in self.namespace:
del self.namespace['session']
@@ -290,20 +290,20 @@ class Session(dict):
self.namespace.release_write_lock()
if self.is_new:
self.request['set_cookie'] = True
-
+
def revert(self):
"""Revert the session to its original state from its first
access in the request"""
self.clear()
self.update(self.accessed_dict)
-
+
# TODO: I think both these methods should be removed. They're from
# the original mod_python code i was ripping off but they really
# have no use here.
def lock(self):
"""Locks this session against other processes/threads. This is
automatic when load/save is called.
-
+
***use with caution*** and always with a corresponding 'unlock'
inside a "finally:" block, as a stray lock typically cannot be
unlocked without shutting down the whole application.
@@ -324,10 +324,10 @@ class Session(dict):
class CookieSession(Session):
"""Pure cookie-based session
-
+
Options recognized when using cookie-based sessions are slightly
more restricted than general sessions.
-
+
``key``
The name the cookie should be set to.
``timeout``
@@ -343,16 +343,16 @@ class CookieSession(Session):
Domain to use for the cookie.
``secure``
Whether or not the cookie should only be sent over SSL.
-
+
"""
def __init__(self, request, key='beaker.session.id', timeout=None,
cookie_expires=True, cookie_domain=None, encrypt_key=None,
validate_key=None, secure=False, **kwargs):
-
+
if not crypto.has_aes and encrypt_key:
raise InvalidCryptoBackendError("No AES library is installed, can't generate "
"encrypted cookie-only Session.")
-
+
self.request = request
self.key = key
self.timeout = timeout
@@ -363,24 +363,24 @@ class CookieSession(Session):
self.secure = secure
self._domain = cookie_domain
self._path = '/'
-
+
try:
cookieheader = request['cookie']
except KeyError:
cookieheader = ''
-
+
if validate_key is None:
raise BeakerException("No validate_key specified for Cookie only "
"Session.")
-
+
try:
self.cookie = SignedCookie(validate_key, input=cookieheader)
except Cookie.CookieError:
self.cookie = SignedCookie(validate_key, input=None)
-
+
self['_id'] = self._make_id()
self.is_new = True
-
+
# If we have a cookie, load it
if self.key in self.cookie and self.cookie[self.key].value is not None:
self.is_new = False
@@ -393,11 +393,11 @@ class CookieSession(Session):
self.clear()
self.accessed_dict = self.copy()
self._create_cookie()
-
+
def created(self):
return self['_creation_time']
created = property(created)
-
+
def id(self):
return self['_id']
id = property(id)
@@ -405,19 +405,19 @@ class CookieSession(Session):
def _set_domain(self, domain):
self['_domain'] = domain
self._domain = domain
-
+
def _get_domain(self):
return self._domain
-
+
domain = property(_get_domain, _set_domain)
-
+
def _set_path(self, path):
self['_path'] = path
self._path = path
-
+
def _get_path(self):
return self._path
-
+
path = property(_get_path, _set_path)
def _encrypt_data(self):
@@ -431,7 +431,7 @@ class CookieSession(Session):
else:
data = pickle.dumps(self.copy(), 2)
return b64encode(data)
-
+
def _decrypt_data(self):
"""Bas64, decipher, then un-serialize the data for the session
dict"""
@@ -445,13 +445,13 @@ class CookieSession(Session):
else:
data = b64decode(self.cookie[self.key].value)
return pickle.loads(data)
-
+
def _make_id(self):
return md5(md5(
"%f%s%f%s" % (time.time(), id({}), random.random(), getpid())
).hexdigest()
).hexdigest()
-
+
def save(self, accessed_only=False):
"""Saves the data for this session to persistent storage"""
if accessed_only and self.is_new:
@@ -460,19 +460,19 @@ class CookieSession(Session):
self.clear()
self.update(self.accessed_dict)
self._create_cookie()
-
+
def expire(self):
"""Delete the 'expires' attribute on this Session, if any."""
-
+
self.pop('_expires', None)
-
+
def _create_cookie(self):
if '_creation_time' not in self:
self['_creation_time'] = time.time()
if '_id' not in self:
self['_id'] = self._make_id()
self['_accessed_time'] = time.time()
-
+
if self.cookie_expires is not True:
if self.cookie_expires is False:
expires = datetime.fromtimestamp( 0x7FFFFFFF )
@@ -492,7 +492,7 @@ class CookieSession(Session):
val = self._encrypt_data()
if len(val) > 4064:
raise BeakerException("Cookie value is too long to store")
-
+
self.cookie[self.key] = val
if '_domain' in self:
self.cookie[self.key]['domain'] = self['_domain']
@@ -500,21 +500,21 @@ class CookieSession(Session):
self.cookie[self.key]['domain'] = self._domain
if self.secure:
self.cookie[self.key]['secure'] = True
-
+
self.cookie[self.key]['path'] = self.get('_path', '/')
-
+
if expires:
self.cookie[self.key]['expires'] = \
expires.strftime("%a, %d-%b-%Y %H:%M:%S GMT" )
self.request['cookie_out'] = self.cookie[self.key].output(header='')
self.request['set_cookie'] = True
-
+
def delete(self):
"""Delete the cookie, and clear the session"""
# Send a delete cookie request
self._delete_cookie()
self.clear()
-
+
def invalidate(self):
"""Clear the contents and start a new session"""
self.delete()
@@ -523,19 +523,19 @@ class CookieSession(Session):
class SessionObject(object):
"""Session proxy/lazy creator
-
+
This object proxies access to the actual session object, so that in
the case that the session hasn't been used before, it will be
setup. This avoid creating and loading the session from persistent
storage unless its actually used during the request.
-
+
"""
def __init__(self, environ, **params):
self.__dict__['_params'] = params
self.__dict__['_environ'] = environ
self.__dict__['_sess'] = None
self.__dict__['_headers'] = []
-
+
def _session(self):
"""Lazy initial creation of session object"""
if self.__dict__['_sess'] is None:
@@ -549,35 +549,35 @@ class SessionObject(object):
self.__dict__['_sess'] = Session(req, use_cookies=True,
**params)
return self.__dict__['_sess']
-
+
def __getattr__(self, attr):
return getattr(self._session(), attr)
-
+
def __setattr__(self, attr, value):
setattr(self._session(), attr, value)
-
+
def __delattr__(self, name):
self._session().__delattr__(name)
-
+
def __getitem__(self, key):
return self._session()[key]
-
+
def __setitem__(self, key, value):
self._session()[key] = value
-
+
def __delitem__(self, key):
self._session().__delitem__(key)
-
+
def __repr__(self):
return self._session().__repr__()
-
+
def __iter__(self):
"""Only works for proxying to a dict"""
return iter(self._session().keys())
-
+
def __contains__(self, key):
return self._session().has_key(key)
-
+
def get_by_id(self, id):
"""Loads a session given a session ID"""
params = self.__dict__['_params']
@@ -585,22 +585,22 @@ class SessionObject(object):
if session.is_new:
return None
return session
-
+
def save(self):
self.__dict__['_dirty'] = True
-
+
def delete(self):
self.__dict__['_dirty'] = True
self._session().delete()
-
+
def persist(self):
"""Persist the session to the storage
-
+
If its set to autosave, then the entire session will be saved
regardless of if save() has been called. Otherwise, just the
accessed time will be updated if save() was not called, or
the session will be saved if save() was called.
-
+
"""
if self.__dict__['_params'].get('auto'):
self._session().save()
@@ -609,10 +609,10 @@ class SessionObject(object):
self._session().save()
else:
self._session().save(accessed_only=True)
-
+
def dirty(self):
return self.__dict__.get('_dirty', False)
-
+
def accessed(self):
"""Returns whether or not the session has been accessed"""
return self.__dict__['_sess'] is not None
diff --git a/module/lib/beaker/synchronization.py b/module/lib/beaker/synchronization.py
index 6616ca78b..761303707 100644
--- a/module/lib/beaker/synchronization.py
+++ b/module/lib/beaker/synchronization.py
@@ -36,7 +36,7 @@ __all__ = ["file_synchronizer", "mutex_synchronizer", "null_synchronizer",
class NameLock(object):
"""a proxy for an RLock object that is stored in a name based
registry.
-
+
Multiple threads can get a reference to the same RLock based on the
name alone, and synchronize operations related to that name.
@@ -115,7 +115,7 @@ class SynchronizerImpl(object):
else:
return self._state.get()
state = property(state)
-
+
def release_read_lock(self):
state = self.state
@@ -123,19 +123,19 @@ class SynchronizerImpl(object):
raise LockError("lock is in writing state")
if not state.reading:
raise LockError("lock is not in reading state")
-
+
if state.reentrantcount == 1:
self.do_release_read_lock()
state.reading = False
state.reentrantcount -= 1
-
+
def acquire_read_lock(self, wait = True):
state = self.state
if state.writing:
raise LockError("lock is in writing state")
-
+
if state.reentrantcount == 0:
x = self.do_acquire_read_lock(wait)
if (wait or x):
@@ -145,7 +145,7 @@ class SynchronizerImpl(object):
elif state.reading:
state.reentrantcount += 1
return True
-
+
def release_write_lock(self):
state = self.state
@@ -159,15 +159,15 @@ class SynchronizerImpl(object):
state.writing = False
state.reentrantcount -= 1
-
+
release = release_write_lock
-
+
def acquire_write_lock(self, wait = True):
state = self.state
if state.reading:
raise LockError("lock is in reading state")
-
+
if state.reentrantcount == 0:
x = self.do_acquire_write_lock(wait)
if (wait or x):
@@ -182,13 +182,13 @@ class SynchronizerImpl(object):
def do_release_read_lock(self):
raise NotImplementedError()
-
+
def do_acquire_read_lock(self):
raise NotImplementedError()
-
+
def do_release_write_lock(self):
raise NotImplementedError()
-
+
def do_acquire_write_lock(self):
raise NotImplementedError()
@@ -198,18 +198,18 @@ class FileSynchronizer(SynchronizerImpl):
Adapted for Python/multithreads from Apache::Session::Lock::File,
http://search.cpan.org/src/CWEST/Apache-Session-1.81/Session/Lock/File.pm
-
+
This module does not unlink temporary files,
because it interferes with proper locking. This can cause
problems on certain systems (Linux) whose file systems (ext2) do not
perform well with lots of files in one directory. To prevent this
you should use a script to clean out old files from your lock directory.
-
+
"""
def __init__(self, identifier, lock_dir):
super(FileSynchronizer, self).__init__()
self._filedescriptor = util.ThreadLocal()
-
+
if lock_dir is None:
lock_dir = tempfile.gettempdir()
else:
@@ -224,14 +224,14 @@ class FileSynchronizer(SynchronizerImpl):
def _filedesc(self):
return self._filedescriptor.get()
_filedesc = property(_filedesc)
-
+
def _open(self, mode):
filedescriptor = self._filedesc
if filedescriptor is None:
filedescriptor = os.open(self.filename, mode)
self._filedescriptor.put(filedescriptor)
return filedescriptor
-
+
def do_acquire_read_lock(self, wait):
filedescriptor = self._open(os.O_CREAT | os.O_RDONLY)
if not wait:
@@ -259,13 +259,13 @@ class FileSynchronizer(SynchronizerImpl):
else:
fcntl.flock(filedescriptor, fcntl.LOCK_EX)
return True
-
+
def do_release_read_lock(self):
self._release_all_locks()
-
+
def do_release_write_lock(self):
self._release_all_locks()
-
+
def _release_all_locks(self):
filedescriptor = self._filedesc
if filedescriptor is not None:
@@ -276,7 +276,7 @@ class FileSynchronizer(SynchronizerImpl):
class ConditionSynchronizer(SynchronizerImpl):
"""a synchronizer using a Condition."""
-
+
def __init__(self, identifier):
super(ConditionSynchronizer, self).__init__()
@@ -308,12 +308,12 @@ class ConditionSynchronizer(SynchronizerImpl):
if not wait:
return True
-
+
def do_release_read_lock(self):
self.condition.acquire()
try:
self.async -= 1
-
+
# check if we are the last asynchronous reader thread
# out the door.
if self.async == 0:
@@ -326,13 +326,13 @@ class ConditionSynchronizer(SynchronizerImpl):
"release_read_locks called")
finally:
self.condition.release()
-
+
def do_acquire_write_lock(self, wait = True):
self.condition.acquire()
try:
# here, we are not a synchronous reader, and after returning,
# assuming waiting or immediate availability, we will be.
-
+
if wait:
# if another sync is working, wait
while self.current_sync_operation is not None:
@@ -342,7 +342,7 @@ class ConditionSynchronizer(SynchronizerImpl):
# we dont want to wait, so forget it
if self.current_sync_operation is not None:
return False
-
+
# establish ourselves as the current sync
# this indicates to other read/write operations
# that they should wait until this is None again
@@ -359,7 +359,7 @@ class ConditionSynchronizer(SynchronizerImpl):
return False
finally:
self.condition.release()
-
+
if not wait:
return True
diff --git a/module/lib/beaker/util.py b/module/lib/beaker/util.py
index 0bd82bb85..04c9617c5 100644
--- a/module/lib/beaker/util.py
+++ b/module/lib/beaker/util.py
@@ -16,7 +16,7 @@ import warnings
import sys
py3k = getattr(sys, 'py3kwarning', False) or sys.version_info >= (3, 0)
-py24 = sys.version_info < (2, 5)
+py24 = sys.version_info < (2,5)
jython = sys.platform.startswith('java')
if py3k or jython:
@@ -45,6 +45,7 @@ def verify_directory(dir):
if tries > 5:
raise
+
def deprecated(message):
def wrapper(fn):
def deprecated_method(*args, **kargs):
@@ -55,7 +56,7 @@ def deprecated(message):
deprecated_method.__doc__ = "%s\n\n%s" % (message, fn.__doc__)
return deprecated_method
return wrapper
-
+
class ThreadLocal(object):
"""stores a value on a per-thread basis"""
@@ -63,25 +64,25 @@ class ThreadLocal(object):
def __init__(self):
self._tlocal = _tlocal()
-
+
def put(self, value):
self._tlocal.value = value
-
+
def has(self):
return hasattr(self._tlocal, 'value')
-
+
def get(self, default=None):
return getattr(self._tlocal, 'value', default)
-
+
def remove(self):
del self._tlocal.value
-
+
class SyncDict(object):
"""
An efficient/threadsafe singleton map algorithm, a.k.a.
"get a value based on this key, and create if not found or not
valid" paradigm:
-
+
exists && isvalid ? get : create
Designed to work with weakref dictionaries to expect items
@@ -95,7 +96,7 @@ class SyncDict(object):
def __init__(self):
self.mutex = _thread.allocate_lock()
self.dict = {}
-
+
def get(self, key, createfunc, *args, **kwargs):
try:
if self.has_key(key):
@@ -124,7 +125,7 @@ class SyncDict(object):
def has_key(self, key):
return self.dict.has_key(key)
-
+
def __contains__(self, key):
return self.dict.__contains__(key)
def __getitem__(self, key):
@@ -145,30 +146,30 @@ class WeakValuedRegistry(SyncDict):
sha1 = None
def encoded_path(root, identifiers, extension = ".enc", depth = 3,
digest_filenames=True):
-
+
"""Generate a unique file-accessible path from the given list of
identifiers starting at the given root directory."""
ident = "_".join(identifiers)
-
+
global sha1
if sha1 is None:
from beaker.crypto import sha1
-
+
if digest_filenames:
if py3k:
ident = sha1(ident.encode('utf-8')).hexdigest()
else:
ident = sha1(ident).hexdigest()
-
+
ident = os.path.basename(ident)
tokens = []
for d in range(1, depth):
tokens.append(ident[0:d])
-
+
dir = os.path.join(root, *tokens)
verify_directory(dir)
-
+
return os.path.join(dir, ident + extension)
@@ -250,7 +251,7 @@ def coerce_cache_params(params):
def parse_cache_config_options(config, include_defaults=True):
"""Parse configuration options and validate for use with the
CacheManager"""
-
+
# Load default cache options
if include_defaults:
options= dict(type='memory', data_dir=None, expire=None,
@@ -263,11 +264,11 @@ def parse_cache_config_options(config, include_defaults=True):
if key.startswith('cache.'):
options[key[6:]] = val
coerce_cache_params(options)
-
+
# Set cache to enabled if not turned off
if 'enabled' not in options:
options['enabled'] = True
-
+
# Configure region dict if regions are available
regions = options.pop('regions', None)
if regions:
@@ -294,7 +295,7 @@ def func_namespace(func):
if hasattr(func, 'im_func'):
kls = func.im_class
func = func.im_func
-
+
if kls:
return '%s.%s' % (kls.__module__, kls.__name__)
else: