lib.util.copy(): Support FileContext source, file types, and --parents #144
1 changed files with 54 additions and 59 deletions
lib.util.copy(): Remove __mkdir_p()
Refactor copy(), making the recursive logic more coherent: Every node the walk creates sits below a node that already exists, so a plain per-node mkdir() suffices and the chain- creating __mkdir_p() is dropped: the landing root's parent must exist now, as in cp -r. Signed-off-by: Jan Lindemann <jan@janware.com>
commit
a328b01e9a
|
|
@ -177,8 +177,11 @@ async def copy(
|
||||||
throw: bool = True,
|
throw: bool = True,
|
||||||
recursive: bool = False,
|
recursive: bool = False,
|
||||||
) -> Exception | str | list[str]:
|
) -> Exception | str | list[str]:
|
||||||
|
|
||||||
|
# Destination address / addresses
|
||||||
|
ret: str | list[str]
|
||||||
if not isinstance(src_uri, str):
|
if not isinstance(src_uri, str):
|
||||||
ret: list[str] = []
|
ret = []
|
||||||
for uri in src_uri: # TODO: Group identical netlocs into one CopyContext
|
for uri in src_uri: # TODO: Group identical netlocs into one CopyContext
|
||||||
rr = await copy(uri, dst, owner, group, mode, throw, recursive)
|
rr = await copy(uri, dst, owner, group, mode, throw, recursive)
|
||||||
if isinstance(rr, Exception):
|
if isinstance(rr, Exception):
|
||||||
|
|
@ -190,32 +193,20 @@ async def copy(
|
||||||
else:
|
else:
|
||||||
raise Exception(f'copy() returned unexpected type {type(rr)}')
|
raise Exception(f'copy() returned unexpected type {type(rr)}')
|
||||||
return ret
|
return ret
|
||||||
from .CopyContext import CopyContext
|
|
||||||
|
|
||||||
async with CopyContext(src_uri, dst) as ctx:
|
|
||||||
|
|
||||||
async def __mkdir_p(path: str) -> None:
|
|
||||||
# -- Create each level of the destination path that does not
|
|
||||||
# exist yet; put() does not create parent directories
|
|
||||||
cur = ''
|
|
||||||
for part in path.strip('/').split('/'):
|
|
||||||
cur = f'{cur}/{part}'
|
|
||||||
if not await ctx.dst.is_dir(cur):
|
|
||||||
await ctx.dst.mkdir(cur)
|
|
||||||
|
|
||||||
async def __copy_tree(src_path: str, dst_path: str) -> list[str]:
|
async def __copy_tree(src_path: str, dst_path: str) -> list[str]:
|
||||||
ret: list[str] = []
|
ret: list[str] = []
|
||||||
for entry in await ctx.src.readdir(src_path):
|
s_st = await ctx.src.stat(src_path)
|
||||||
s_path = f'{src_path}/{entry}'
|
if dst_path and not await ctx.dst.is_dir(dst_path):
|
||||||
d_path = f'{dst_path}/{entry}'
|
await ctx.dst.mkdir(dst_path)
|
||||||
if await ctx.src.is_dir(s_path):
|
for dirent in await ctx.src.readdir(src_path):
|
||||||
await __mkdir_p(d_path)
|
s_path = f'{src_path}/{dirent}' if src_path else dirent
|
||||||
|
d_path = f'{dst_path}/{dirent}' if dst_path else dirent
|
||||||
|
s_st = await ctx.src.stat(s_path)
|
||||||
|
if s_st.is_dir:
|
||||||
ret.extend(await __copy_tree(s_path, d_path))
|
ret.extend(await __copy_tree(s_path, d_path))
|
||||||
else:
|
continue
|
||||||
result = await ctx.src.get(s_path, throw = True)
|
result = await ctx.src.get(s_path, throw = True)
|
||||||
parent = d_path.rpartition('/')[0]
|
|
||||||
if parent:
|
|
||||||
await __mkdir_p(parent)
|
|
||||||
await ctx.dst.put(
|
await ctx.dst.put(
|
||||||
path = d_path,
|
path = d_path,
|
||||||
content = result.stdout,
|
content = result.stdout,
|
||||||
|
|
@ -228,34 +219,38 @@ async def copy(
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if recursive and await ctx.src.is_dir(ctx.src.root):
|
|
||||||
# -- A directory source is mirrored below the destination
|
from .CopyContext import CopyContext
|
||||||
# root: into it, if the root is an existing directory,
|
|
||||||
# as it, if it does not exist yet
|
async with CopyContext(src_uri, dst) as ctx:
|
||||||
dst_root = ctx.dst.root
|
|
||||||
if await ctx.dst.is_dir(dst_root):
|
ret = '' if ctx.dst.chroot else ctx.dst.root
|
||||||
dst_root += '/' + os.path.basename(src_uri)
|
if ret:
|
||||||
await __mkdir_p(dst_root)
|
# Land the source's basename within existing destination directories
|
||||||
return await __copy_tree(ctx.src.root, dst_root)
|
basename = os.path.basename(Uri(src_uri).path.rstrip('/'))
|
||||||
result = await ctx.src.get(ctx.src.root, throw = True)
|
if basename and await ctx.dst.is_dir(ret):
|
||||||
dst_path = ctx.dst.root
|
ret += '/' + basename
|
||||||
if await ctx.dst.is_dir(ctx.dst.root):
|
src_root = '' if ctx.src.chroot else ctx.src.root
|
||||||
dst_path += '/' + os.path.basename(src_uri)
|
|
||||||
|
if recursive and await ctx.src.is_dir(src_root):
|
||||||
|
return await __copy_tree(src_root, ret)
|
||||||
|
|
||||||
|
result = await ctx.src.get(src_root, throw = True)
|
||||||
await ctx.dst.put(
|
await ctx.dst.put(
|
||||||
path = dst_path,
|
path = ret,
|
||||||
content = result.stdout,
|
content = result.stdout,
|
||||||
owner = owner,
|
owner = owner,
|
||||||
group = group,
|
group = group,
|
||||||
mode = mode,
|
mode = mode,
|
||||||
throw = True,
|
throw = True,
|
||||||
)
|
)
|
||||||
return dst_path
|
return ret
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if throw:
|
if throw:
|
||||||
raise
|
raise
|
||||||
log(ERR, f'Failed to copy {src_uri} -> {dst} ({str(e)})')
|
log(ERR, f'Failed to copy {src_uri} -> {dst} ({str(e)})')
|
||||||
return e
|
return e
|
||||||
assert False, 'Unreachable code'
|
|
||||||
|
|
||||||
async def get_username( # export
|
async def get_username( # export
|
||||||
args: Namespace | None = None,
|
args: Namespace | None = None,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue