Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 10 additions & 14 deletions cachebox/_cachebox.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,),
Expand All @@ -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()
Expand Down Expand Up @@ -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,),
Expand All @@ -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()
38 changes: 20 additions & 18 deletions cachebox/_wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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)

Expand All @@ -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
Expand Down Expand Up @@ -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 = (
Expand Down
Loading