mirror of
https://github.com/ARM-software/devlib.git
synced 2025-03-04 17:27:51 +00:00
target: Asyncify Target._prepare_xfer()
_prepare_xfer() deals with all the paths resulting from glob expansion, so it can benefit from async capabilities in order to process multiple files concurrently. Convert the internals to async/await to enable useful map_concurrently()
This commit is contained in:
parent
4431932e0d
commit
1b6c8069bd
@ -663,10 +663,22 @@ class Target(object):
|
|||||||
transfering multiple sources.
|
transfering multiple sources.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
once = functools.lru_cache(maxsize=None)
|
def once(f):
|
||||||
|
cache = dict()
|
||||||
|
|
||||||
|
@functools.wraps(f)
|
||||||
|
async def wrapper(path):
|
||||||
|
try:
|
||||||
|
return cache[path]
|
||||||
|
except KeyError:
|
||||||
|
x = await f(path)
|
||||||
|
cache[path] = x
|
||||||
|
return x
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
_target_cache = {}
|
_target_cache = {}
|
||||||
def target_paths_kind(paths, as_root=False):
|
async def target_paths_kind(paths, as_root=False):
|
||||||
def process(x):
|
def process(x):
|
||||||
x = x.strip()
|
x = x.strip()
|
||||||
if x == 'notexist':
|
if x == 'notexist':
|
||||||
@ -686,7 +698,7 @@ class Target(object):
|
|||||||
)
|
)
|
||||||
for path in _paths
|
for path in _paths
|
||||||
)
|
)
|
||||||
res = self.execute(cmd, as_root=as_root)
|
res = await self.execute.asyn(cmd, as_root=as_root)
|
||||||
_target_cache.update(zip(_paths, map(process, res.split())))
|
_target_cache.update(zip(_paths, map(process, res.split())))
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@ -695,7 +707,7 @@ class Target(object):
|
|||||||
]
|
]
|
||||||
|
|
||||||
_host_cache = {}
|
_host_cache = {}
|
||||||
def host_paths_kind(paths, as_root=False):
|
async def host_paths_kind(paths, as_root=False):
|
||||||
def path_kind(path):
|
def path_kind(path):
|
||||||
if os.path.isdir(path):
|
if os.path.isdir(path):
|
||||||
return 'dir'
|
return 'dir'
|
||||||
@ -722,47 +734,55 @@ class Target(object):
|
|||||||
src_excep = HostError
|
src_excep = HostError
|
||||||
src_path_kind = host_paths_kind
|
src_path_kind = host_paths_kind
|
||||||
|
|
||||||
_dst_mkdir = once(self.makedirs)
|
_dst_mkdir = once(self.makedirs.asyn)
|
||||||
dst_path_join = self.path.join
|
dst_path_join = self.path.join
|
||||||
dst_paths_kind = target_paths_kind
|
dst_paths_kind = target_paths_kind
|
||||||
dst_remove_file = once(functools.partial(self.remove, as_root=as_root))
|
|
||||||
|
@once
|
||||||
|
async def dst_remove_file(path):
|
||||||
|
return await self.remove.asyn(path, as_root=as_root)
|
||||||
elif action == 'pull':
|
elif action == 'pull':
|
||||||
src_excep = TargetStableError
|
src_excep = TargetStableError
|
||||||
src_path_kind = target_paths_kind
|
src_path_kind = target_paths_kind
|
||||||
|
|
||||||
_dst_mkdir = once(functools.partial(os.makedirs, exist_ok=True))
|
@once
|
||||||
|
async def _dst_mkdir(path):
|
||||||
|
return os.makedirs(path, exist_ok=True)
|
||||||
dst_path_join = os.path.join
|
dst_path_join = os.path.join
|
||||||
dst_paths_kind = host_paths_kind
|
dst_paths_kind = host_paths_kind
|
||||||
dst_remove_file = once(os.remove)
|
|
||||||
|
@once
|
||||||
|
async def dst_remove_file(path):
|
||||||
|
return os.remove(path)
|
||||||
else:
|
else:
|
||||||
raise ValueError('Unknown action "{}"'.format(action))
|
raise ValueError('Unknown action "{}"'.format(action))
|
||||||
|
|
||||||
# Handle the case where path is None
|
# Handle the case where path is None
|
||||||
def dst_mkdir(path):
|
async def dst_mkdir(path):
|
||||||
if path:
|
if path:
|
||||||
_dst_mkdir(path)
|
await _dst_mkdir(path)
|
||||||
|
|
||||||
def rewrite_dst(src, dst):
|
async def rewrite_dst(src, dst):
|
||||||
new_dst = dst_path_join(dst, os.path.basename(src))
|
new_dst = dst_path_join(dst, os.path.basename(src))
|
||||||
|
|
||||||
src_kind, = src_path_kind([src], as_root)
|
src_kind, = await src_path_kind([src], as_root)
|
||||||
# Batch both checks to avoid a costly extra execute()
|
# Batch both checks to avoid a costly extra execute()
|
||||||
dst_kind, new_dst_kind = dst_paths_kind([dst, new_dst], as_root)
|
dst_kind, new_dst_kind = await dst_paths_kind([dst, new_dst], as_root)
|
||||||
|
|
||||||
if src_kind == 'file':
|
if src_kind == 'file':
|
||||||
if dst_kind == 'dir':
|
if dst_kind == 'dir':
|
||||||
if new_dst_kind == 'dir':
|
if new_dst_kind == 'dir':
|
||||||
raise IsADirectoryError(new_dst)
|
raise IsADirectoryError(new_dst)
|
||||||
if new_dst_kind == 'file':
|
if new_dst_kind == 'file':
|
||||||
dst_remove_file(new_dst)
|
await dst_remove_file(new_dst)
|
||||||
return new_dst
|
return new_dst
|
||||||
else:
|
else:
|
||||||
return new_dst
|
return new_dst
|
||||||
elif dst_kind == 'file':
|
elif dst_kind == 'file':
|
||||||
dst_remove_file(dst)
|
await dst_remove_file(dst)
|
||||||
return dst
|
return dst
|
||||||
else:
|
else:
|
||||||
dst_mkdir(os.path.dirname(dst))
|
await dst_mkdir(os.path.dirname(dst))
|
||||||
return dst
|
return dst
|
||||||
elif src_kind == 'dir':
|
elif src_kind == 'dir':
|
||||||
if dst_kind == 'dir':
|
if dst_kind == 'dir':
|
||||||
@ -776,7 +796,7 @@ class Target(object):
|
|||||||
elif dst_kind == 'file':
|
elif dst_kind == 'file':
|
||||||
raise FileExistsError(dst_kind)
|
raise FileExistsError(dst_kind)
|
||||||
else:
|
else:
|
||||||
dst_mkdir(os.path.dirname(dst))
|
await dst_mkdir(os.path.dirname(dst))
|
||||||
return dst
|
return dst
|
||||||
else:
|
else:
|
||||||
raise FileNotFoundError(src)
|
raise FileNotFoundError(src)
|
||||||
@ -785,18 +805,19 @@ class Target(object):
|
|||||||
if not sources:
|
if not sources:
|
||||||
raise src_excep('No file matching source pattern: {}'.format(pattern))
|
raise src_excep('No file matching source pattern: {}'.format(pattern))
|
||||||
|
|
||||||
if dst_paths_kind([dest]) != ['dir']:
|
if (await dst_paths_kind([dest])) != ['dir']:
|
||||||
raise NotADirectoryError('A folder dest is required for multiple matches but destination is a file: {}'.format(dest))
|
raise NotADirectoryError('A folder dest is required for multiple matches but destination is a file: {}'.format(dest))
|
||||||
|
|
||||||
|
async def f(src):
|
||||||
|
return await rewrite_dst(src, dest)
|
||||||
|
mapping = await self.async_manager.map_concurrently(f, sources)
|
||||||
|
|
||||||
# TODO: since rewrite_dst() will currently return a different path for
|
# TODO: since rewrite_dst() will currently return a different path for
|
||||||
# each source, it will not bring anything. In order to be useful,
|
# each source, it will not bring anything. In order to be useful,
|
||||||
# connections need to be able to understand that if the destination is
|
# connections need to be able to understand that if the destination is
|
||||||
# an empty folder, the source is supposed to be transfered into it with
|
# an empty folder, the source is supposed to be transfered into it with
|
||||||
# the same basename.
|
# the same basename.
|
||||||
return groupby_value({
|
return groupby_value(mapping)
|
||||||
src: rewrite_dst(src, dest)
|
|
||||||
for src in sources
|
|
||||||
})
|
|
||||||
|
|
||||||
@asyn.asyncf
|
@asyn.asyncf
|
||||||
@call_conn
|
@call_conn
|
||||||
|
Loading…
x
Reference in New Issue
Block a user