| """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__ | |
| if doc: | |
| doc = doc.replace( | |
| 'cross-interpreter container', kind, | |
| ).replace( | |
| 'cross-interpreter', kind, | |
| ) | |
| subclass = type( | |
| f'Unbound{kind.capitalize()}Item', | |
| (cls,), | |
| { | |
| "_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)) |