util.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. """Utility functions.
  2. """
  3. import contextlib
  4. import multiprocessing
  5. import sys
  6. import re
  7. from milc import cli
  8. TRIPLET_PATTERN = re.compile(r'^(\d+)\.(\d+)\.(\d+)')
  9. maybe_exit_should_exit = True
  10. maybe_exit_reraise = False
  11. # Controls whether or not early `exit()` calls should be made
  12. def maybe_exit(rc):
  13. if maybe_exit_should_exit:
  14. sys.exit(rc)
  15. if maybe_exit_reraise:
  16. e = sys.exc_info()[1]
  17. if e:
  18. raise e
  19. def maybe_exit_config(should_exit: bool = True, should_reraise: bool = False):
  20. global maybe_exit_should_exit
  21. global maybe_exit_reraise
  22. maybe_exit_should_exit = should_exit
  23. maybe_exit_reraise = should_reraise
  24. def truthy(value, value_if_unknown=False):
  25. """Returns True if the value is truthy, False otherwise.
  26. Deals with:
  27. True: 1, true, t, yes, y, on
  28. False: 0, false, f, no, n, off
  29. """
  30. if value in {False, True}:
  31. return bool(value)
  32. test_value = str(value).strip().lower()
  33. if test_value in {"1", "true", "t", "yes", "y", "on"}:
  34. return True
  35. if test_value in {"0", "false", "f", "no", "n", "off"}:
  36. return False
  37. return value_if_unknown
  38. @contextlib.contextmanager
  39. def parallelize():
  40. """Returns a function that can be used in place of a map() call.
  41. Attempts to use `mpire`, falling back to `multiprocessing` if it's not
  42. available. If parallelization is not requested, returns the original map()
  43. function.
  44. """
  45. # Work out if we've already got a config value for parallel searching
  46. if cli.config.user.parallel_search is None:
  47. parallel_search = True
  48. else:
  49. parallel_search = cli.config.user.parallel_search
  50. # Non-parallel searches use `map()`
  51. if not parallel_search:
  52. yield map
  53. return
  54. # Prefer mpire's `WorkerPool` if it's available
  55. with contextlib.suppress(ImportError):
  56. from mpire import WorkerPool
  57. from mpire.utils import make_single_arguments
  58. with WorkerPool() as pool:
  59. def _worker(func, *args):
  60. # Ensure we don't unpack tuples -- mpire's `WorkerPool` tries to do so normally so we tell it not to.
  61. for r in pool.imap_unordered(func, make_single_arguments(*args, generator=False), progress_bar=True):
  62. yield r
  63. yield _worker
  64. return
  65. # Otherwise fall back to multiprocessing's `Pool`
  66. with multiprocessing.Pool() as pool:
  67. yield pool.imap_unordered
  68. def parallel_map(*args, **kwargs):
  69. """Effectively runs `map()` but executes it in parallel if necessary.
  70. """
  71. with parallelize() as map_fn:
  72. # This needs to be enclosed in a `list()` as some implementations return
  73. # a generator function, which means the scope of the pool is closed off
  74. # before the results are returned. Returning a list ensures results are
  75. # materialised before any worker pool is shut down.
  76. return list(map_fn(*args, **kwargs))
  77. def triplet_to_bcd(ver: str):
  78. m = TRIPLET_PATTERN.match(ver)
  79. if not m:
  80. return '0x00000000'
  81. return f'0x{int(m.group(1)):02d}{int(m.group(2)):02d}{int(m.group(3)):04d}'