mirror of
https://github.com/python/cpython.git
synced 2025-07-20 01:35:19 +00:00

See 6b98b274b6
for an explanation of the problem and solution. Here I've applied the solution to channels.
102 lines
2.8 KiB
Python
102 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))
|