add file size and modify time

This commit is contained in:
2025-11-06 17:07:15 +08:00
parent 06595b9ca9
commit b9f0f5adef
4 changed files with 72 additions and 11 deletions

View File

@@ -46,6 +46,9 @@ async def a_main():
fs = LocalStorage.from_config(stm) fs = LocalStorage.from_config(stm)
async for rt in fs.sync_storage(): async for rt in fs.sync_storage():
print(rt) print(rt)
async with FCTX(fs.open(PurePosixPath("app.txt"), "w")) as fp:
await fp.write(b"Hello Wyvern!")
await fs.set_last_modify_time(PurePosixPath("app.txt"), 0)
print(fs.dump_fs_tree()) print(fs.dump_fs_tree())
def main(): def main():

View File

@@ -1,16 +1,22 @@
from backup_box.storage.storage import AsyncFileReader, AsyncFileWriter from backup_box.storage.storage import AsyncFileReader, AsyncFileWriter
from pathlib import PurePosixPath, PurePath from pathlib import PurePath
from ..config import StorageItem from ..config import StorageItem
from .vnode import VFileSystem, VFile, VDir, new_storage_dir, new_storage_file from .vnode import VFileSystem, VFile, VDir, new_storage_dir, new_storage_file
from typing import Literal, overload from typing import Literal, overload
from stat import S_ISDIR from stat import S_ISDIR
from aiofiles import open from aiofiles import open
from aiofiles.os import remove, rmdir, stat, listdir, mkdir from aiofiles.os import remove, rmdir, stat, listdir, mkdir
from concurrent.futures import ThreadPoolExecutor
from asyncio import get_event_loop
from os import utime as utime_sync
from functools import partial
from time import time
class LocalStorage(VFileSystem): class LocalStorage(VFileSystem):
def __init__(self, base_path: str) -> None: def __init__(self, base_path: str) -> None:
super().__init__() super().__init__()
self._base = PurePath(base_path) self._base = PurePath(base_path)
self._exec = ThreadPoolExecutor(1)
@classmethod @classmethod
def from_config(cls, cfg: StorageItem) -> "LocalStorage": def from_config(cls, cfg: StorageItem) -> "LocalStorage":
@@ -60,13 +66,22 @@ class LocalStorage(VFileSystem):
async def _on_create_node(self, node_path: list[VFile | VDir]) -> None: async def _on_create_node(self, node_path: list[VFile | VDir]) -> None:
if len(node_path) > 0 and node_path[-1]["ty"] == "dir": if len(node_path) > 0 and node_path[-1]["ty"] == "dir":
real_path = self._vnode_path_to_syspath(*node_path) real_path = self._vnode_path_to_syspath(*node_path)
print(real_path)
await mkdir(real_path) await mkdir(real_path)
async def _on_set_last_modify_time(self, node_path: list[VFile | VDir]) -> None:
if len(node_path) <= 0 or node_path[-1]["ty"] != "file":
raise IsADirectoryError()
real_path = self._vnode_path_to_syspath(*node_path)
await get_event_loop().run_in_executor(self._exec, utime_sync, real_path, (int(time()), node_path[-1]["mt"]))
@overload @overload
async def _on_open_node(self, node_path: list[VFile | VDir], mode: Literal["r"] = "r") -> AsyncFileReader: ... async def _on_open_node(self, node_path: list[VFile | VDir], mode: Literal["r"] = "r") -> AsyncFileReader: ...
@overload @overload
async def _on_open_node(self, node_path: list[VFile | VDir], mode: Literal["w"]) -> AsyncFileWriter: ... async def _on_open_node(self, node_path: list[VFile | VDir], mode: Literal["w"]) -> AsyncFileWriter: ...
async def _on_open_node(self, node_path: list[VFile | VDir], mode: Literal["r"] | Literal["w"] = "r") -> AsyncFileWriter | AsyncFileReader: async def _on_open_node(self, node_path: list[VFile | VDir], mode: Literal["r"] | Literal["w"] = "r") -> AsyncFileWriter | AsyncFileReader:
if len(node_path) <= 0:
raise IsADirectoryError()
real_path = self._vnode_path_to_syspath(*node_path) real_path = self._vnode_path_to_syspath(*node_path)
if mode == "r": if mode == "r":
return await open(real_path, "rb") return await open(real_path, "rb")
@@ -74,8 +89,3 @@ class LocalStorage(VFileSystem):
return await open(real_path, "wb") return await open(real_path, "wb")
else: else:
raise ValueError() raise ValueError()

