code: def cached_method(f: Callable[..., Any]) -> Callable[..., Any]:
      """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.
      """
      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(), {}
              )
              # noinspection PyUnresolvedReferences
              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():
                  # Store the task immediately so that other concurrent calls of the
                  # method can re-use the same task and don't schedule a second execution.
                  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
  
imports: import asyncio
  import functools
  import importlib
  import inspect
  import logging
  from typing import Text, Dict, Optional, Any, List, Callable, Collection, Type
  from rasa.shared.exceptions import RasaException
label_desc: Use image node_id 6 for calling a global function with example usage: cached_method(f) and returns: decorated, decorated, str, self, str, f, unknown, self, unknown, cache