from collections.abc import Callable
from functools import lru_cache
from typing import TypeVar


_RT = TypeVar("_RT")


class Freezable:
  _frozen: bool = False

  def freeze(self):
    if not self._frozen:
      self._frozen = True

  def __setattr__(self, *args, **kwargs):
    if self._frozen:
      raise Exception("cannot modify frozen object")
    super().__setattr__(*args, **kwargs)


def cache(user_function: Callable[..., _RT], /) -> Callable[..., _RT]:
  return lru_cache(maxsize=None)(user_function)