diff --git a/rose/config.py b/rose/config.py index 1763426..83512fe 100644 --- a/rose/config.py +++ b/rose/config.py @@ -22,7 +22,7 @@ class MissingConfigKeyError(RoseError): pass -@dataclass +@dataclass(frozen=True) class Config: music_source_dir: Path fuse_mount_dir: Path diff --git a/rose/virtualfs.py b/rose/virtualfs.py index 42d7077..4afe48d 100644 --- a/rose/virtualfs.py +++ b/rose/virtualfs.py @@ -1,4 +1,5 @@ import errno +import functools import logging import os import stat @@ -36,27 +37,35 @@ def __init__(self, config: Config): def getattr(self, path: str, _: int) -> dict[str, Any]: logger.debug(f"Received getattr for {path}") + return self._cached_getattr(self.config, path) + + @staticmethod + @functools.lru_cache(maxsize=69696) + def _cached_getattr(config: Config, path: str) -> dict[str, Any]: + # We cache the getattr call with lru_cache because this is called _extremely_ often. Like + # for every node that we see in the output of `ls`. + logger.debug(f"Recomputing uncached getattr for {path}") p = parse_virtual_path(path) logger.debug(f"Parsed getattr path as {p}") if p.view == "root": return mkstat("dir") elif p.album and p.file: - if tp := track_exists(self.config, p.album, p.file): + if tp := track_exists(config, p.album, p.file): return mkstat("file", tp) - if cp := cover_exists(self.config, p.album, p.file): + if cp := cover_exists(config, p.album, p.file): return mkstat("file", cp) elif p.album: - if release_exists(self.config, p.album): - return mkstat("dir") + if rp := release_exists(config, p.album): + return mkstat("dir", rp) elif p.artist: - if artist_exists(self.config, p.artist): + if artist_exists(config, p.artist): return mkstat("dir") elif p.genre: - if genre_exists(self.config, p.genre): + if genre_exists(config, p.genre): return mkstat("dir") elif p.label: - if label_exists(self.config, p.label): + if label_exists(config, p.label): return mkstat("dir") else: return mkstat("dir")