From 3988a620a866fb00e9f11cd84c79ad1459618b79 Mon Sep 17 00:00:00 2001 From: Yusur Princeps Date: Sun, 31 May 2026 09:43:51 +0200 Subject: [PATCH] 0.13.0a2 add functools.cooldown() --- CHANGELOG.md | 3 ++- src/suou/__init__.py | 2 +- src/suou/functools.py | 45 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a30c9ea..8922767 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,10 @@ ## 0.13.0 + Added module `argparse` with class `LetterSubparsers()` -+ module `sqlalchemy`: ++ Module `sqlalchemy`: * removed deprecated alias `entity_base()`. use `declarative_base()` instead. * fix imports. ++ Module `functools`: add `cooldown()` ## 0.12.6 diff --git a/src/suou/__init__.py b/src/suou/__init__.py index d28099f..c90e37c 100644 --- a/src/suou/__init__.py +++ b/src/suou/__init__.py @@ -39,7 +39,7 @@ from .color import OKLabColor, chalk, WebColor, RGBColor, LinearRGBColor, XYZCol from .mat import Matrix from .argparse import LetterSubparsers -__version__ = "0.13.0a1" +__version__ = "0.13.0a2" __all__ = ( 'ConfigOptions', 'ConfigParserConfigSource', 'ConfigSource', 'ConfigValue', diff --git a/src/suou/functools.py b/src/suou/functools.py index e72b689..dafd0d6 100644 --- a/src/suou/functools.py +++ b/src/suou/functools.py @@ -339,6 +339,49 @@ def none_pass(func: Callable[_T, _U], *args, **kwargs) -> Callable[_T, _U]: return func(x, *args, **kwargs) return wrapper +def cooldown(unit: int, /, exception: Exception | None = None): + ''' + Implement a calling cooldown for a function of procedure. + If the decorated function is called during the cooldown, + the last result is returned (or occasionally an exception). + + If an exception is passed explicitly as a decorator, it is + raised upon calling during cooldown. + + Otherwise, the last result is returned (or the last + exception is raised.) + + *New in 0.13.0* + ''' + def decorator(func: Callable[..., _U]): + @wraps(func) + def wrapper(*args, **kwargs): + now = time.time() + if wrapper.timeout_until is not None and wrapper.timeout_until > now: + if exception is not None: + raise exception + elif wrapper.last_exc is not None: + raise wrapper.last_exc + else: + return wrapper.last_result + else: + wrapper.timeout_until = now + unit + try: + wrapper.last_result = func(*args, **kwargs) + except Exception as e: + wrapper.last_exc = e + raise + else: + wrapper.last_exc = None + return wrapper.last_result + + wrapper.last_result: _U | None = None + wrapper.last_exc: Exception | None = None + wrapper.timeout_until: float | None = None + + return wrapper + return decorator + __all__ = ( - 'deprecated', 'not_implemented', 'timed_cache', 'none_pass', 'alru_cache' + 'deprecated', 'not_implemented', 'timed_cache', 'none_pass', 'alru_cache', 'cooldown' )