Tooling for managing asset compression, storage, and retrieval
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

80 lines
2.3 KiB

  1. import asyncio
  2. import logging
  3. import typing
  4. LOG = logging.getLogger("acm.async")
  5. async def run_command_shell(
  6. command,
  7. stdout=asyncio.subprocess.PIPE,
  8. stderr=asyncio.subprocess.PIPE,
  9. on_success: typing.List[typing.Callable] = [()]
  10. ):
  11. """Run command in subprocess (shell).
  12. Note:
  13. This can be used if you wish to execute e.g. "copy"
  14. on Windows, which can only be executed in the shell.
  15. """
  16. process = await asyncio.create_subprocess_shell(
  17. command, stdout=stdout, stderr=stderr
  18. )
  19. process_stdout, process_stderr = await process.communicate()
  20. if process.returncode == 0:
  21. for success_callable in on_success:
  22. success_callable()
  23. if stdout != asyncio.subprocess.DEVNULL:
  24. result = process_stdout.decode().strip()
  25. return result
  26. else:
  27. return None
  28. def make_chunks(tasks, chunk_size):
  29. """Yield successive chunk_size-sized chunks from tasks.
  30. Note:
  31. Taken from https://stackoverflow.com/a/312464
  32. modified for python 3 only
  33. """
  34. for i in range(0, len(tasks), chunk_size):
  35. yield tasks[i: i + chunk_size]
  36. def run_asyncio_commands(tasks, max_concurrent_tasks=0):
  37. """Run tasks asynchronously using asyncio and return results.
  38. If max_concurrent_tasks are set to 0, no limit is applied.
  39. Note:
  40. By default, Windows uses SelectorEventLoop, which does not support
  41. subprocesses. Therefore ProactorEventLoop is used on Windows.
  42. https://docs.python.org/3/library/asyncio-eventloops.html#windows
  43. """
  44. all_results = []
  45. if max_concurrent_tasks == 0:
  46. chunks = [tasks]
  47. num_chunks = len(chunks)
  48. else:
  49. chunks = make_chunks(tasks=tasks, chunk_size=max_concurrent_tasks)
  50. num_chunks = len(
  51. list(make_chunks(tasks=tasks, chunk_size=max_concurrent_tasks)))
  52. if asyncio.get_event_loop().is_closed():
  53. asyncio.set_event_loop(asyncio.new_event_loop())
  54. if platform.system() == "Windows":
  55. asyncio.set_event_loop(asyncio.ProactorEventLoop())
  56. loop = asyncio.get_event_loop()
  57. chunk = 1
  58. for tasks_in_chunk in chunks:
  59. commands = asyncio.gather(*tasks_in_chunk)
  60. results = loop.run_until_complete(commands)
  61. all_results += results
  62. chunk += 1
  63. loop.close()
  64. return all_results