View File

@@ -20,15 +20,17 @@ class AsyncFileWriter(Protocol):
... ...
class AsyncFileContextManager[T](AbstractAsyncContextManager[T]): class AsyncFileContextManager[T](AbstractAsyncContextManager[T]):
def __init__(self, fobj: T) -> None: def __init__(self, fobj: Awaitable[T]) -> None:
self.__fobj = fobj self.__fobj = fobj
self.__obj: T | None = None
async def __aenter__(self) -> T: async def __aenter__(self) -> T:
return self.__fobj # type: ignore self.__obj = await self.__fobj
return self.__obj # type: ignore
async def __aexit__(self, exc_type, exc, tb): async def __aexit__(self, exc_type, exc, tb):
if hasattr(self.__fobj, "close"): if hasattr(self.__obj, "close"):
func = getattr(self.__fobj, "close") func = getattr(self.__obj, "close")
await func() await func()
class Storage(ABC): class Storage(ABC):
@@ -91,3 +93,18 @@ class Storage(ABC):
def makedirs(self, path: PurePosixPath, exists_ok=False) -> Awaitable[None]: def makedirs(self, path: PurePosixPath, exists_ok=False) -> Awaitable[None]:
""" create dir """ """ create dir """
... ...
@abstractmethod
def get_file_size(self, path: PurePosixPath) -> Awaitable[int]:
""" get file size, can raise error for dir """
...
@abstractmethod
def get_last_modify_time(self, path: PurePosixPath) -> Awaitable[int]:
""" get last modify time in seconds since unix time, can raise error for dir """
...
@abstractmethod
def set_last_modify_time(self, path: PurePosixPath, timestamp: int) -> Awaitable[None]:
""" get last modify time in seconds since unix time, can raise error for dir """
...

View File

@@ -7,7 +7,7 @@ from abc import abstractmethod
class VFile(TypedDict): class VFile(TypedDict):
ty: Literal["file"] # type ty: Literal["file"] # type
n: str # virtual file name n: str # virtual file name
mt: int # modify time mt: int # modify time seconds
sz: int # size sz: int # size
class VDir(TypedDict): class VDir(TypedDict):
@@ -103,6 +103,13 @@ class VFileSystem(Storage):
print("create:", path) print("create:", path)
... ...
@abstractmethod
async def _on_set_last_modify_time(self, node_path: list[VFSItem]) -> None:
""" create a file or dir, parent dir is always created first. """
path = PurePosixPath( *(n["n"] for n in node_path) )
print("create:", path)
...
@overload @overload
@abstractmethod @abstractmethod
async def _on_open_node(self, node_path: list[VFSItem], mode: Literal["r"] = "r") -> AsyncFileReader: ... async def _on_open_node(self, node_path: list[VFSItem], mode: Literal["r"] = "r") -> AsyncFileReader: ...
@@ -240,6 +247,8 @@ class VFileSystem(Storage):
build.append(node) build.append(node)
await self._on_create_node(build) await self._on_create_node(build)
# create dir # create dir
if parent == self._root:
return
for n in parent["c"]: for n in parent["c"]:
if n["n"] == path.name: if n["n"] == path.name:
if not exists_ok: if not exists_ok:
@@ -253,3 +262,25 @@ class VFileSystem(Storage):
build.append(node) build.append(node)
await self._on_create_node(build) await self._on_create_node(build)
async def get_file_size(self, path: PurePosixPath) -> int:
node = find_vnode(self._root, path)[-1]
if node["ty"] == "file":
return node["sz"]
raise IsADirectoryError(path)
async def get_last_modify_time(self, path: PurePosixPath) -> int:
node = find_vnode(self._root, path)[-1]
if node["ty"] == "file":
return node["mt"]
else:
raise IsADirectoryError(path)
async def set_last_modify_time(self, path: PurePosixPath, timestamp: int):
""" get last modify time in seconds since unix time """
nodes = find_vnode(self._root, path)
node = nodes[-1]
if node["ty"] == "file":
node["mt"] = timestamp
await self._on_set_last_modify_time(nodes)
else:
raise IsADirectoryError(path)