pyzmq/zmq/error.py

211 lines
5.2 KiB
Python

"""0MQ Error classes and functions."""
# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.
from __future__ import annotations
from errno import EINTR
class ZMQBaseError(Exception):
"""Base exception class for 0MQ errors in Python."""
class ZMQError(ZMQBaseError):
"""Wrap an errno style error.
Parameters
----------
errno : int
The ZMQ errno or None. If None, then ``zmq_errno()`` is called and
used.
msg : str
Description of the error or None.
"""
errno: int | None = None
def __init__(self, errno: int | None = None, msg: str | None = None):
"""Wrap an errno style error.
Parameters
----------
errno : int
The ZMQ errno or None. If None, then ``zmq_errno()`` is called and
used.
msg : string
Description of the error or None.
"""
from zmq.backend import strerror, zmq_errno
if errno is None:
errno = zmq_errno()
if isinstance(errno, int):
self.errno = errno
if msg is None:
self.strerror = strerror(errno)
else:
self.strerror = msg
else:
if msg is None:
self.strerror = str(errno)
else:
self.strerror = msg
# flush signals, because there could be a SIGINT
# waiting to pounce, resulting in uncaught exceptions.
# Doing this here means getting SIGINT during a blocking
# libzmq call will raise a *catchable* KeyboardInterrupt
# PyErr_CheckSignals()
def __str__(self) -> str:
return self.strerror
def __repr__(self) -> str:
return f"{self.__class__.__name__}('{str(self)}')"
class ZMQBindError(ZMQBaseError):
"""An error for ``Socket.bind_to_random_port()``.
See Also
--------
.Socket.bind_to_random_port
"""
class NotDone(ZMQBaseError):
"""Raised when timeout is reached while waiting for 0MQ to finish with a Message
See Also
--------
.MessageTracker.wait : object for tracking when ZeroMQ is done
"""
class ContextTerminated(ZMQError):
"""Wrapper for zmq.ETERM
.. versionadded:: 13.0
"""
def __init__(self, errno="ignored", msg="ignored"):
from zmq import ETERM
super().__init__(ETERM)
class Again(ZMQError):
"""Wrapper for zmq.EAGAIN
.. versionadded:: 13.0
"""
def __init__(self, errno="ignored", msg="ignored"):
from zmq import EAGAIN
super().__init__(EAGAIN)
class InterruptedSystemCall(ZMQError, InterruptedError):
"""Wrapper for EINTR
This exception should be caught internally in pyzmq
to retry system calls, and not propagate to the user.
.. versionadded:: 14.7
"""
errno = EINTR
def __init__(self, errno="ignored", msg="ignored"):
super().__init__(EINTR)
def __str__(self):
s = super().__str__()
return s + ": This call should have been retried. Please report this to pyzmq."
def _check_rc(rc, errno=None, error_without_errno=True):
"""internal utility for checking zmq return condition
and raising the appropriate Exception class
"""
if rc == -1:
if errno is None:
from zmq.backend import zmq_errno
errno = zmq_errno()
if errno == 0 and not error_without_errno:
return
from zmq import EAGAIN, ETERM
if errno == EINTR:
raise InterruptedSystemCall(errno)
elif errno == EAGAIN:
raise Again(errno)
elif errno == ETERM:
raise ContextTerminated(errno)
else:
raise ZMQError(errno)
_zmq_version_info = None
_zmq_version = None
class ZMQVersionError(NotImplementedError):
"""Raised when a feature is not provided by the linked version of libzmq.
.. versionadded:: 14.2
"""
min_version = None
def __init__(self, min_version: str, msg: str = "Feature"):
global _zmq_version
if _zmq_version is None:
from zmq import zmq_version
_zmq_version = zmq_version()
self.msg = msg
self.min_version = min_version
self.version = _zmq_version
def __repr__(self):
return f"ZMQVersionError('{str(self)}')"
def __str__(self):
return f"{self.msg} requires libzmq >= {self.min_version}, have {self.version}"
def _check_version(
min_version_info: tuple[int] | tuple[int, int] | tuple[int, int, int],
msg: str = "Feature",
):
"""Check for libzmq
raises ZMQVersionError if current zmq version is not at least min_version
min_version_info is a tuple of integers, and will be compared against zmq.zmq_version_info().
"""
global _zmq_version_info
if _zmq_version_info is None:
from zmq import zmq_version_info
_zmq_version_info = zmq_version_info()
if _zmq_version_info < min_version_info:
min_version = ".".join(str(v) for v in min_version_info)
raise ZMQVersionError(min_version, msg)
__all__ = [
"ZMQBaseError",
"ZMQBindError",
"ZMQError",
"NotDone",
"ContextTerminated",
"InterruptedSystemCall",
"Again",
"ZMQVersionError",
]