lib.py 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. import sys
  2. import locale
  3. import random
  4. import time
  5. def to_utf8(s):
  6. """Re-encode string from the default system encoding to UTF-8."""
  7. current = locale.getpreferredencoding()
  8. return s.decode(current).encode("UTF-8") if s and current != "UTF-8" else s
  9. def debug(obj, fd=sys.stderr):
  10. """Write obj to standard error."""
  11. string = str(obj.encode(get_encoding(fd), "backslashreplace")
  12. if isinstance(obj, unicode) else obj)
  13. fd.write(string + "\n")
  14. def catch_exceptions(exit_codes, fun, *args, **kwargs):
  15. """
  16. Catch exceptions on fun(*args, **kwargs) and return the exit code specified
  17. in the exit_codes dictionary. Return 0 if no exception is raised.
  18. """
  19. try:
  20. fun(*args, **kwargs)
  21. return 0
  22. except tuple(exit_codes.keys()) as exc:
  23. debug("[%s] %s" % (exc.__class__.__name__, exc))
  24. return exit_codes[exc.__class__]
  25. def get_encoding(fd):
  26. """Guess terminal encoding."""
  27. return fd.encoding or locale.getpreferredencoding()
  28. def first(it):
  29. """Return first element in iterable."""
  30. return it.next()
  31. def string_to_dict(string):
  32. """Return dictionary from string "key1=value1, key2=value2"."""
  33. if string:
  34. pairs = [s.strip() for s in string.split(",")]
  35. return dict(pair.split("=") for pair in pairs)
  36. def retriable_exceptions(fun, retriable_exceptions, max_retries=None):
  37. """Run function and retry on some exceptions (with exponential backoff)."""
  38. retry = 0
  39. while 1:
  40. try:
  41. return fun()
  42. except tuple(retriable_exceptions) as exc:
  43. retry += 1
  44. if type(exc) not in retriable_exceptions:
  45. raise exc
  46. elif max_retries is not None and retry > max_retries:
  47. debug("Retry limit reached, time to give up")
  48. raise exc
  49. else:
  50. seconds = random.uniform(0, 2**retry)
  51. debug("Retryable error {0}/{1}: {2}. Waiting {3} seconds".
  52. format(retry, max_retries or "-", type(exc).__name__, seconds))
  53. time.sleep(seconds)