def cached_method(f: Callable[(..., Any)]) -> Callable[(..., Any)]:
    "Caches method calls based on the call's `args` and `kwargs`.\n\n    Works for `async` and `sync` methods. Don't apply this to functions.\n\n    Args:\n        f: The decorated method whose return value should be cached.\n\n    Returns:\n        The return value which the method gives for the first call with the given\n        arguments.\n    "
    assert ('self' in arguments_of(f)), 'This decorator can only be used with methods.'

    class Cache():
        'Helper class to abstract the caching details.'

        def __init__(self, caching_object: object, args: Any, kwargs: Any) -> None:
            self.caching_object = caching_object
            self.cache = getattr(caching_object, self._cache_name(), {})
            self.cache_key = functools._make_key(args, kwargs, typed=False)

        def _cache_name(self) -> Text:
            return f'_cached_{self.caching_object.__class__.__name__}_{f.__name__}'

        def is_cached(self) -> bool:
            return (self.cache_key in self.cache)

        def cache_result(self, result: Any) -> None:
            self.cache[self.cache_key] = result
            setattr(self.caching_object, self._cache_name(), self.cache)

        def cached_result(self) -> Any:
            return self.cache[self.cache_key]
    if asyncio.iscoroutinefunction(f):

        @functools.wraps(f)
        async def decorated(self: object, *args: Any, **kwargs: Any) -> Any:
            cache = Cache(self, args, kwargs)
            if (not cache.is_cached()):
                to_cache = asyncio.ensure_future(f(self, *args, **kwargs))
                cache.cache_result(to_cache)
            return (await cache.cached_result())
        return decorated
    else:

        @functools.wraps(f)
        def decorated(self: object, *args: Any, **kwargs: Any) -> Any:
            cache = Cache(self, args, kwargs)
            if (not cache.is_cached()):
                to_cache = f(self, *args, **kwargs)
                cache.cache_result(to_cache)
            return cache.cached_result()
        return decorated
Caches method calls based on the call's `args` and `kwargs`.

Works for `async` and `sync` methods. Don't apply this to functions.

Args:
    f: The decorated method whose return value should be cached.

Returns:
    The return value which the method gives for the first call with the given
    arguments.
def cached_method(f: Callable[(..., Any)]) -> Callable[(..., Any)]:
    "Caches method calls based on the call's `args` and `kwargs`.\n\n    Works for `async` and `sync` methods. Don't apply this to functions.\n\n    Args:\n        f: The decorated method whose return value should be cached.\n\n    Returns:\n        The return value which the method gives for the first call with the given\n        arguments.\n    "
    assert ('self' in arguments_of(f)), 'This decorator can only be used with methods.'

    class Cache():
        'Helper class to abstract the caching details.'

        def __init__(self, caching_object: object, args: Any, kwargs: Any) -> None:
            self.caching_object = caching_object
            self.cache = getattr(caching_object, self._cache_name(), {})
            self.cache_key = functools._make_key(args, kwargs, typed=False)

        def _cache_name(self) -> Text:
            return f'_cached_{self.caching_object.__class__.__name__}_{f.__name__}'

        def is_cached(self) -> bool:
            return (self.cache_key in self.cache)

        def cache_result(self, result: Any) -> None:
            self.cache[self.cache_key] = result
            setattr(self.caching_object, self._cache_name(), self.cache)

        def cached_result(self) -> Any:
            return self.cache[self.cache_key]
    if asyncio.iscoroutinefunction(f):

        @functools.wraps(f)
        async def decorated(self: object, *args: Any, **kwargs: Any) -> Any:
            cache = Cache(self, args, kwargs)
            if (not cache.is_cached()):
                to_cache = asyncio.ensure_future(f(self, *args, **kwargs))
                cache.cache_result(to_cache)
            return (await cache.cached_result())
        return decorated
    else:

        @functools.wraps(f)
        def decorated(self: object, *args: Any, **kwargs: Any) -> Any:
            cache = Cache(self, args, kwargs)
            if (not cache.is_cached()):
                to_cache = f(self, *args, **kwargs)
                cache.cache_result(to_cache)
            return cache.cached_result()
        return decorated