lib.util.copy(): Tolerate list[str] and FileContext args

Modify copy():

- Allow the source argument src_uri to be a list of URIs, in which case copy should return a list of paths instead of one path
- Change the dst_uri parameter to dst, signalling that it now also tolerates a FileContext
- Use the CopyContext class to manage the source and target FileContext instances
Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
Jan Lindemann 2026-04-27 07:33:27 +02:00
commit 5837d10a1c
Signed by: Jan Lindemann
GPG key ID: 3750640C9E25DD61

View file

@ -101,29 +101,29 @@ async def run_sudo(cmd: list[str], *args, interactive: bool=True, ec: ExecContex
ec = Local(interactive=interactive) ec = Local(interactive=interactive)
return await ec.sudo(cmd, *args, **kwargs) return await ec.sudo(cmd, *args, **kwargs)
async def copy(src_uri: str, dst_uri: str, owner: str|None=None, group: str|None=None, mode: int|None=None, throw=True) -> Exception|str: async def copy(src_uri: str|Iterable[str], dst: str|FileContext, owner: str|None=None, group: str|None=None, mode: int|None=None, throw=True) -> Exception|str|list[str]:
from .ExecContext import ExecContext if not isinstance(src_uri, str):
src: ExecContext|None = None ret: list[str] = []
dst: ExecContext|None = None for uri in src_uri: # TODO: Group identical netlocs into one CopyContext
def __ec(uri: str) -> tuple[ExecContext, str]: rr = ret.append(await copy(uri, dst, owner, group, mode, throw))
return ExecContext.create(uri), urlparse(uri).path if isinstance(rr, Exception):
try: return rr
src, src_path = __ec(src_uri) return ret
content = (await src.get(src_path, throw=True)).stdout from .CopyContext import CopyContext
dst, dst_path = __ec(dst_uri) async with CopyContext(src_uri, dst) as ctx:
if await dst.is_dir(path): try:
dst_path += os.path.basename(src_path) content = (await ctx.src.get(ctx.src.root, throw=True)).stdout
await dst.put(path=dst_path, content=content, owner=owner, group=group, mode=mode, throw=True) dst_path = ctx.dst.root
except Exception as e: if await ctx.dst.is_dir(ctx.dst.root):
if throw: dst_path += '/' + os.path.basename(src_uri)
raise await ctx.dst.put(path=dst_path, content=content, owner=owner, group=group, mode=mode, throw=True)
log(ERR, f'Failed to copy {src_uri} -> {dst_uri} ({str(e)})') return dst_path
return e except Exception as e:
finally: if throw:
for ec in [src, dst]: raise
if ec is not None: log(ERR, f'Failed to copy {src_uri} -> {dst} ({str(e)})')
await ec.close() return e
return dst_path assert False, 'Unreachable code'
async def get_username(args: Namespace|None=None, url: str|None=None, askpass_env: list[str]=[], ec: ExecContext|None=None) -> str: # export async def get_username(args: Namespace|None=None, url: str|None=None, askpass_env: list[str]=[], ec: ExecContext|None=None) -> str: # export
url_user = None if url is None else Uri(url).username url_user = None if url is None else Uri(url).username