| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | """Cross-interpreter Queues High Level Module.""" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-28 16:08:08 -07:00
										 |  |  | import pickle | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | import queue | 
					
						
							|  |  |  | import time | 
					
						
							|  |  |  | import weakref | 
					
						
							| 
									
										
										
										
											2024-04-24 10:18:24 -06:00
										 |  |  | import _interpqueues as _queues | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | # aliases: | 
					
						
							| 
									
										
										
										
											2024-04-24 10:18:24 -06:00
										 |  |  | from _interpqueues import ( | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |     QueueError, QueueNotFoundError, | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __all__ = [ | 
					
						
							|  |  |  |     'create', 'list_all', | 
					
						
							|  |  |  |     'Queue', | 
					
						
							|  |  |  |     'QueueError', 'QueueNotFoundError', 'QueueEmpty', 'QueueFull', | 
					
						
							|  |  |  | ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-05 08:54:46 -07:00
										 |  |  | class QueueEmpty(QueueError, queue.Empty): | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |     """Raised from get_nowait() when the queue is empty.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     It is also raised from get() if it times out. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-05 08:54:46 -07:00
										 |  |  | class QueueFull(QueueError, queue.Full): | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |     """Raised from put_nowait() when the queue is full.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     It is also raised from put() if it times out. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-28 16:08:08 -07:00
										 |  |  | _SHARED_ONLY = 0 | 
					
						
							|  |  |  | _PICKLED = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def create(maxsize=0, *, syncobj=False): | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  |     """Return a new cross-interpreter queue.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The queue may be used to pass data safely between interpreters. | 
					
						
							| 
									
										
										
										
											2024-02-28 16:08:08 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     "syncobj" sets the default for Queue.put() | 
					
						
							|  |  |  |     and Queue.put_nowait(). | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2024-02-28 16:08:08 -07:00
										 |  |  |     fmt = _SHARED_ONLY if syncobj else _PICKLED | 
					
						
							|  |  |  |     qid = _queues.create(maxsize, fmt) | 
					
						
							|  |  |  |     return Queue(qid, _fmt=fmt) | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def list_all(): | 
					
						
							|  |  |  |     """Return a list of all open queues.""" | 
					
						
							| 
									
										
										
										
											2024-02-28 16:08:08 -07:00
										 |  |  |     return [Queue(qid, _fmt=fmt) | 
					
						
							|  |  |  |             for qid, fmt in _queues.list_all()] | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _known_queues = weakref.WeakValueDictionary() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Queue: | 
					
						
							|  |  |  |     """A cross-interpreter queue.""" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-28 16:08:08 -07:00
										 |  |  |     def __new__(cls, id, /, *, _fmt=None): | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  |         # There is only one instance for any given ID. | 
					
						
							|  |  |  |         if isinstance(id, int): | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |             id = int(id) | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  |             raise TypeError(f'id must be an int, got {id!r}') | 
					
						
							| 
									
										
										
										
											2024-02-28 16:08:08 -07:00
										 |  |  |         if _fmt is None: | 
					
						
							| 
									
										
										
										
											2024-03-05 08:54:46 -07:00
										 |  |  |             _fmt, = _queues.get_queue_defaults(id) | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |             self = _known_queues[id] | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  |         except KeyError: | 
					
						
							|  |  |  |             self = super().__new__(cls) | 
					
						
							|  |  |  |             self._id = id | 
					
						
							| 
									
										
										
										
											2024-02-28 16:08:08 -07:00
										 |  |  |             self._fmt = _fmt | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |             _known_queues[id] = self | 
					
						
							|  |  |  |             _queues.bind(id) | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |     def __del__(self): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             _queues.release(self._id) | 
					
						
							|  |  |  |         except QueueNotFoundError: | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             del _known_queues[self._id] | 
					
						
							|  |  |  |         except KeyError: | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         return f'{type(self).__name__}({self.id})' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __hash__(self): | 
					
						
							|  |  |  |         return hash(self._id) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-05 08:54:46 -07:00
										 |  |  |     # for pickling: | 
					
						
							|  |  |  |     def __getnewargs__(self): | 
					
						
							|  |  |  |         return (self._id,) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # for pickling: | 
					
						
							|  |  |  |     def __getstate__(self): | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  |     @property | 
					
						
							|  |  |  |     def id(self): | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |         return self._id | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def maxsize(self): | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |         try: | 
					
						
							|  |  |  |             return self._maxsize | 
					
						
							|  |  |  |         except AttributeError: | 
					
						
							|  |  |  |             self._maxsize = _queues.get_maxsize(self._id) | 
					
						
							|  |  |  |             return self._maxsize | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def empty(self): | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |         return self.qsize() == 0 | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def full(self): | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |         return _queues.is_full(self._id) | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def qsize(self): | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |         return _queues.get_count(self._id) | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |     def put(self, obj, timeout=None, *, | 
					
						
							| 
									
										
										
										
											2024-02-28 16:08:08 -07:00
										 |  |  |             syncobj=None, | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |             _delay=10 / 1000,  # 10 milliseconds | 
					
						
							|  |  |  |             ): | 
					
						
							|  |  |  |         """Add the object to the queue.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         This blocks while the queue is full. | 
					
						
							| 
									
										
										
										
											2024-02-28 16:08:08 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         If "syncobj" is None (the default) then it uses the | 
					
						
							|  |  |  |         queue's default, set with create_queue().. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         If "syncobj" is false then all objects are supported, | 
					
						
							|  |  |  |         at the expense of worse performance. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         If "syncobj" is true then the object must be "shareable". | 
					
						
							|  |  |  |         Examples of "shareable" objects include the builtin singletons, | 
					
						
							|  |  |  |         str, and memoryview.  One benefit is that such objects are | 
					
						
							|  |  |  |         passed through the queue efficiently. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The key difference, though, is conceptual: the corresponding | 
					
						
							|  |  |  |         object returned from Queue.get() will be strictly equivalent | 
					
						
							|  |  |  |         to the given obj.  In other words, the two objects will be | 
					
						
							|  |  |  |         effectively indistinguishable from each other, even if the | 
					
						
							|  |  |  |         object is mutable.  The received object may actually be the | 
					
						
							|  |  |  |         same object, or a copy (immutable values only), or a proxy. | 
					
						
							|  |  |  |         Regardless, the received object should be treated as though | 
					
						
							|  |  |  |         the original has been shared directly, whether or not it | 
					
						
							|  |  |  |         actually is.  That's a slightly different and stronger promise | 
					
						
							|  |  |  |         than just (initial) equality, which is all "syncobj=False" | 
					
						
							|  |  |  |         can promise. | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-02-28 16:08:08 -07:00
										 |  |  |         if syncobj is None: | 
					
						
							|  |  |  |             fmt = self._fmt | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             fmt = _SHARED_ONLY if syncobj else _PICKLED | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |         if timeout is not None: | 
					
						
							|  |  |  |             timeout = int(timeout) | 
					
						
							|  |  |  |             if timeout < 0: | 
					
						
							|  |  |  |                 raise ValueError(f'timeout value must be non-negative') | 
					
						
							|  |  |  |             end = time.time() + timeout | 
					
						
							| 
									
										
										
										
											2024-02-28 16:08:08 -07:00
										 |  |  |         if fmt is _PICKLED: | 
					
						
							|  |  |  |             obj = pickle.dumps(obj) | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |         while True: | 
					
						
							|  |  |  |             try: | 
					
						
							| 
									
										
										
										
											2024-02-28 16:08:08 -07:00
										 |  |  |                 _queues.put(self._id, obj, fmt) | 
					
						
							| 
									
										
										
										
											2024-03-05 08:54:46 -07:00
										 |  |  |             except QueueFull as exc: | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |                 if timeout is not None and time.time() >= end: | 
					
						
							|  |  |  |                     raise  # re-raise | 
					
						
							|  |  |  |                 time.sleep(_delay) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 break | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-28 16:08:08 -07:00
										 |  |  |     def put_nowait(self, obj, *, syncobj=None): | 
					
						
							|  |  |  |         if syncobj is None: | 
					
						
							|  |  |  |             fmt = self._fmt | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             fmt = _SHARED_ONLY if syncobj else _PICKLED | 
					
						
							|  |  |  |         if fmt is _PICKLED: | 
					
						
							|  |  |  |             obj = pickle.dumps(obj) | 
					
						
							| 
									
										
										
										
											2024-03-05 08:54:46 -07:00
										 |  |  |         _queues.put(self._id, obj, fmt) | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def get(self, timeout=None, *, | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |             _delay=10 / 1000,  # 10 milliseconds | 
					
						
							|  |  |  |             ): | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07: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 10:43:30 -07:00
										 |  |  |         while True: | 
					
						
							|  |  |  |             try: | 
					
						
							| 
									
										
										
										
											2024-02-28 16:08:08 -07:00
										 |  |  |                 obj, fmt = _queues.get(self._id) | 
					
						
							| 
									
										
										
										
											2024-03-05 08:54:46 -07:00
										 |  |  |             except QueueEmpty as exc: | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |                 if timeout is not None and time.time() >= end: | 
					
						
							|  |  |  |                     raise  # re-raise | 
					
						
							|  |  |  |                 time.sleep(_delay) | 
					
						
							| 
									
										
										
										
											2024-02-28 16:08:08 -07:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |         if fmt == _PICKLED: | 
					
						
							|  |  |  |             obj = pickle.loads(obj) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             assert fmt == _SHARED_ONLY | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  |         return obj | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |     def get_nowait(self): | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07: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 10:43:30 -07:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2024-03-01 09:36:35 -07:00
										 |  |  |             obj, fmt = _queues.get(self._id) | 
					
						
							| 
									
										
										
										
											2024-03-05 08:54:46 -07:00
										 |  |  |         except QueueEmpty as exc: | 
					
						
							| 
									
										
										
										
											2023-12-12 10:43:30 -07:00
										 |  |  |             raise  # re-raise | 
					
						
							| 
									
										
										
										
											2024-03-01 09:36:35 -07:00
										 |  |  |         if fmt == _PICKLED: | 
					
						
							|  |  |  |             obj = pickle.loads(obj) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             assert fmt == _SHARED_ONLY | 
					
						
							|  |  |  |         return obj | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-05 08:54:46 -07:00
										 |  |  | _queues._register_heap_types(Queue, QueueEmpty, QueueFull) |