LocalStorage mapped to real fs
This commit is contained in:
@@ -37,8 +37,13 @@ async def a_main():
|
|||||||
print("Config:", config.get_config())
|
print("Config:", config.get_config())
|
||||||
print("Hello World")
|
print("Hello World")
|
||||||
from .storage.local_storage import LocalStorage
|
from .storage.local_storage import LocalStorage
|
||||||
|
from .storage.storage import AsyncFileContextManager as FCTX
|
||||||
from pathlib import PurePosixPath
|
from pathlib import PurePosixPath
|
||||||
fs = LocalStorage(r"D:\AuberyZhao\BackupBox\backup_box")
|
cfg = config.get_config()
|
||||||
|
stm: config.StorageItem = { "type": "LocalStorageItem", "path": "" }
|
||||||
|
for stm in cfg["storage"].values():
|
||||||
|
break
|
||||||
|
fs = LocalStorage.from_config(stm)
|
||||||
async for rt in fs.sync_storage():
|
async for rt in fs.sync_storage():
|
||||||
print(rt)
|
print(rt)
|
||||||
print(fs.dump_fs_tree())
|
print(fs.dump_fs_tree())
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
from backup_box.storage.storage import AsyncFileReader, AsyncFileWriter
|
from backup_box.storage.storage import AsyncFileReader, AsyncFileWriter
|
||||||
from pathlib import PurePath, PurePosixPath
|
from pathlib import PurePosixPath, PurePath
|
||||||
from ..config import StorageItem
|
from ..config import StorageItem
|
||||||
from .vnode import VFileSystem, VFile, VDir
|
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 aiofiles import open
|
||||||
|
from aiofiles.os import remove, rmdir, stat, listdir, mkdir
|
||||||
|
|
||||||
class LocalStorage(VFileSystem):
|
class LocalStorage(VFileSystem):
|
||||||
def __init__(self, base_path: str) -> None:
|
def __init__(self, base_path: str) -> None:
|
||||||
@@ -15,7 +18,7 @@ class LocalStorage(VFileSystem):
|
|||||||
raise ValueError("config type mismatch!")
|
raise ValueError("config type mismatch!")
|
||||||
return LocalStorage(cfg["path"])
|
return LocalStorage(cfg["path"])
|
||||||
|
|
||||||
def _vnode_path_to_syspath(self, *path: VFile | VDir):
|
def _vnode_path_to_syspath(self, *path: VFile | VDir) -> PurePath:
|
||||||
filtered = []
|
filtered = []
|
||||||
# filter abslute path
|
# filter abslute path
|
||||||
for n in path:
|
for n in path:
|
||||||
@@ -25,33 +28,53 @@ class LocalStorage(VFileSystem):
|
|||||||
if name in [".", "..", ""]:
|
if name in [".", "..", ""]:
|
||||||
continue
|
continue
|
||||||
filtered.append(name)
|
filtered.append(name)
|
||||||
return self._base.joinpath(*filtered)
|
return PurePath(self._base.joinpath(*filtered))
|
||||||
|
|
||||||
|
async def _fill_vnode_with_real_fs(self, vparent: VDir, real_path: PurePath):
|
||||||
|
try:
|
||||||
|
for name in await listdir(real_path):
|
||||||
|
p = real_path.joinpath(name)
|
||||||
|
st = await stat(p)
|
||||||
|
if S_ISDIR(st.st_mode):
|
||||||
|
child_node = new_storage_dir(name)
|
||||||
|
await self._fill_vnode_with_real_fs(child_node, p)
|
||||||
|
else:
|
||||||
|
child_node = new_storage_file(name, int(st.st_mtime), st.st_size)
|
||||||
|
vparent["c"].append(child_node)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
async def sync_storage(self):
|
async def sync_storage(self):
|
||||||
curr = 0
|
|
||||||
total = 1
|
|
||||||
parents: list[VDir] = []
|
|
||||||
self._root["c"].clear()
|
self._root["c"].clear()
|
||||||
node = self._root
|
await self._fill_vnode_with_real_fs(self._root, self._base)
|
||||||
yield curr, total
|
yield 1, 1
|
||||||
while True:
|
|
||||||
if node["ty"] == "dir":
|
|
||||||
pass
|
|
||||||
if len(parents) <= 0:
|
|
||||||
break
|
|
||||||
|
|
||||||
async def _on_remove_node(self, node_path: list[VFile | VDir]) -> None:
|
async def _on_remove_node(self, node_path: list[VFile | VDir]) -> None:
|
||||||
raise NotImplementedError
|
real_path = self._vnode_path_to_syspath(*node_path)
|
||||||
|
st = await stat(real_path)
|
||||||
|
if S_ISDIR(st.st_mode):
|
||||||
|
await rmdir(real_path)
|
||||||
|
else:
|
||||||
|
await remove(real_path)
|
||||||
|
|
||||||
async def _on_create_node(self, node_path: list[VFile | VDir]) -> None:
|
async def _on_create_node(self, node_path: list[VFile | VDir]) -> None:
|
||||||
raise NotImplementedError
|
if len(node_path) > 0 and node_path[-1]["ty"] == "dir":
|
||||||
|
real_path = self._vnode_path_to_syspath(*node_path)
|
||||||
|
await mkdir(real_path)
|
||||||
|
|
||||||
@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:
|
||||||
raise NotImplementedError
|
real_path = self._vnode_path_to_syspath(*node_path)
|
||||||
|
if mode == "r":
|
||||||
|
return await open(real_path, "rb")
|
||||||
|
elif mode == "w":
|
||||||
|
return await open(real_path, "wb")
|
||||||
|
else:
|
||||||
|
raise ValueError()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,24 +2,34 @@ from ..config import StorageItem
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from pathlib import PurePosixPath
|
from pathlib import PurePosixPath
|
||||||
from collections.abc import Awaitable, AsyncIterable
|
from collections.abc import Awaitable, AsyncIterable
|
||||||
from typing import overload, Literal, Protocol, SupportsBytes
|
from contextlib import AbstractAsyncContextManager
|
||||||
|
from typing import overload, Literal, Protocol, SupportsBytes, Self, runtime_checkable
|
||||||
|
|
||||||
class AsyncFileReader(Protocol):
|
class AsyncFileReader(Protocol):
|
||||||
async def read(self, n: int = -1) -> bytes:
|
async def read(self, n: int = -1, /) -> bytes:
|
||||||
...
|
...
|
||||||
|
|
||||||
async def close(self) -> None:
|
async def close(self) -> None:
|
||||||
...
|
...
|
||||||
|
|
||||||
class AsyncFileWriter(Protocol):
|
class AsyncFileWriter(Protocol):
|
||||||
async def write(self, s: SupportsBytes) -> int:
|
async def write(self, s: SupportsBytes, /) -> int:
|
||||||
...
|
...
|
||||||
|
|
||||||
async def close(self) -> None:
|
async def close(self) -> None:
|
||||||
...
|
...
|
||||||
|
|
||||||
class AsyncFileLike(AsyncFileReader, AsyncFileWriter):
|
class AsyncFileContextManager[T](AbstractAsyncContextManager[T]):
|
||||||
...
|
def __init__(self, fobj: T) -> None:
|
||||||
|
self.__fobj = fobj
|
||||||
|
|
||||||
|
async def __aenter__(self) -> T:
|
||||||
|
return self.__fobj # type: ignore
|
||||||
|
|
||||||
|
async def __aexit__(self, exc_type, exc, tb):
|
||||||
|
if hasattr(self.__fobj, "close"):
|
||||||
|
func = getattr(self.__fobj, "close")
|
||||||
|
await func()
|
||||||
|
|
||||||
class Storage(ABC):
|
class Storage(ABC):
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -28,7 +38,7 @@ class Storage(ABC):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def from_config(cls, cfg: StorageItem) -> 'Storage':
|
def from_config(cls, cfg: StorageItem) -> Self:
|
||||||
...
|
...
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
|||||||
Reference in New Issue
Block a user