From 4c81647bbe0537dcd761578a37ccbd7b8689f6bb Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Wed, 22 Apr 2026 16:53:27 +0200 Subject: [PATCH] lib.FileContext: Support attaching data filters - Add optional in_pipe and out_pipe parameters to __init__() - Add a add_proc_filter() method Add possibilites to attach input / output pipes to a FileContext instance. Data will be passed through the input pipe between ._get() and .get(), and through the output pipe between .put() and _put(). Signed-off-by: Jan Lindemann --- src/python/jw/pkg/lib/FileContext.py | 61 +++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/src/python/jw/pkg/lib/FileContext.py b/src/python/jw/pkg/lib/FileContext.py index de6fbd5d..8d104e80 100644 --- a/src/python/jw/pkg/lib/FileContext.py +++ b/src/python/jw/pkg/lib/FileContext.py @@ -3,7 +3,8 @@ from __future__ import annotations import abc, re -from typing import TYPE_CHECKING +from enum import Enum, auto +from typing import TYPE_CHECKING, Self from functools import cached_property, cache if TYPE_CHECKING: @@ -11,16 +12,31 @@ if TYPE_CHECKING: from .log import * from .base import Input, InputMode, Result, StatResult +from .ProcFilter import ProcPipeline class FileContext(abc.ABC): - def __init__(self, uri: str, interactive: bool|None=None, verbose_default=False, chroot: bool=False): + class Direction(Enum): + In = auto() + Out = auto() + + def __init__( + self, + uri: str, + interactive: bool|None = None, + verbose_default = False, + chroot: bool = False, + in_pipe: ProcPipeline|None = None, + out_pipe: ProcPipeline|None = None, + ): self.__uri = uri self.__id, self.__root = self.split_uri(uri) self.__chroot = chroot self.__interactive = interactive self.__verbose_default = verbose_default self.__log_name: str|None = None + self.__in_pipe = in_pipe + self.__out_pipe = out_pipe assert verbose_default is not None async def __aenter__(self): @@ -30,6 +46,19 @@ class FileContext(abc.ABC): async def __aexit__(self, exc_type, exc, tb): await self.close() + def __pipe(self, d: Direction): + match d: + case self.Direction.In: + if not self.__in_pipe: + self.__in_pipe = ProcPipeline() + return self.__in_pipe + case self.Direction.Out: + if not self.__out_pipe: + self.__out_pipe = ProcPipeline() + return self.__out_pipe + case _: + raise Exception(f'Invalid pipe direction "{str(d)}"') + def _chroot(self, path: str) -> str: if not self.__chroot: return path @@ -39,6 +68,9 @@ class FileContext(abc.ABC): return self.__root + path return self.__root + '/' + path + def add_proc_filter(self, d: Direction, proc_filter: ProcFilter): + self.__pipe(d).append(proc_filter) + async def _open(self) -> None: pass @@ -121,7 +153,14 @@ class FileContext(abc.ABC): group: str|None=None, mode: str|None=None, ) -> Result: - return await self._get(self._chroot(path), wd=wd, throw=throw, verbose=verbose, title=title) + ret = await self._get( + self._chroot(path), + wd = wd, + throw = throw, + verbose = verbose, + title = title, + ) + return await self.__in_pipe.run(ret) if self.__in_pipe else ret async def _put( self, @@ -152,8 +191,20 @@ class FileContext(abc.ABC): atomic: bool = False ) -> Result: mode_str = None if mode is None else oct(mode).replace('0o', '0') - return await self._put(self._chroot(path), content, wd=wd, throw=throw, verbose=verbose, - title=title, owner=owner, group=group, mode=mode_str, atomic=atomic) + if self.__out_pipe is not None: + content = self.__out_pipe.run(content).stdout + return await self._put( + self._chroot(path), + content, + wd = wd, + throw = throw, + verbose = verbose, + title = title, + owner = owner, + group = group, + mode = mode_str, + atomic = atomic, + ) async def _unlink(self, path: str) -> None: raise NotImplementedError(f'{self.log_name}: unlink("{path}") is not implemented')