2023-12-12 11:24:31 -04:00
|
|
|
"""Cross-interpreter Queues High Level Module."""
|
|
|
|
|
|
|
|
import queue
|
|
|
|
import time
|
|
|
|
import weakref
|
2023-12-12 13:43:30 -04:00
|
|
|
import _xxinterpqueues as _queues
|
2023-12-12 11:24:31 -04:00
|
|
|
|
|
|
|
# aliases:
|
2023-12-12 13:43:30 -04:00
|
|
|
from _xxinterpqueues import (
|
|
|
|
QueueError, QueueNotFoundError,
|
2023-12-12 11:24:31 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
__all__ = [
|
|
|
|
'create', 'list_all',
|
|
|
|
'Queue',
|
|
|
|
'QueueError', 'QueueNotFoundError', 'QueueEmpty', 'QueueFull',
|
|
|
|
]
|
|
|
|
|
|
|
|
|
2023-12-12 13:43:30 -04:00
|
|
|
class QueueEmpty(_queues.QueueEmpty, queue.Empty):
|
|
|
|
"""Raised from get_nowait() when the queue is empty.
|
|
|
|
|
|
|
|
It is also raised from get() if it times out.
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
class QueueFull(_queues.QueueFull, queue.Full):
|
|
|
|
"""Raised from put_nowait() when the queue is full.
|
|
|
|
|
|
|
|
It is also raised from put() if it times out.
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
2023-12-12 11:24:31 -04:00
|
|
|
def create(maxsize=0):
|
|
|
|
"""Return a new cross-interpreter queue.
|
|
|
|
|
|
|
|
The queue may be used to pass data safely between interpreters.
|
|
|
|
"""
|
2023-12-12 13:43:30 -04:00
|
|
|
qid = _queues.create(maxsize)
|
|
|
|
return Queue(qid)
|
2023-12-12 11:24:31 -04:00
|
|
|
|
|
|
|
|
|
|
|
def list_all():
|
|
|
|
"""Return a list of all open queues."""
|
|
|
|
return [Queue(qid)
|
|
|
|
for qid in _queues.list_all()]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_known_queues = weakref.WeakValueDictionary()
|
|
|
|
|
|
|
|
class Queue:
|
|
|
|
"""A cross-interpreter queue."""
|
|
|
|
|
|
|
|
def __new__(cls, id, /):
|
|
|
|
# There is only one instance for any given ID.
|
|
|
|
if isinstance(id, int):
|
2023-12-12 13:43:30 -04:00
|
|
|
id = int(id)
|
|
|
|
else:
|
2023-12-12 11:24:31 -04:00
|
|
|
raise TypeError(f'id must be an int, got {id!r}')
|
|
|
|
try:
|
2023-12-12 13:43:30 -04:00
|
|
|
self = _known_queues[id]
|
2023-12-12 11:24:31 -04:00
|
|
|
except KeyError:
|
|
|
|
self = super().__new__(cls)
|
|
|
|
self._id = id
|
2023-12-12 13:43:30 -04:00
|
|
|
_known_queues[id] = self
|
|
|
|
_queues.bind(id)
|
2023-12-12 11:24:31 -04:00
|
|
|
return self
|
|
|
|
|
2023-12-12 13:43:30 -04:00
|
|
|
def __del__(self):
|
|
|
|
try:
|
|
|
|
_queues.release(self._id)
|
|
|
|
except QueueNotFoundError:
|
|
|
|
pass
|
|
|
|
try:
|
|
|
|
del _known_queues[self._id]
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
|
2023-12-12 11:24:31 -04:00
|
|
|
def __repr__(self):
|
|
|
|
return f'{type(self).__name__}({self.id})'
|
|
|
|
|
|
|
|
def __hash__(self):
|
|
|
|
return hash(self._id)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def id(self):
|
2023-12-12 13:43:30 -04:00
|
|
|
return self._id
|
2023-12-12 11:24:31 -04:00
|
|
|
|
|
|
|
@property
|
|
|
|
def maxsize(self):
|
2023-12-12 13:43:30 -04:00
|
|
|
try:
|
|
|
|
return self._maxsize
|
|
|
|
except AttributeError:
|
|
|
|
self._maxsize = _queues.get_maxsize(self._id)
|
|
|
|
return self._maxsize
|
2023-12-12 11:24:31 -04:00
|
|
|
|
|
|
|
def empty(self):
|
2023-12-12 13:43:30 -04:00
|
|
|
return self.qsize() == 0
|
2023-12-12 11:24:31 -04:00
|
|
|
|
|
|
|
def full(self):
|
2023-12-12 13:43:30 -04:00
|
|
|
return _queues.is_full(self._id)
|
2023-12-12 11:24:31 -04:00
|
|
|
|
|
|
|
def qsize(self):
|
2023-12-12 13:43:30 -04:00
|
|
|
return _queues.get_count(self._id)
|
2023-12-12 11:24:31 -04:00
|
|
|
|
2023-12-12 13:43:30 -04:00
|
|
|
def put(self, obj, timeout=None, *,
|
|
|
|
_delay=10 / 1000, # 10 milliseconds
|
|
|
|
):
|
|
|
|
"""Add the object to the queue.
|
|
|
|
|
|
|
|
This blocks while the queue is full.
|
|
|
|
"""
|
|
|
|
if timeout is not None:
|
|
|
|
timeout = int(timeout)
|
|
|
|
if timeout < 0:
|
|
|
|
raise ValueError(f'timeout value must be non-negative')
|
|
|
|
end = time.time() + timeout
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
_queues.put(self._id, obj)
|
|
|
|
except _queues.QueueFull as exc:
|
|
|
|
if timeout is not None and time.time() >= end:
|
|
|
|
exc.__class__ = QueueFull
|
|
|
|
raise # re-raise
|
|
|
|
time.sleep(_delay)
|
|
|
|
else:
|
|
|
|
break
|
2023-12-12 11:24:31 -04:00
|
|
|
|
|
|
|
def put_nowait(self, obj):
|
2023-12-12 13:43:30 -04:00
|
|
|
try:
|
|
|
|
return _queues.put(self._id, obj)
|
|
|
|
except _queues.QueueFull as exc:
|
|
|
|
exc.__class__ = QueueFull
|
|
|
|
raise # re-raise
|
2023-12-12 11:24:31 -04:00
|
|
|
|
|
|
|
def get(self, timeout=None, *,
|
2023-12-12 13:43:30 -04:00
|
|
|
_delay=10 / 1000, # 10 milliseconds
|
|
|
|
):
|
2023-12-12 11:24:31 -04:00
|
|
|
"""Return the next object from the queue.
|
|
|
|
|
|
|
|
This blocks while the queue is empty.
|
|
|
|
"""
|
|
|
|
if timeout is not None:
|
|
|
|
timeout = int(timeout)
|
|
|
|
if timeout < 0:
|
|
|
|
raise ValueError(f'timeout value must be non-negative')
|
|
|
|
end = time.time() + timeout
|
2023-12-12 13:43:30 -04:00
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
return _queues.get(self._id)
|
|
|
|
except _queues.QueueEmpty as exc:
|
|
|
|
if timeout is not None and time.time() >= end:
|
|
|
|
exc.__class__ = QueueEmpty
|
|
|
|
raise # re-raise
|
|
|
|
time.sleep(_delay)
|
2023-12-12 11:24:31 -04:00
|
|
|
return obj
|
|
|
|
|
2023-12-12 13:43:30 -04:00
|
|
|
def get_nowait(self):
|
2023-12-12 11:24:31 -04:00
|
|
|
"""Return the next object from the channel.
|
|
|
|
|
|
|
|
If the queue is empty then raise QueueEmpty. Otherwise this
|
|
|
|
is the same as get().
|
|
|
|
"""
|
2023-12-12 13:43:30 -04:00
|
|
|
try:
|
|
|
|
return _queues.get(self._id)
|
|
|
|
except _queues.QueueEmpty as exc:
|
|
|
|
exc.__class__ = QueueEmpty
|
|
|
|
raise # re-raise
|
2023-12-12 11:24:31 -04:00
|
|
|
|
|
|
|
|
2023-12-12 13:43:30 -04:00
|
|
|
_queues._register_queue_type(Queue)
|