| """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)) |