@@ -41,6 +41,12 @@ def decorator(fn: Any) -> Any:
4141 # Capture signature once at decoration time (P2 — avoids per-call overhead).
4242 sig = inspect .signature (fn )
4343
44+ # Sanitize fn.__qualname__ for use in filenames: nested-defined functions
45+ # produce names like "outer.<locals>.fetch" which contain "<" and ">",
46+ # both illegal on Windows (NTFS reserved characters → ENOTSUP/EINVAL on
47+ # write). Stripping them keeps cache keys portable across platforms.
48+ safe_qualname = fn .__qualname__ .replace ("<" , "_" ).replace (">" , "_" )
49+
4450 def _get_sublayer_dir () -> Path :
4551 # Always re-resolve so set_cache_dir() takes effect even if the
4652 # listener registry has been cleared by a test fixture or other
@@ -53,7 +59,7 @@ def _make_cache_key(*args: Any, **kwargs: Any) -> str:
5359 key_repr = repr (sorted (bound .arguments .items ()))
5460 digest = hashlib .sha256 (key_repr .encode ()).hexdigest ()[:16 ]
5561 module = fn .__module__ or ""
56- return f"{ module } .{ fn . __qualname__ } __{ digest } "
62+ return f"{ module } .{ safe_qualname } __{ digest } "
5763
5864 def _cache_path (key : str , result : Any ) -> Path :
5965 ext = ".parquet" if isinstance (result , pd .DataFrame ) else ".pkl"
@@ -91,7 +97,7 @@ def _do_cache_clear() -> None:
9197 if not d .exists ():
9298 return
9399 module = fn .__module__ or ""
94- prefix = f"{ module } .{ fn . __qualname__ } __"
100+ prefix = f"{ module } .{ safe_qualname } __"
95101 for p in d .iterdir ():
96102 if p .name .startswith (prefix ):
97103 p .unlink (missing_ok = True )
0 commit comments