mirror of https://github.com/python/cpython
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))
|