error.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. """0MQ Error classes and functions."""
  2. # Copyright (C) PyZMQ Developers
  3. # Distributed under the terms of the Modified BSD License.
  4. from __future__ import annotations
  5. from errno import EINTR
  6. class DraftFDWarning(RuntimeWarning):
  7. """Warning for using experimental FD on draft sockets.
  8. .. versionadded:: 27
  9. """
  10. def __init__(self, msg=""):
  11. if not msg:
  12. msg = (
  13. "pyzmq's back-fill socket.FD support on thread-safe sockets is experimental, and may be removed."
  14. " This warning will go away automatically if/when libzmq implements socket.FD on thread-safe sockets."
  15. " You can suppress this warning with `warnings.simplefilter('ignore', zmq.error.DraftFDWarning)"
  16. )
  17. super().__init__(msg)
  18. class ZMQBaseError(Exception):
  19. """Base exception class for 0MQ errors in Python."""
  20. class ZMQError(ZMQBaseError):
  21. """Wrap an errno style error.
  22. Parameters
  23. ----------
  24. errno : int
  25. The ZMQ errno or None. If None, then ``zmq_errno()`` is called and
  26. used.
  27. msg : str
  28. Description of the error or None.
  29. """
  30. errno: int | None = None
  31. strerror: str
  32. def __init__(self, errno: int | None = None, msg: str | None = None):
  33. """Wrap an errno style error.
  34. Parameters
  35. ----------
  36. errno : int
  37. The ZMQ errno or None. If None, then ``zmq_errno()`` is called and
  38. used.
  39. msg : string
  40. Description of the error or None.
  41. """
  42. from zmq.backend import strerror, zmq_errno
  43. if errno is None:
  44. errno = zmq_errno()
  45. if isinstance(errno, int):
  46. self.errno = errno
  47. if msg is None:
  48. self.strerror = strerror(errno)
  49. else:
  50. self.strerror = msg
  51. else:
  52. if msg is None:
  53. self.strerror = str(errno)
  54. else:
  55. self.strerror = msg
  56. # flush signals, because there could be a SIGINT
  57. # waiting to pounce, resulting in uncaught exceptions.
  58. # Doing this here means getting SIGINT during a blocking
  59. # libzmq call will raise a *catchable* KeyboardInterrupt
  60. # PyErr_CheckSignals()
  61. def __str__(self) -> str:
  62. return self.strerror
  63. def __repr__(self) -> str:
  64. return f"{self.__class__.__name__}('{str(self)}')"
  65. class ZMQBindError(ZMQBaseError):
  66. """An error for ``Socket.bind_to_random_port()``.
  67. See Also
  68. --------
  69. .Socket.bind_to_random_port
  70. """
  71. class NotDone(ZMQBaseError):
  72. """Raised when timeout is reached while waiting for 0MQ to finish with a Message
  73. See Also
  74. --------
  75. .MessageTracker.wait : object for tracking when ZeroMQ is done
  76. """
  77. class ContextTerminated(ZMQError):
  78. """Wrapper for zmq.ETERM
  79. .. versionadded:: 13.0
  80. """
  81. def __init__(self, errno="ignored", msg="ignored"):
  82. from zmq import ETERM
  83. super().__init__(ETERM)
  84. class Again(ZMQError):
  85. """Wrapper for zmq.EAGAIN
  86. .. versionadded:: 13.0
  87. """
  88. def __init__(self, errno="ignored", msg="ignored"):
  89. from zmq import EAGAIN
  90. super().__init__(EAGAIN)
  91. class InterruptedSystemCall(ZMQError, InterruptedError):
  92. """Wrapper for EINTR
  93. This exception should be caught internally in pyzmq
  94. to retry system calls, and not propagate to the user.
  95. .. versionadded:: 14.7
  96. """
  97. errno = EINTR
  98. strerror: str
  99. def __init__(self, errno="ignored", msg="ignored"):
  100. super().__init__(EINTR)
  101. def __str__(self):
  102. s = super().__str__()
  103. return s + ": This call should have been retried. Please report this to pyzmq."
  104. def _check_rc(rc, errno=None, error_without_errno=True):
  105. """internal utility for checking zmq return condition
  106. and raising the appropriate Exception class
  107. """
  108. if rc == -1:
  109. if errno is None:
  110. from zmq.backend import zmq_errno
  111. errno = zmq_errno()
  112. if errno == 0 and not error_without_errno:
  113. return
  114. from zmq import EAGAIN, ETERM
  115. if errno == EINTR:
  116. raise InterruptedSystemCall(errno)
  117. elif errno == EAGAIN:
  118. raise Again(errno)
  119. elif errno == ETERM:
  120. raise ContextTerminated(errno)
  121. else:
  122. raise ZMQError(errno)
  123. _zmq_version_info = None
  124. _zmq_version = None
  125. class ZMQVersionError(NotImplementedError):
  126. """Raised when a feature is not provided by the linked version of libzmq.
  127. .. versionadded:: 14.2
  128. """
  129. min_version = None
  130. def __init__(self, min_version: str, msg: str = "Feature"):
  131. global _zmq_version
  132. if _zmq_version is None:
  133. from zmq import zmq_version
  134. _zmq_version = zmq_version()
  135. self.msg = msg
  136. self.min_version = min_version
  137. self.version = _zmq_version
  138. def __repr__(self):
  139. return f"ZMQVersionError('{str(self)}')"
  140. def __str__(self):
  141. return f"{self.msg} requires libzmq >= {self.min_version}, have {self.version}"
  142. def _check_version(
  143. min_version_info: tuple[int] | tuple[int, int] | tuple[int, int, int],
  144. msg: str = "Feature",
  145. ):
  146. """Check for libzmq
  147. raises ZMQVersionError if current zmq version is not at least min_version
  148. min_version_info is a tuple of integers, and will be compared against zmq.zmq_version_info().
  149. """
  150. global _zmq_version_info
  151. if _zmq_version_info is None:
  152. from zmq import zmq_version_info
  153. _zmq_version_info = zmq_version_info()
  154. if _zmq_version_info < min_version_info:
  155. min_version = ".".join(str(v) for v in min_version_info)
  156. raise ZMQVersionError(min_version, msg)
  157. __all__ = [
  158. "DraftFDWarning",
  159. "ZMQBaseError",
  160. "ZMQBindError",
  161. "ZMQError",
  162. "NotDone",
  163. "ContextTerminated",
  164. "InterruptedSystemCall",
  165. "Again",
  166. "ZMQVersionError",
  167. ]