# Copyright © 2015 STRG.AT GmbH, Vienna, Austria
#
# This file is part of the The SCORE Framework.
#
# The SCORE Framework and all its parts are free software: you can redistribute
# them and/or modify them under the terms of the GNU Lesser General Public
# License version 3 as published by the Free Software Foundation which is in the
# file named COPYING.LESSER.txt.
#
# The SCORE Framework and all its parts are distributed without any WARRANTY;
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. For more details see the GNU Lesser General Public
# License.
#
# If you have not received a copy of the GNU Lesser General Public License see
# http://www.gnu.org/licenses/.
#
# The License-Agreement realised between you as Licensee and STRG.AT GmbH as
# Licenser including the issue of its valid conclusion and its pre- and
# post-contractual effects is governed by the laws of Austria. Any disputes
# concerning this License-Agreement including the issue of its valid conclusion
# and its pre- and post-contractual effects are exclusively decided by the
# competent court, in whose district STRG.AT GmbH has its registered seat, at
# the discretion of STRG.AT GmbH also the competent court, in whose district the
# Licensee has his registered seat, an establishment or assets.
from score.init import parse_object, extract_conf, parse_time_interval, \
ConfiguredModule, parse_dotted_path
import logging
from ._container import NotFound
log = logging.getLogger(__name__)
[docs]def init(confdict):
"""
Initializes this module according to :ref:`our module initialization
guidelines <module_initialization>` with the following configuration keys:
:confkey:`container`
The cache container configuration. A container defines a name,
a backend and optionally a generator and an expire. The configuration
key for a container starts with ``container`` followed by the name
and the configuration keys for the container.
For example, the following configuration::
container.greeter.backend = score.kvcache.backend.FileCache
container.greeter.backend.path = /tmp/greeter.sqlite3
container.greeter.generator = dotted.path.to.greeting_generator
container.greeter.expire = 1m
The Backend config will be passed to
:func:`score.init.init_object`. Have a look at the configurable
backend's constructor parameters for further information about
the backend's configurable keys.
To make life easier for a huge set of container configurations, we serve
the possibility to configure backend aliases that will replace the
container's backend config if the name matches.
For example::
backend.example_filecache = score.kvcache.backend.FileCache
backend.example_filecache.path = /tmp/filecache.sqlite3
container.greeter.backend = example_filecache
container.greeter.generator = dotted.path.to.greeting_generator
container.greeter.expire = 1m
container.counter.backend = example_filecache
container.counter.generator = dotted.path.to.counting_generator
container.counter.expire = 30 seconds
"""
containers = {}
for container_conf in extract_conf(confdict, 'container.'):
if not container_conf.endswith('.backend'):
continue
backend_key = 'container.%s' % container_conf
backend_val = confdict[backend_key]
if backend_val in extract_conf(confdict, 'backend.'):
alias_conf = extract_conf(confdict, 'backend.%s' % backend_val)
for k, v in alias_conf.items():
confdict.update({'%s%s' % (backend_key, k): v})
container_name = container_conf[:-len('.backend')]
backend = parse_object(confdict, backend_key)
generator_key = 'container.%s.generator' % container_name
generator = None
if generator_key in confdict:
generator = parse_dotted_path(confdict[generator_key])
expire_key = 'container.%s.expire' % container_name
expire = None
if expire_key in confdict:
expire = parse_time_interval(confdict[expire_key])
containers[container_name] = CacheContainer(container_name, backend,
generator=generator,
expire=expire)
return ConfiguredKvCacheModule(containers)
class CacheContainer:
"""
A cache container to wrap key-value pairs.
"""
def __init__(self, name, backend, generator=None, expire=None):
self.name = name
self.backend = backend
self.generator = generator
self.expire = expire
def __delitem__(self, key):
"""
Invalidates a value for given key.
"""
self.backend.invalidate(self.name, key)
def __setitem__(self, key, value):
"""
Explicitly sets a value for given key ignoring validity.
"""
self.backend.store(self.name, key, value, self.expire)
def __contains__(self, key):
"""
Checks if a given *key* exists in this container.
"""
try:
self.backend.retrieve(self.name, key)
return True
except NotFound:
return False
def __getitem__(self, key):
"""
Gets a value for given key and takes care of not found cache items.
"""
try:
value = self.backend.retrieve(self.name, key)
except NotFound:
if self.generator is None:
raise
value = self.generator(key)
self[key] = value
return value