lib.FcWrapper: Add a delegating file context wrapper

Add a file context that sits in front of another one and delegates every
primitive call to it.

The wrapper adopts the identity of the wrapped context: uri, root, chroot
flag, and the chroot path composition are the wrapped context's, so a path
composed by the wrapper composes identically in the wrapped context. Every
primitive is forwarded to the wrapped context's primitives, which receive
the already-composed path; open() and close() are forwarded to the wrapped
context's public methods, so the open count is kept in one place.

The point is to give derived contexts a stable base: a caching context, or
any other filter, overrides the primitives it cares about and inherits the
rest, instead of re-deriving the chroot bookkeeping of every method.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v1.1.0
Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
Jan Lindemann 2026-10-09 10:48:17 +02:00
commit cda9e210da
Signed by: Jan Lindemann
GPG key ID: 3750640C9E25DD61

View file

@ -0,0 +1,122 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Iterable, override
if TYPE_CHECKING:
from .base import Result, StatResult
from .file_types import FileType
from .FileContext import FileContext
class FcWrapper(FileContext): # export
"""A file context that delegates to a wrapped context.
The wrapper adopts the identity of the wrapped context: uri,
root, chroot flag, and the chroot path composition are all the
wrapped context's, so a path composed by the wrapper composes
identically in the wrapped context. Every primitive call is
forwarded to the wrapped context's primitives, which receive
the already-composed path; open and close are forwarded to the
wrapped context's public methods, so the open count is kept in
one place.
"""
def __init__(self, wrapped: FileContext) -> None:
super().__init__(
wrapped.uri,
interactive = wrapped.interactive,
verbose_default = wrapped.verbose_default,
chroot = wrapped.chroot,
)
self.__wrapped = wrapped
@property
def wrapped(self) -> FileContext:
return self.__wrapped
@override
async def _open(self) -> None:
await self.__wrapped.open()
@override
async def _close(self) -> None:
await self.__wrapped.close()
@override
async def _get(
self, path: str, wd: str | None, throw: bool, verbose: bool | None, title: str
) -> Result:
return await self.__wrapped._get(path, wd, throw, verbose, title)
@override
async def _put(
self,
path: str,
content: bytes,
wd: str | None,
throw: bool,
verbose: bool | None,
title: str,
owner: str | None,
group: str | None,
mode: str | None,
atomic: bool,
ftype: FileType,
) -> Result:
return await self.__wrapped._put(
path,
content,
wd,
throw,
verbose,
title,
owner,
group,
mode,
atomic,
ftype,
)
@override
async def _unlink(self, path: str) -> None:
await self.__wrapped._unlink(path)
@override
async def _erase(self, path: str) -> None:
await self.__wrapped._erase(path)
@override
async def _rename(self, src: str, dst: str) -> None:
await self.__wrapped._rename(src, dst)
@override
async def _mkdir(self, path: str, mode: int, ftype: FileType) -> None:
await self.__wrapped._mkdir(path, mode, ftype)
@override
async def _mktemp(self, tmpl: str, directory: bool) -> str:
return await self.__wrapped._mktemp(tmpl, directory)
@override
async def _chown(self, path: str, owner: str | None, group: str | None) -> None:
await self.__wrapped._chown(path, owner, group)
@override
async def _chmod(self, path: str, mode: int) -> None:
await self.__wrapped._chmod(path, mode)
@override
async def _stat(self, path: str, follow_symlinks: bool) -> StatResult:
return await self.__wrapped._stat(path, follow_symlinks)
@override
async def _file_exists(self, path: str) -> bool:
return await self.__wrapped._file_exists(path)
@override
async def _is_dir(self, path: str, follow_symlinks: bool) -> bool:
return await self.__wrapped._is_dir(path, follow_symlinks)
@override
async def _readdir(self, path: str) -> Iterable[str]:
return await self.__wrapped._readdir(path)