PEP-734 has been accepted (for 3.14). (FTR, I'm opposed to putting this under the concurrent package, but doing so is the SC condition under which the module can land in 3.14.)
103 lines
2.8 KiB
Python
103 lines
2.8 KiB
Python
"""Common code between queues and channels."""
|
|
|
|
|
|
class ItemInterpreterDestroyed(Exception):
|
|
"""Raised when trying to get an item whose interpreter was destroyed."""
|
|
|
|
|
|
class classonly:
|
|
"""A non-data descriptor that makes a value only visible on the class.
|
|
|
|
This is like the "classmethod" builtin, but does not show up on
|
|
instances of the class. It may be used as a decorator.
|
|
"""
|
|
|
|
def __init__(self, value):
|
|
self.value = value
|
|
self.getter = classmethod(value).__get__
|
|
self.name = None
|
|
|
|
def __set_name__(self, cls, name):
|
|
if self.name is not None:
|
|
raise TypeError('already used')
|
|
self.name = name
|
|
|
|
def __get__(self, obj, cls):
|
|
if obj is not None:
|
|
raise AttributeError(self.name)
|
|
# called on the class
|
|
return self.getter(None, cls)
|
|
|
|
|
|
class UnboundItem:
|
|
"""Represents a cross-interpreter item no longer bound to an interpreter.
|
|
|
|
An item is unbound when the interpreter that added it to the
|
|
cross-interpreter container is destroyed.
|
|
"""
|
|
|
|
__slots__ = ()
|
|
|
|
@classonly
|
|
def singleton(cls, kind, module, name='UNBOUND'):
|
|
doc = cls.__doc__.replace('cross-interpreter container', kind)
|
|
doc = doc.replace('cross-interpreter', kind)
|
|
subclass = type(
|
|
f'Unbound{kind.capitalize()}Item',
|
|
(cls,),
|
|
dict(
|
|
_MODULE=module,
|
|
_NAME=name,
|
|
__doc__=doc,
|
|
),
|
|
)
|
|
return object.__new__(subclass)
|
|
|
|
_MODULE = __name__
|
|
_NAME = 'UNBOUND'
|
|
|
|
def __new__(cls):
|
|
raise Exception(f'use {cls._MODULE}.{cls._NAME}')
|
|
|
|
def __repr__(self):
|
|
return f'{self._MODULE}.{self._NAME}'
|
|
# return f'interpreters._queues.UNBOUND'
|
|
|
|
|
|
UNBOUND = object.__new__(UnboundItem)
|
|
UNBOUND_ERROR = object()
|
|
UNBOUND_REMOVE = object()
|
|
|
|
_UNBOUND_CONSTANT_TO_FLAG = {
|
|
UNBOUND_REMOVE: 1,
|
|
UNBOUND_ERROR: 2,
|
|
UNBOUND: 3,
|
|
}
|
|
_UNBOUND_FLAG_TO_CONSTANT = {v: k
|
|
for k, v in _UNBOUND_CONSTANT_TO_FLAG.items()}
|
|
|
|
|
|
def serialize_unbound(unbound):
|
|
op = unbound
|
|
try:
|
|
flag = _UNBOUND_CONSTANT_TO_FLAG[op]
|
|
except KeyError:
|
|
raise NotImplementedError(f'unsupported unbound replacement op {op!r}')
|
|
return flag,
|
|
|
|
|
|
def resolve_unbound(flag, exctype_destroyed):
|
|
try:
|
|
op = _UNBOUND_FLAG_TO_CONSTANT[flag]
|
|
except KeyError:
|
|
raise NotImplementedError(f'unsupported unbound replacement op {flag!r}')
|
|
if op is UNBOUND_REMOVE:
|
|
# "remove" not possible here
|
|
raise NotImplementedError
|
|
elif op is UNBOUND_ERROR:
|
|
raise exctype_destroyed("item's original interpreter destroyed")
|
|
elif op is UNBOUND:
|
|
return UNBOUND
|
|
else:
|
|
raise NotImplementedError(repr(op))
|