1
0

decorator.py 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. """Decorator utilities."""
  2. import functools
  3. from typing import Callable, ParamSpec, TypeVar
  4. T = TypeVar("T")
  5. def once(f: Callable[[], T]) -> Callable[[], T]:
  6. """A decorator that calls the function once and caches the result.
  7. Args:
  8. f: The function to call.
  9. Returns:
  10. A function that calls the function once and caches the result.
  11. """
  12. unset = object()
  13. value: object | T = unset
  14. @functools.wraps(f)
  15. def wrapper() -> T:
  16. nonlocal value
  17. value = f() if value is unset else value
  18. return value # pyright: ignore[reportReturnType]
  19. return wrapper
  20. def once_unless_none(f: Callable[[], T | None]) -> Callable[[], T | None]:
  21. """A decorator that calls the function once and caches the result unless it is None.
  22. Args:
  23. f: The function to call.
  24. Returns:
  25. A function that calls the function once and caches the result unless it is None.
  26. """
  27. value: T | None = None
  28. @functools.wraps(f)
  29. def wrapper() -> T | None:
  30. nonlocal value
  31. value = f() if value is None else value
  32. return value
  33. return wrapper
  34. P = ParamSpec("P")
  35. def debug(f: Callable[P, T]) -> Callable[P, T]:
  36. """A decorator that prints the function name, arguments, and result.
  37. Args:
  38. f: The function to call.
  39. Returns:
  40. A function that prints the function name, arguments, and result.
  41. """
  42. @functools.wraps(f)
  43. def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
  44. result = f(*args, **kwargs)
  45. print( # noqa: T201
  46. f"Calling {f.__name__} with args: {args} and kwargs: {kwargs}, result: {result}"
  47. )
  48. return result
  49. return wrapper