Source code for everest.representers.urlloader
"""
URL lazy loader.
This file is part of the everest project.
See LICENSE.txt for licensing, CONTRIBUTORS.txt for contributor information.
Created on Apr 25, 2012.
"""
__docformat__ = 'reStructuredText en'
__all__ = ['LazyAttributeLoaderProxy',
'LazyUrlLoader',
]
[docs]class LazyUrlLoader(object):
"""
Helper class for lazy loading of URLs.
"""
def __init__(self, url, resolver):
self.__url = url
self.__resolver = resolver
def __call__(self):
return self.__resolver(self.__url)
[docs]class LazyAttributeLoaderProxy(object):
"""
Proxy for lazy loading of attributes referencing entities that are loaded
through a URL-linked resource.
"""
def __init__(self, _loader_map=None, **kw):
if _loader_map is None \
or not (isinstance(_loader_map, dict) and len(_loader_map) > 0):
raise ValueError('Must pass _loader_map dictionary containing '
'at least one attribute to load lazily.')
self._loader_map = _loader_map
super(LazyAttributeLoaderProxy, self).__init__(**kw)
def __getattribute__(self, attr):
attrs = object.__getattribute__(self, '__dict__')
if attr in attrs.get('_loader_map', ()):
# Setting this flag protects agains recursive loading.
loaded_attrs = self.__load()
try:
result = loaded_attrs[attr]
except KeyError:
# Loading failed - try again later.
result = object.__getattribute__(self, attr)
else:
result = object.__getattribute__(self, attr)
return result
@classmethod
[docs] def create(cls, entity_cls, data):
"""
Factory class method to create a lazy loader for entities linked
through resource URLs.
This returns an instance of a new dynamically created subtype of
the given entity class which also inherits from this class to add
the referenced entity attribute loading functionality. Once all
referenced entity attributes have been loaded successfully, the
instance's class is reverted to the given entity class.
"""
loader_map = {}
for attr, value in data.items():
if isinstance(value, LazyUrlLoader):
loader_map[attr] = value
data[attr] = None
if len(loader_map) > 0:
data['_loader_map'] = loader_map
new_type = type('%sLazyAttributeLoaderProxy' % entity_cls.__name__,
(cls, entity_cls), {})
ent = new_type.create_from_data(data)
else:
ent = entity_cls.create_from_data(data)
return ent
def __load(self):
loaded_attrs = dict()
loader_map = object.__getattribute__(self, '_loader_map')
for attr, loader in loader_map.items():
# To prevent recursive attempts to load the currently loading
# attribute, we remove it from the loader map.
del loader_map[attr]
try:
new_value = loader().get_entity()
except KeyError: # URL resolving (traversal) failed.
# Reinsert the attribute into the map for later loading.
loader_map[attr] = loader
else:
setattr(self, attr, new_value)
loaded_attrs[attr] = new_value
# Once all attributes are loaded successfully, we do not need the
# proxy any longer.
if len(loader_map) == 0:
self.__class__ = self.__class__.__bases__[-1]
delattr(self, '_loader_map')
return loaded_attrs