Source code for jacinle.utils.cache

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# File   : cache.py
# Author : Jiayuan Mao
# Email  : maojiayuan@gmail.com
# Date   : 01/19/2018
#
# This file is part of Jacinle.
# Distributed under terms of the MIT license.

import functools
import collections
import os.path as osp
import threading
from typing import Callable

from jacinle.logging import get_logger
from jacinle.utils.meta import synchronized

logger = get_logger(__file__)

__all__ = ['cached_property', 'cached_result', 'fs_cached_result']


class _cached_property_v1:
    """A decorator that converts a function into a cached property. Similar to ``@property``, but the function result is cached.
    This function has been deprecated. Please use :func:`cached_property` instead.
    """

    def __init__(self, fget):
        self.fget = fget
        self.__module__ = fget.__module__
        self.__name__ = fget.__name__
        self.__doc__ = fget.__doc__
        self.__cache_key = '__result_cache_{}_{}'.format(
            fget.__name__, id(fget))
        self.__mutex = collections.defaultdict(threading.Lock)

    def __get__(self, instance, owner):
        with self.__mutex[id(instance)]:
            if instance is None:
                return self.fget
            v = getattr(instance, self.__cache_key, None)
            if v is not None:
                return v
            v = self.fget(instance)
            assert v is not None
            setattr(instance, self.__cache_key, v)
            return v


[docs] def cached_property(fget): """A decorator that converts a function into a cached property. Similar to ``@property``, but the function result is cached. This function has threading lock support.""" mutex = collections.defaultdict(threading.Lock) cache = dict() def impl(self): nonlocal impl id_ = id(self) with mutex[id_]: if id_ not in cache: cache[id_] = fget(self) return cache[id_] else: return cache[id_] return property(impl)
[docs] def cached_result(func): """A decorator that caches the result of a function. Note that this decorator does not support any arguments to the function.""" def impl(): nonlocal impl ret = func() impl = lambda: ret return ret @synchronized() @functools.wraps(func) def f(): return impl() return f
[docs] def fs_cached_result(filename: str, force_update: bool = False, verbose: bool = False) -> Callable[[Callable], Callable]: """A decorator that caches the result of a function into a file. Note that this decorator does not take care of any arguments to the function. Args: filename: the filename to store the result. force_update: if True, the result will be updated even if the file exists. verbose: if True, the filenames will be printed to the console. Returns: a decorator function. """ import jacinle.io as io def wrapper(func): @synchronized() @functools.wraps(func) def wrapped_func(*args, **kwargs): if not force_update and osp.exists(filename): if verbose: logger.info('Using cached results from "{}".'.format(filename)) cached_value = io.load(filename) if cached_value is not None: return cached_value computed_value = func(*args, **kwargs) if verbose: logger.info('Writing result cache to "{}".'.format(filename)) io.dump(filename, computed_value) return computed_value return wrapped_func return wrapper