2012-12-04 03:41:02 +00:00
|
|
|
from .exceptions import AlreadyRegistered, NotRegistered
|
2013-05-25 04:50:59 +00:00
|
|
|
from .signals import content_required, existence_required, source_saved
|
2013-08-15 02:58:36 +00:00
|
|
|
from .utils import autodiscover, call_strategy_method
|
2012-12-01 22:03:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class GeneratorRegistry(object):
|
|
|
|
|
"""
|
2013-01-24 03:46:57 +00:00
|
|
|
An object for registering generators. This registry provides
|
2012-12-01 22:03:51 +00:00
|
|
|
a convenient way for a distributable app to define default generators
|
|
|
|
|
without locking the users of the app into it.
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self._generators = {}
|
2013-05-10 08:39:46 +00:00
|
|
|
content_required.connect(self.content_required_receiver)
|
|
|
|
|
existence_required.connect(self.existence_required_receiver)
|
2012-12-01 22:03:51 +00:00
|
|
|
|
2012-12-04 03:48:14 +00:00
|
|
|
def register(self, id, generator):
|
2013-04-05 16:25:02 +00:00
|
|
|
registered_generator = self._generators.get(id)
|
|
|
|
|
if registered_generator and generator != self._generators[id]:
|
2013-01-24 03:46:57 +00:00
|
|
|
raise AlreadyRegistered('The generator with id %s is'
|
2012-12-01 22:03:51 +00:00
|
|
|
' already registered' % id)
|
|
|
|
|
self._generators[id] = generator
|
|
|
|
|
|
2013-04-05 20:41:53 +00:00
|
|
|
def unregister(self, id):
|
2012-12-01 22:03:51 +00:00
|
|
|
try:
|
|
|
|
|
del self._generators[id]
|
|
|
|
|
except KeyError:
|
2013-01-24 03:46:57 +00:00
|
|
|
raise NotRegistered('The generator with id %s is not'
|
2012-12-01 22:03:51 +00:00
|
|
|
' registered' % id)
|
|
|
|
|
|
|
|
|
|
def get(self, id, **kwargs):
|
2013-08-15 02:58:36 +00:00
|
|
|
autodiscover()
|
|
|
|
|
|
2012-12-01 22:03:51 +00:00
|
|
|
try:
|
|
|
|
|
generator = self._generators[id]
|
|
|
|
|
except KeyError:
|
2013-01-24 03:46:57 +00:00
|
|
|
raise NotRegistered('The generator with id %s is not'
|
2012-12-01 22:03:51 +00:00
|
|
|
' registered' % id)
|
|
|
|
|
if callable(generator):
|
|
|
|
|
return generator(**kwargs)
|
|
|
|
|
else:
|
|
|
|
|
return generator
|
|
|
|
|
|
|
|
|
|
def get_ids(self):
|
2013-08-15 02:58:36 +00:00
|
|
|
autodiscover()
|
2012-12-01 22:03:51 +00:00
|
|
|
return self._generators.keys()
|
|
|
|
|
|
2013-05-10 08:39:46 +00:00
|
|
|
def content_required_receiver(self, sender, file, **kwargs):
|
|
|
|
|
self._receive(file, 'on_content_required')
|
|
|
|
|
|
|
|
|
|
def existence_required_receiver(self, sender, file, **kwargs):
|
|
|
|
|
self._receive(file, 'on_existence_required')
|
|
|
|
|
|
|
|
|
|
def _receive(self, file, callback):
|
2013-01-29 06:40:00 +00:00
|
|
|
generator = file.generator
|
2013-01-31 09:07:57 +00:00
|
|
|
|
|
|
|
|
# FIXME: I guess this means you can't register functions?
|
|
|
|
|
if generator.__class__ in self._generators.values():
|
2013-01-29 06:40:00 +00:00
|
|
|
# Only invoke the strategy method for registered generators.
|
2013-05-10 08:39:46 +00:00
|
|
|
call_strategy_method(file, callback)
|
2013-01-29 06:40:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class SourceGroupRegistry(object):
|
|
|
|
|
"""
|
|
|
|
|
The source group registry is responsible for listening to source_* signals
|
2013-01-31 15:03:42 +00:00
|
|
|
on source groups, and relaying them to the image generated file strategies
|
|
|
|
|
of the appropriate generators.
|
2013-01-29 06:40:00 +00:00
|
|
|
|
2013-01-31 08:51:29 +00:00
|
|
|
In addition, registering a new source group also registers its generated
|
|
|
|
|
files with that registry.
|
2013-01-29 06:40:00 +00:00
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
_signals = {
|
2013-05-25 03:21:30 +00:00
|
|
|
source_saved: 'on_source_saved',
|
2013-01-29 06:40:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self._source_groups = {}
|
|
|
|
|
for signal in self._signals.keys():
|
|
|
|
|
signal.connect(self.source_group_receiver)
|
|
|
|
|
|
|
|
|
|
def register(self, generator_id, source_group):
|
2013-01-31 08:51:29 +00:00
|
|
|
from .specs.sourcegroups import SourceGroupFilesGenerator
|
2013-01-29 06:40:00 +00:00
|
|
|
generator_ids = self._source_groups.setdefault(source_group, set())
|
|
|
|
|
generator_ids.add(generator_id)
|
2013-02-05 00:39:25 +00:00
|
|
|
cachefile_registry.register(generator_id,
|
2013-01-31 08:51:29 +00:00
|
|
|
SourceGroupFilesGenerator(source_group, generator_id))
|
2013-01-29 06:40:00 +00:00
|
|
|
|
|
|
|
|
def unregister(self, generator_id, source_group):
|
2013-01-31 08:51:29 +00:00
|
|
|
from .specs.sourcegroups import SourceGroupFilesGenerator
|
2013-01-29 06:40:00 +00:00
|
|
|
generator_ids = self._source_groups.setdefault(source_group, set())
|
|
|
|
|
if generator_id in generator_ids:
|
|
|
|
|
generator_ids.remove(generator_id)
|
2013-02-05 00:39:25 +00:00
|
|
|
cachefile_registry.unregister(generator_id,
|
2013-01-31 08:51:29 +00:00
|
|
|
SourceGroupFilesGenerator(source_group, generator_id))
|
2013-01-29 06:40:00 +00:00
|
|
|
|
|
|
|
|
def source_group_receiver(self, sender, source, signal, **kwargs):
|
|
|
|
|
"""
|
|
|
|
|
Relay source group signals to the appropriate spec strategy.
|
|
|
|
|
|
|
|
|
|
"""
|
2013-02-08 23:15:00 +00:00
|
|
|
from .cachefiles import ImageCacheFile
|
2013-01-29 06:40:00 +00:00
|
|
|
source_group = sender
|
|
|
|
|
|
|
|
|
|
# Ignore signals from unregistered groups.
|
|
|
|
|
if source_group not in self._source_groups:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
specs = [generator_registry.get(id, source=source) for id in
|
|
|
|
|
self._source_groups[source_group]]
|
|
|
|
|
callback_name = self._signals[signal]
|
|
|
|
|
|
|
|
|
|
for spec in specs:
|
2013-02-08 23:15:00 +00:00
|
|
|
file = ImageCacheFile(spec)
|
2013-05-10 04:51:47 +00:00
|
|
|
call_strategy_method(file, callback_name)
|
2013-01-29 06:40:00 +00:00
|
|
|
|
2012-12-01 22:03:51 +00:00
|
|
|
|
2013-02-05 00:39:25 +00:00
|
|
|
class CacheFileRegistry(object):
|
2012-12-01 22:03:51 +00:00
|
|
|
"""
|
2013-01-31 08:51:29 +00:00
|
|
|
An object for registering generated files with image generators. The two are
|
2012-12-01 22:03:51 +00:00
|
|
|
associated with each other via a string id. We do this (as opposed to
|
2013-02-05 00:39:25 +00:00
|
|
|
associating them directly by, for example, putting a ``cachefiles``
|
2013-01-31 08:51:29 +00:00
|
|
|
attribute on image generators) so that image generators can be overridden
|
|
|
|
|
without losing the associated files. That way, a distributable app can
|
|
|
|
|
define its own generators without locking the users of the app into it.
|
2012-12-01 22:03:51 +00:00
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
2013-02-05 00:39:25 +00:00
|
|
|
self._cachefiles = {}
|
2012-12-01 22:03:51 +00:00
|
|
|
|
2013-02-05 00:39:25 +00:00
|
|
|
def register(self, generator_id, cachefiles):
|
2012-12-01 22:03:51 +00:00
|
|
|
"""
|
2013-01-31 08:51:29 +00:00
|
|
|
Associates generated files with a generator id
|
2012-12-01 22:03:51 +00:00
|
|
|
|
|
|
|
|
"""
|
2013-02-05 00:39:25 +00:00
|
|
|
if cachefiles not in self._cachefiles:
|
|
|
|
|
self._cachefiles[cachefiles] = set()
|
|
|
|
|
self._cachefiles[cachefiles].add(generator_id)
|
2012-12-01 22:03:51 +00:00
|
|
|
|
2013-02-05 00:39:25 +00:00
|
|
|
def unregister(self, generator_id, cachefiles):
|
2012-12-01 22:03:51 +00:00
|
|
|
"""
|
2013-01-31 08:51:29 +00:00
|
|
|
Disassociates generated files with a generator id
|
2012-12-01 22:03:51 +00:00
|
|
|
|
|
|
|
|
"""
|
2013-01-24 05:04:43 +00:00
|
|
|
try:
|
2013-02-05 00:39:25 +00:00
|
|
|
self._cachefiles[cachefiles].remove(generator_id)
|
2013-01-24 05:04:43 +00:00
|
|
|
except KeyError:
|
|
|
|
|
pass
|
2012-12-01 22:03:51 +00:00
|
|
|
|
2013-01-24 03:46:57 +00:00
|
|
|
def get(self, generator_id):
|
2013-02-05 00:39:25 +00:00
|
|
|
for k, v in self._cachefiles.items():
|
2013-01-24 05:04:43 +00:00
|
|
|
if generator_id in v:
|
2013-01-31 08:51:29 +00:00
|
|
|
for file in k():
|
|
|
|
|
yield file
|
2012-12-01 22:03:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class Register(object):
|
|
|
|
|
"""
|
2013-01-31 08:51:29 +00:00
|
|
|
Register generators and generated files.
|
2012-12-01 22:03:51 +00:00
|
|
|
|
|
|
|
|
"""
|
2013-01-24 03:46:57 +00:00
|
|
|
def generator(self, id, generator=None):
|
|
|
|
|
if generator is None:
|
2012-12-04 03:49:32 +00:00
|
|
|
# Return a decorator
|
|
|
|
|
def decorator(cls):
|
2013-01-24 03:46:57 +00:00
|
|
|
self.generator(id, cls)
|
2012-12-04 03:49:32 +00:00
|
|
|
return cls
|
|
|
|
|
return decorator
|
|
|
|
|
|
2013-01-24 03:46:57 +00:00
|
|
|
generator_registry.register(id, generator)
|
2012-12-01 22:03:51 +00:00
|
|
|
|
2013-01-24 03:46:57 +00:00
|
|
|
# iterable that returns kwargs or callable that returns iterable of kwargs
|
2013-02-05 00:39:25 +00:00
|
|
|
def cachefiles(self, generator_id, cachefiles):
|
|
|
|
|
cachefile_registry.register(generator_id, cachefiles)
|
2012-12-01 22:03:51 +00:00
|
|
|
|
2013-01-29 06:40:00 +00:00
|
|
|
def source_group(self, generator_id, source_group):
|
|
|
|
|
source_group_registry.register(generator_id, source_group)
|
|
|
|
|
|
2012-12-01 22:03:51 +00:00
|
|
|
|
|
|
|
|
class Unregister(object):
|
|
|
|
|
"""
|
2013-01-31 08:51:29 +00:00
|
|
|
Unregister generators and generated files.
|
2012-12-01 22:03:51 +00:00
|
|
|
|
|
|
|
|
"""
|
2013-04-05 20:41:53 +00:00
|
|
|
def generator(self, id):
|
|
|
|
|
generator_registry.unregister(id)
|
2012-12-01 22:03:51 +00:00
|
|
|
|
2013-02-05 00:39:25 +00:00
|
|
|
def cachefiles(self, generator_id, cachefiles):
|
|
|
|
|
cachefile_registry.unregister(generator_id, cachefiles)
|
2012-12-01 22:03:51 +00:00
|
|
|
|
2013-01-29 06:40:00 +00:00
|
|
|
def source_group(self, generator_id, source_group):
|
|
|
|
|
source_group_registry.unregister(generator_id, source_group)
|
|
|
|
|
|
2012-12-01 22:03:51 +00:00
|
|
|
|
|
|
|
|
generator_registry = GeneratorRegistry()
|
2013-02-05 00:39:25 +00:00
|
|
|
cachefile_registry = CacheFileRegistry()
|
2013-01-29 06:40:00 +00:00
|
|
|
source_group_registry = SourceGroupRegistry()
|
2012-12-01 22:03:51 +00:00
|
|
|
register = Register()
|
|
|
|
|
unregister = Unregister()
|