[docs]classLocalFileStore(ByteStore):"""BaseStore interface that works on the local file system. Examples: Create a LocalFileStore instance and perform operations on it: .. code-block:: python from langchain.storage import LocalFileStore # Instantiate the LocalFileStore with the root path file_store = LocalFileStore("/path/to/root") # Set values for keys file_store.mset([("key1", b"value1"), ("key2", b"value2")]) # Get values for keys values = file_store.mget(["key1", "key2"]) # Returns [b"value1", b"value2"] # Delete keys file_store.mdelete(["key1"]) # Iterate over keys for key in file_store.yield_keys(): print(key) # noqa: T201 """
[docs]def__init__(self,root_path:Union[str,Path],*,chmod_file:Optional[int]=None,chmod_dir:Optional[int]=None,update_atime:bool=False,)->None:"""Implement the BaseStore interface for the local file system. Args: root_path (Union[str, Path]): The root path of the file store. All keys are interpreted as paths relative to this root. chmod_file: (optional, defaults to `None`) If specified, sets permissions for newly created files, overriding the current `umask` if needed. chmod_dir: (optional, defaults to `None`) If specified, sets permissions for newly created dirs, overriding the current `umask` if needed. update_atime: (optional, defaults to `False`) If `True`, updates the filesystem access time (but not the modified time) when a file is read. This allows MRU/LRU cache policies to be implemented for filesystems where access time updates are disabled. """self.root_path=Path(root_path).absolute()self.chmod_file=chmod_fileself.chmod_dir=chmod_dirself.update_atime=update_atime
def_get_full_path(self,key:str)->Path:"""Get the full path for a given key relative to the root path. Args: key (str): The key relative to the root path. Returns: Path: The full path for the given key. """ifnotre.match(r"^[a-zA-Z0-9_.\-/]+$",key):raiseInvalidKeyException(f"Invalid characters in key: {key}")full_path=os.path.abspath(self.root_path/key)common_path=os.path.commonpath([str(self.root_path),full_path])ifcommon_path!=str(self.root_path):raiseInvalidKeyException(f"Invalid key: {key}. Key should be relative to the full path."f"{self.root_path} vs. {common_path} and full path of {full_path}")returnPath(full_path)def_mkdir_for_store(self,dir:Path)->None:"""Makes a store directory path (including parents) with specified permissions This is needed because `Path.mkdir()` is restricted by the current `umask`, whereas the explicit `os.chmod()` used here is not. Args: dir: (Path) The store directory to make Returns: None """ifnotdir.exists():self._mkdir_for_store(dir.parent)dir.mkdir(exist_ok=True)ifself.chmod_dirisnotNone:os.chmod(dir,self.chmod_dir)
[docs]defmget(self,keys:Sequence[str])->List[Optional[bytes]]:"""Get the values associated with the given keys. Args: keys: A sequence of keys. Returns: A sequence of optional values associated with the keys. If a key is not found, the corresponding value will be None. """values:List[Optional[bytes]]=[]forkeyinkeys:full_path=self._get_full_path(key)iffull_path.exists():value=full_path.read_bytes()values.append(value)ifself.update_atime:# update access time only; preserve modified timeos.utime(full_path,(time.time(),os.stat(full_path).st_mtime))else:values.append(None)returnvalues
[docs]defmset(self,key_value_pairs:Sequence[Tuple[str,bytes]])->None:"""Set the values for the given keys. Args: key_value_pairs: A sequence of key-value pairs. Returns: None """forkey,valueinkey_value_pairs:full_path=self._get_full_path(key)self._mkdir_for_store(full_path.parent)full_path.write_bytes(value)ifself.chmod_fileisnotNone:os.chmod(full_path,self.chmod_file)
[docs]defmdelete(self,keys:Sequence[str])->None:"""Delete the given keys and their associated values. Args: keys (Sequence[str]): A sequence of keys to delete. Returns: None """forkeyinkeys:full_path=self._get_full_path(key)iffull_path.exists():full_path.unlink()
[docs]defyield_keys(self,prefix:Optional[str]=None)->Iterator[str]:"""Get an iterator over keys that match the given prefix. Args: prefix (Optional[str]): The prefix to match. Returns: Iterator[str]: An iterator over keys that match the given prefix. """prefix_path=self._get_full_path(prefix)ifprefixelseself.root_pathforfileinprefix_path.rglob("*"):iffile.is_file():relative_path=file.relative_to(self.root_path)yieldstr(relative_path)