Async, sandboxed file system service with handlers, cache, and locks.
| 41 | |
| 42 | |
| 43 | class FileSystemService: |
| 44 | """Async, sandboxed file system service with handlers, cache, and locks.""" |
| 45 | |
| 46 | def __init__( |
| 47 | self, |
| 48 | base_dir: Union[str, Path], |
| 49 | *, |
| 50 | storage: Optional[StorageBackend] = None, |
| 51 | cache: Optional[LRUByteCache] = None, |
| 52 | ) -> None: |
| 53 | """Initialize the file system service. |
| 54 | |
| 55 | Args: |
| 56 | base_dir: Base directory for file operations |
| 57 | storage: Storage backend implementation |
| 58 | cache: Cache implementation for file content |
| 59 | """ |
| 60 | self._policy = PathPolicy(Path(base_dir) if isinstance(base_dir, str) else base_dir) |
| 61 | self._storage = storage or LocalAsyncStorage() |
| 62 | self._cache = cache or LRUByteCache() |
| 63 | self._locks = AsyncLockManager() |
| 64 | |
| 65 | self._handlers = HandlerRegistry() |
| 66 | # Register all handlers with priority order (more specific first) |
| 67 | self._handlers.register(XlsxHandler()) |
| 68 | self._handlers.register(DocxHandler()) |
| 69 | self._handlers.register(PdfHandler()) |
| 70 | self._handlers.register(PythonHandler()) |
| 71 | self._handlers.register(MarkdownHandler()) |
| 72 | self._handlers.register(JsonHandler()) |
| 73 | self._handlers.register(CsvHandler()) |
| 74 | self._handlers.register(BinaryHandler()) |
| 75 | self._handlers.register(TextHandler()) # Fallback handler |
| 76 | |
| 77 | # Performance optimization: pre-compile common regex patterns |
| 78 | self._compiled_patterns: dict[str, re.Pattern] = {} |
| 79 | |
| 80 | # --------------- Helpers --------------- |
| 81 | def _key(self, relative: Path) -> str: |
| 82 | """Generate cache key from relative path.""" |
| 83 | return str(relative.as_posix()) |
| 84 | |
| 85 | async def _read_raw(self, absolute: Path, relative: Path) -> bytes: |
| 86 | """Read raw bytes with caching.""" |
| 87 | cache_key = self._key(relative) |
| 88 | cached = self._cache.get(cache_key) |
| 89 | if cached is not None: |
| 90 | return cached |
| 91 | data = await self._storage.read_bytes(absolute) |
| 92 | self._cache.put(cache_key, data) |
| 93 | return data |
| 94 | |
| 95 | def _select_handler(self, path: Path): |
| 96 | """Select appropriate handler for file extension.""" |
| 97 | handler = self._handlers.find_for_extension(path.suffix) |
| 98 | return handler |
| 99 | |
| 100 | def _compile_pattern(self, pattern: str) -> re.Pattern: |