jasily/data/prop.py
def cache(descriptor=None, *, store: IStore = None):
    '''
    usage:

    ``` py
    @cache
    @property
    def name(self): pass
    ```
    '''

    if descriptor is None:
        return functools.partial(cache, store=store)

    hasattrs = {
        'get': hasattr(descriptor, '__get__'),
        'set': hasattr(descriptor, '__set__'),
        'del': hasattr(descriptor, '__delete__')
    }

    descriptor_name = get_descriptor_name(descriptor)

    # pylint: disable=R0903,C0111
    class CacheDescriptor(ICacheDescriptor):
        def __init__(self):
            if descriptor_name is not None:
                self.__name__ = descriptor_name

    cache_descriptor = CacheDescriptor()

    if store is None:
        store = FieldStore(cache_descriptor)
    elif not isinstance(store, IStore):
        raise TypeError(f'store must be a {IStore}.')

    if hasattrs['get']:
        def get(self, obj, objtype):
            if obj is None:
                return descriptor.__get__(obj, objtype)
            value = store.get(self, obj, defval=NOVALUE)
            if value is NOVALUE:
                value = descriptor.__get__(obj, objtype)
                store.set(self, obj, value)
            return value

        CacheDescriptor.__get__ = get

    if hasattrs['set']:
        def set(self, obj, value):
            store.pop(self, obj)
            descriptor.__set__(obj, value)

        CacheDescriptor.__set__ = set

    if hasattrs['del']:
        def delete(self, obj):
            store.pop(self, obj)
            descriptor.__delete__(obj)

        CacheDescriptor.__delete__ = delete

    return cache_descriptor
def cache(descriptor=None, *, store: IStore = None):
    '''
    usage:

    ``` py
    @cache
    @property
    def name(self): pass
    ```
    '''

    if descriptor is None:
        return functools.partial(cache, store=store)

    hasattrs = {
        'get': hasattr(descriptor, '__get__'),
        'set': hasattr(descriptor, '__set__'),
        'del': hasattr(descriptor, '__delete__')
    }

    descriptor_name = get_descriptor_name(descriptor)

    # pylint: disable=R0903,C0111
    class CacheDescriptor(ICacheDescriptor):
        def __init__(self):
            if descriptor_name is not None:
                self.__name__ = descriptor_name

    cache_descriptor = CacheDescriptor()

    if store is None:
        store = FieldStore(cache_descriptor)
    elif not isinstance(store, IStore):
        raise TypeError(f'store must be a {IStore}.')

    if hasattrs['get']:
        def get(self, obj, objtype):
            if obj is None:
                return descriptor.__get__(obj, objtype)
            value = store.get(self, obj, defval=NOVALUE)
            if value is NOVALUE:
                value = descriptor.__get__(obj, objtype)
                store.set(self, obj, value)
            return value

        CacheDescriptor.__get__ = get

    if hasattrs['set']:
        def set(self, obj, value):
            store.pop(self, obj)
            descriptor.__set__(obj, value)

        CacheDescriptor.__set__ = set

    if hasattrs['del']:
        def delete(self, obj):
            store.pop(self, obj)
            descriptor.__delete__(obj)

        CacheDescriptor.__delete__ = delete

    return cache_descriptor
usage:

    ``` py
    @cache
    @property
    def name(self): pass
    ```