manager: handle many forms of path in notify_path_change
- ID
3e11553- date
2026-11-06 23:02:36+00:00- author
Alex Chan <alexc@tailscale.com>- parent
b919468- message
manager: handle many forms of path in notify_path_change Allow passing paths in a singleton, list, tuple, or set, and allow each path to be a str or a Path. This makes the types less "pure" but is more convenient for callers.- changed files
2 files, 27 additions, 6 deletions
Changed files
src/funkycache/manager.py (11579 → 11691)
diff --git a/src/funkycache/manager.py b/src/funkycache/manager.py
index d7c7cfc..aedbe63 100644
--- a/src/funkycache/manager.py
+++ b/src/funkycache/manager.py
@@ -5,7 +5,7 @@ Ensure each function has an independent cache.
"""
import atexit
-from collections.abc import Callable
+from collections.abc import Callable, Sequence
from dataclasses import dataclass
from functools import wraps
import inspect
@@ -232,12 +232,15 @@ class FunkyCache:
case _:
pass
- def notify_path_change(self, changed_paths: Path | list[Path]) -> None:
+ def notify_path_change(
+ self, changed_paths: Path | str | Sequence[Path | str]
+ ) -> None:
"""
Invalidate cache that depend on this path and/or paths.
"""
- if not isinstance(changed_paths, list):
+ if isinstance(changed_paths, (Path, str)):
changed_paths = [changed_paths]
+ changed_paths: list[Path] = [Path(p) for p in changed_paths]
for name, known_cache in self._known_caches.items():
# If this cache isn't affected by any of the paths that
tests/test_manager.py (19451 → 20246)
diff --git a/tests/test_manager.py b/tests/test_manager.py
index 662674f..6b3b801 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -3,7 +3,7 @@ Tests for the FunkyCache cache manager.
"""
from abc import ABC, abstractmethod
-from collections.abc import Iterator, Mapping
+from collections.abc import Callable, Iterator, Mapping, Sequence
from collections import Counter
import inspect
from pathlib import Path
@@ -116,8 +116,26 @@ class FunkyCacheTestSuite(ABC):
def double(n: int) -> int: # pragma: no cover
return n * 2
+ # path_modifier captures all the semantically equivalent ways
+ # to pass a single path to notify_path_change.
+ @pytest.mark.parametrize(
+ "path_modifier",
+ [
+ pytest.param(lambda p: p, id="single-path"),
+ pytest.param(lambda p: str(p), id="single-str"),
+ pytest.param(lambda p: [p], id="list-path"),
+ pytest.param(lambda p: [str(p)], id="list-str"),
+ pytest.param(lambda p: (p,), id="tuple-path"),
+ pytest.param(lambda p: (str(p),), id="tuple-str"),
+ pytest.param(lambda p: {p}, id="s-path"),
+ pytest.param(lambda p: {str(p)}, id="s-str"),
+ ],
+ )
def test_cache_invalidated_on_path_change(
- self, cache: FunkyCache, tmp_path: Path
+ self,
+ cache: FunkyCache,
+ tmp_path: Path,
+ path_modifier: Callable[[Path], Path | str | Sequence[Path | str]],
) -> None:
"""
If a function depends on a path and the path changes, the cache
@@ -144,7 +162,7 @@ class FunkyCacheTestSuite(ABC):
# Change the underlying file
p.write_text("bonjour monde")
- cache.notify_path_change([p])
+ cache.notify_path_change(path_modifier(p))
# The third call is a cache miss; goes to the real function
assert initial("apple") == "A"