图片解析应用
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.

70 lines
2.2 KiB

  1. import socket
  2. from time import sleep
  3. from typing import TYPE_CHECKING, Any, Callable, Iterable, Tuple, Type, TypeVar
  4. from redis.exceptions import ConnectionError, TimeoutError
  5. T = TypeVar("T")
  6. if TYPE_CHECKING:
  7. from redis.backoff import AbstractBackoff
  8. class Retry:
  9. """Retry a specific number of times after a failure"""
  10. def __init__(
  11. self,
  12. backoff: "AbstractBackoff",
  13. retries: int,
  14. supported_errors: Tuple[Type[Exception], ...] = (
  15. ConnectionError,
  16. TimeoutError,
  17. socket.timeout,
  18. ),
  19. ):
  20. """
  21. Initialize a `Retry` object with a `Backoff` object
  22. that retries a maximum of `retries` times.
  23. `retries` can be negative to retry forever.
  24. You can specify the types of supported errors which trigger
  25. a retry with the `supported_errors` parameter.
  26. """
  27. self._backoff = backoff
  28. self._retries = retries
  29. self._supported_errors = supported_errors
  30. def update_supported_errors(
  31. self, specified_errors: Iterable[Type[Exception]]
  32. ) -> None:
  33. """
  34. Updates the supported errors with the specified error types
  35. """
  36. self._supported_errors = tuple(
  37. set(self._supported_errors + tuple(specified_errors))
  38. )
  39. def call_with_retry(
  40. self,
  41. do: Callable[[], T],
  42. fail: Callable[[Exception], Any],
  43. ) -> T:
  44. """
  45. Execute an operation that might fail and returns its result, or
  46. raise the exception that was thrown depending on the `Backoff` object.
  47. `do`: the operation to call. Expects no argument.
  48. `fail`: the failure handler, expects the last error that was thrown
  49. """
  50. self._backoff.reset()
  51. failures = 0
  52. while True:
  53. try:
  54. return do()
  55. except self._supported_errors as error:
  56. failures += 1
  57. fail(error)
  58. if self._retries >= 0 and failures > self._retries:
  59. raise error
  60. backoff = self._backoff.compute(failures)
  61. if backoff > 0:
  62. sleep(backoff)