"""Abstract storage adapter for notebook snapshot backends. This module defines the common interface for dumb bytes buckets. It has no knowledge of notebooks, versions, manifests, compression, or hashing. """ from abc import ABC, abstractmethod def validate_key(key: str) -> str: """Validate and return a POSIX-style relative storage key. Rejects empty keys, absolute paths, parent-directory segments, and backslashes. The returned value is the same string passed in. Parameters ---------- key: str The storage key to validate. Returns ------- str The validated key. Raises ------ ValueError If the key is empty, absolute, contains ``..``, or contains backslashes. """ if not key: raise ValueError("Storage key must not be empty") if key.startswith("/"): raise ValueError(f"Storage key must be relative, got absolute path: {key!r}") if "\\" in key: raise ValueError(f"Storage key must use POSIX separators, got backslash: {key!r}") if ".." in key.split("/"): raise ValueError(f"Storage key must not contain parent segments: {key!r}") return key class StorageAdapter(ABC): """Abstract base class for byte-oriented storage backends.""" @abstractmethod def put(self, key: str, data: bytes) -> None: """Store ``data`` under ``key``. Parameters ---------- key: str The storage key. data: bytes The payload to store. """ raise NotImplementedError @abstractmethod def get(self, key: str) -> bytes: """Return the bytes stored under ``key``. Parameters ---------- key: str The storage key. Returns ------- bytes The stored payload. Raises ------ KeyError If the key does not exist. """ raise NotImplementedError @abstractmethod def list_keys(self, prefix: str = "") -> list[str]: """Return a sorted list of keys starting with ``prefix``. Parameters ---------- prefix: str, optional Filter keys to those beginning with this value. The empty string matches all keys. Returns ------- list[str] Sorted matching keys. """ raise NotImplementedError @abstractmethod def delete(self, key: str) -> None: """Delete the object at ``key``. This method is idempotent: deleting a missing key does not raise. Parameters ---------- key: str The storage key. """ raise NotImplementedError def exists(self, key: str) -> bool: """Return whether ``key`` exists in storage. Parameters ---------- key: str The storage key. Returns ------- bool ``True`` if the key exists, ``False`` otherwise. """ try: self.get(key) return True except KeyError: return False