diff --git a/cachebox/_cachebox.py b/cachebox/_cachebox.py index def0a30..9beaff0 100644 --- a/cachebox/_cachebox.py +++ b/cachebox/_cachebox.py @@ -161,16 +161,15 @@ def __init__( ) self._thread: threading.Thread | None = None - self._thread_is_running: bool = False + self._stop_event = threading.Event() if sweep_interval is not None: if isinstance(sweep_interval, timedelta): sweep_interval = sweep_interval.total_seconds() if sweep_interval < 1: - raise ValueError("sweep_interval must be more than 1 seconds.") + raise ValueError("sweep_interval must be at least 1 second.") - self._thread_is_running = True self._thread = threading.Thread( target=self._sweeper_thread, args=(sweep_interval,), @@ -185,14 +184,13 @@ def sweep_interval(self) -> float | None: """The configured ``sweep_interval`` in seconds.""" return self._sweep_interval - def _sweeper_thread(self, interval: float): - while self._thread_is_running: - time.sleep(interval) + def _sweeper_thread(self, interval: float) -> None: + while not self._stop_event.wait(interval): self.expire() def stop_sweeper(self) -> None: """Signals the background sweeper thread to stop, if one is active.""" - self._thread_is_running = False + self._stop_event.set() def __del__(self) -> None: self.stop_sweeper() @@ -333,16 +331,15 @@ def __init__( ) self._thread: threading.Thread | None = None - self._thread_is_running: bool = False + self._stop_event = threading.Event() if sweep_interval is not None: if isinstance(sweep_interval, timedelta): sweep_interval = sweep_interval.total_seconds() if sweep_interval < 1: - raise ValueError("sweep_interval must be more than 1 seconds.") + raise ValueError("sweep_interval must be at least 1 second.") - self._thread_is_running = True self._thread = threading.Thread( target=self._sweeper_thread, args=(sweep_interval,), @@ -357,14 +354,13 @@ def sweep_interval(self) -> float | None: """The configured ``sweep_interval`` in seconds.""" return self._sweep_interval - def _sweeper_thread(self, interval: float): - while self._thread_is_running: - time.sleep(interval) + def _sweeper_thread(self, interval: float) -> None: + while not self._stop_event.wait(interval): self.expire() def stop_sweeper(self) -> None: """Signals the background sweeper thread to stop, if one is active.""" - self._thread_is_running = False + self._stop_event.set() def __del__(self) -> None: self.stop_sweeper() diff --git a/cachebox/_wrappers.py b/cachebox/_wrappers.py index 14c4a8f..ed81393 100644 --- a/cachebox/_wrappers.py +++ b/cachebox/_wrappers.py @@ -2,12 +2,14 @@ import typing from collections import namedtuple from contextlib import AbstractAsyncContextManager, AbstractContextManager +from collections.abc import Callable, Hashable from cachebox._core import BaseCacheImpl, Cache -_PostProcess: typing.TypeAlias = typing.Callable[[typing.Any], typing.Any] -_Callback: typing.TypeAlias = typing.Callable[[int, typing.Any, typing.Any], typing.Any] - +_PostProcess: typing.TypeAlias = Callable[[typing.Any], typing.Any] +_Callback: typing.TypeAlias = Callable[ + [int, typing.Any, typing.Any], typing.Any +] class _Lock: __slots__ = ("_lock", "waiters") @@ -65,10 +67,10 @@ def _create_cache_info(cache, hits, misses) -> CacheInfo: def _cached_wrapper_without_lock( func, - cache: BaseCacheImpl | typing.Callable, - key_maker: typing.Callable[[tuple, dict], typing.Hashable], + cache: BaseCacheImpl | Callable, + key_maker: Callable[[tuple, dict], Hashable], clear_reuse: bool, - callback: typing.Callable[[int, typing.Any, typing.Any], None] | None, + callback: Callable[[int, typing.Any, typing.Any], None] | None, postprocess: _PostProcess | None, ): cache_is_fn = callable(cache) @@ -139,10 +141,10 @@ async def _call_async_callback(callback, event, key, result): def _async_cached_wrapper_without_lock( func, - cache: BaseCacheImpl | typing.Callable, - key_maker: typing.Callable[[tuple, dict], typing.Hashable], + cache: BaseCacheImpl | Callable, + key_maker: Callable[[tuple, dict], Hashable], clear_reuse: bool, - callback: typing.Callable[[int, typing.Any, typing.Any], None] | None, + callback: Callable[[int, typing.Any, typing.Any], None] | None, postprocess: _PostProcess | None, ): cache_is_fn = callable(cache) @@ -179,7 +181,7 @@ async def _wrapped(*args, **kwds): result = await func(*args, **kwds) _cache.insert(key, result) - hits += 1 + misses += 1 await _call_async_callback(callback, EVENT_MISS, key, result) return postprocess(result) if postprocess is not None else result @@ -202,12 +204,12 @@ def cache_clear() -> None: def _cached_wrapper( func, - cache: BaseCacheImpl | typing.Callable, - key_maker: typing.Callable[[tuple, dict], typing.Hashable], + cache: BaseCacheImpl | Callable, + key_maker: Callable[[tuple, dict], Hashable], clear_reuse: bool, - callback: typing.Callable[[int, typing.Any, typing.Any], None] | None, + callback: Callable[[int, typing.Any, typing.Any], None] | None, postprocess: _PostProcess | None, - lock_type: typing.Type[AbstractContextManager], + lock_type: type[AbstractContextManager], ): cache_is_fn = callable(cache) @@ -221,8 +223,8 @@ def _cached_wrapper( hits = 0 misses = 0 - locks: Cache[typing.Hashable, _Lock] = Cache(0) - pending_errors: dict[typing.Hashable, BaseException] = {} + locks: Cache[Hashable, _Lock] = Cache(0) + pending_errors: dict[Hashable, BaseException] = {} def _wrapped(*args, **kwds): nonlocal hits, misses @@ -308,12 +310,12 @@ def cache_clear() -> None: def _async_cached_wrapper( func, - cache: BaseCacheImpl | typing.Callable, + cache: BaseCacheImpl | Callable, key_maker: typing.Callable[..., typing.Hashable], clear_reuse: bool, callback: _Callback | None, postprocess: _PostProcess | None, - lock_type: typing.Type[AbstractAsyncContextManager], + lock_type: type[AbstractAsyncContextManager], ): cache_is_fn = callable(cache) _make_key = (