Source code for planetary_coverage.misc.cache

"""Miscellaneous cache module."""

from .logger import logger


log_cache, debug_cache = logger('Cache Property')


def _get_registry(self):
    """Get object cache registry.

    Create a new register is not already exists
    and add clear cache function.

    """
    if not hasattr(self, '__cache_registry'):
        setattr(self, '__cache_registry', {})
        setattr(self, 'clear_cache', lambda: _clear_cache(self))

        log_cache.debug('`__cache_registry` and `clear_cache()` were added')

    return getattr(self, '__cache_registry')


def _register(self, attr, parents):
    """Register a property and its parents."""
    register = _get_registry(self)
    register[attr] = parents

    log_cache.debug('`%s` parent(s) are registered in the cache', attr)


def _clear_cache(self):
    """Clear all the cached properties."""
    for key in _get_registry(self):
        delattr(self, key)

    delattr(self, '__cache_registry')
    delattr(self, 'clear_cache')

    log_cache.debug('`__cache_registry` and `clear_cache()` were removed')
    log_cache.info('The cache is now clean')


def _clear_children(self, attr):
    """Clear property children from the cache."""
    for key, parents in _get_registry(self).items():
        if isinstance(parents, str) and attr == parents:
            delattr(self, key)

        elif isinstance(parents, (set, list, tuple)) and attr in parents:
            delattr(self, key)


[docs]def cached_property(method=None, *, parent=None): """Cached class property decorator attribute. Can be used in the following forms: - ``@cached_property`` - ``@cached_property()`` - ``@cached_property(parent='my_parent')`` - ``@cached_property(parent=('my_parent_1', ...))`` - ``@cached_property(parent=['my_parent_1', ...])`` - ``@cached_property(parent={'my_parent_1', ...})`` Parameters ---------- method: callable, optional Method to memoizes. parent: str or set or tuple or list List of parents cached properties, for garbage collection. Note ---- To remove a single cached attributed, use the :py:func:`delattr` function or the ``del`` keyword. In that case, all the child cached property will also be cleared. To clear the cache globally on this object, use the :py:func:`clear_cache()` method. Warning ------- This caching strategy is not thread-safe contrary to the functools method. """ def decorator(method): attr = method.__name__ prop = f'_{attr}_cached' def fget(self): try: value = getattr(self, prop) log_cache.info('`%s` is loaded from the cache', attr) except AttributeError: log_cache.info('`%s` is not in the cache', attr) # Load the value from the method value = method(self) log_cache.debug('`%s` value is %r', attr, value) # Cache the value setattr(self, prop, value) log_cache.info('`%s` is now saved in the cache', attr) # Register the attribute's parent(s) _register(self, attr, parent) return value def fdel(self): if hasattr(self, prop): delattr(self, prop) log_cache.info('`%s` was removed from the cache', attr) _clear_children(self, attr) doc = '[Cached] ' + method.__doc__ return property(fget, fdel=fdel, doc=doc) return decorator(method) if callable(method) else decorator