lib.py 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. from __future__ import print_function
  2. import os
  3. import sys
  4. import locale
  5. import random
  6. import time
  7. import signal
  8. from contextlib import contextmanager
  9. @contextmanager
  10. def default_sigint():
  11. original_sigint_handler = signal.getsignal(signal.SIGINT)
  12. signal.signal(signal.SIGINT, signal.SIG_DFL)
  13. try:
  14. yield
  15. finally:
  16. signal.signal(signal.SIGINT, original_sigint_handler)
  17. def get_encoding():
  18. return locale.getpreferredencoding()
  19. def to_utf8(s):
  20. """Re-encode string from the default system encoding to UTF-8."""
  21. current = locale.getpreferredencoding()
  22. if hasattr(s, 'decode'): #Python 3 workaround
  23. return (s.decode(current).encode("UTF-8") if s and current != "UTF-8" else s)
  24. elif isinstance(s, bytes):
  25. return bytes.decode(s)
  26. else:
  27. return s
  28. def debug(obj, fd=sys.stderr):
  29. """Write obj to standard error."""
  30. print(obj, file=fd)
  31. def catch_exceptions(exit_codes, fun, *args, **kwargs):
  32. """
  33. Catch exceptions on fun(*args, **kwargs) and return the exit code specified
  34. in the exit_codes dictionary. Return 0 if no exception is raised.
  35. """
  36. try:
  37. fun(*args, **kwargs)
  38. return 0
  39. except tuple(exit_codes.keys()) as exc:
  40. debug("[{0}] {1}".format(exc.__class__.__name__, exc))
  41. return exit_codes[exc.__class__]
  42. def first(it):
  43. """Return first element in iterable."""
  44. return it.next()
  45. def string_to_dict(string):
  46. """Return dictionary from string "key1=value1, key2=value2"."""
  47. if string:
  48. pairs = [s.strip() for s in string.split(",")]
  49. return dict(pair.split("=") for pair in pairs)
  50. def get_first_existing_filename(prefixes, relative_path):
  51. """Get the first existing filename of relative_path seeking on prefixes directories."""
  52. for prefix in prefixes:
  53. path = os.path.join(prefix, relative_path)
  54. if os.path.exists(path):
  55. return path
  56. def retriable_exceptions(fun, retriable_exceptions, max_retries=None):
  57. """Run function and retry on some exceptions (with exponential backoff)."""
  58. retry = 0
  59. while 1:
  60. try:
  61. return fun()
  62. except tuple(retriable_exceptions) as exc:
  63. retry += 1
  64. if type(exc) not in retriable_exceptions:
  65. raise exc
  66. elif max_retries is not None and retry > max_retries:
  67. debug("[Retryable errors] Retry limit reached")
  68. raise exc
  69. else:
  70. seconds = random.uniform(0, 2**retry)
  71. message = ("[Retryable error {current_retry}/{total_retries}] " +
  72. "{error_type} ({error_msg}). Wait {wait_time} seconds").format(
  73. current_retry=retry,
  74. total_retries=max_retries or "-",
  75. error_type=type(exc).__name__,
  76. error_msg=str(exc) or "-",
  77. wait_time="%.1f" % seconds,
  78. )
  79. debug(message)
  80. time.sleep(seconds)