Updates
This commit is contained in:
241
ETB-API/venv/lib/python3.12/site-packages/vine/promises.py
Normal file
241
ETB-API/venv/lib/python3.12/site-packages/vine/promises.py
Normal file
@@ -0,0 +1,241 @@
|
||||
"""Promise implementation."""
|
||||
import inspect
|
||||
import sys
|
||||
from collections import deque
|
||||
from weakref import WeakMethod, ref
|
||||
|
||||
from .abstract import Thenable
|
||||
from .utils import reraise
|
||||
|
||||
__all__ = ['promise']
|
||||
|
||||
|
||||
@Thenable.register
|
||||
class promise:
|
||||
"""Promise of future evaluation.
|
||||
|
||||
This is a special implementation of promises in that it can
|
||||
be used both for "promise of a value" and lazy evaluation.
|
||||
The biggest upside for this is that everything in a promise can also be
|
||||
a promise, e.g. filters, callbacks and errbacks can all be promises.
|
||||
|
||||
Usage examples:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> p = promise()
|
||||
>>> p.then(promise(print, ('OK',))) # noqa
|
||||
>>> p.on_error = promise(print, ('ERROR',)) # noqa
|
||||
>>> p(20)
|
||||
OK, 20
|
||||
>>> p.then(promise(print, ('hello',))) # noqa
|
||||
hello, 20
|
||||
|
||||
|
||||
>>> p.throw(KeyError('foo'))
|
||||
ERROR, KeyError('foo')
|
||||
|
||||
|
||||
>>> p2 = promise()
|
||||
>>> p2.then(print) # noqa
|
||||
>>> p2.cancel()
|
||||
>>> p(30)
|
||||
|
||||
Example:
|
||||
.. code-block:: python
|
||||
|
||||
from vine import promise, wrap
|
||||
|
||||
class Protocol:
|
||||
|
||||
def __init__(self):
|
||||
self.buffer = []
|
||||
|
||||
def receive_message(self):
|
||||
return self.read_header().then(
|
||||
self.read_body).then(
|
||||
wrap(self.prepare_body))
|
||||
|
||||
def read(self, size, callback=None):
|
||||
callback = callback or promise()
|
||||
tell_eventloop_to_read(size, callback)
|
||||
return callback
|
||||
|
||||
def read_header(self, callback=None):
|
||||
return self.read(4, callback)
|
||||
|
||||
def read_body(self, header, callback=None):
|
||||
body_size, = unpack('>L', header)
|
||||
return self.read(body_size, callback)
|
||||
|
||||
def prepare_body(self, value):
|
||||
self.buffer.append(value)
|
||||
"""
|
||||
|
||||
if not hasattr(sys, 'pypy_version_info'): # pragma: no cover
|
||||
__slots__ = (
|
||||
'fun', 'args', 'kwargs', 'ready', 'failed',
|
||||
'value', 'ignore_result', 'reason', '_svpending', '_lvpending',
|
||||
'on_error', 'cancelled', 'weak', '__weakref__',
|
||||
# adding '__dict__' to get dynamic assignment if needed
|
||||
"__dict__",
|
||||
)
|
||||
|
||||
def __init__(self, fun=None, args=None, kwargs=None,
|
||||
callback=None, on_error=None, weak=False,
|
||||
ignore_result=False):
|
||||
self.weak = weak
|
||||
self.ignore_result = ignore_result
|
||||
self.fun = self._get_fun_or_weakref(fun=fun, weak=weak)
|
||||
self.args = args or ()
|
||||
self.kwargs = kwargs or {}
|
||||
self.ready = False
|
||||
self.failed = False
|
||||
self.value = None
|
||||
self.reason = None
|
||||
# Optimization
|
||||
# Most promises will only have one callback, so we optimize for this
|
||||
# case by using a list only when there are multiple callbacks.
|
||||
# s(calar) pending / l(ist) pending
|
||||
self._svpending = None
|
||||
self._lvpending = None
|
||||
self.on_error = on_error
|
||||
self.cancelled = False
|
||||
|
||||
if callback is not None:
|
||||
self.then(callback)
|
||||
|
||||
if self.fun:
|
||||
assert self.fun and callable(fun)
|
||||
|
||||
@staticmethod
|
||||
def _get_fun_or_weakref(fun, weak):
|
||||
"""Return the callable or a weak reference.
|
||||
|
||||
Handles both bound and unbound methods.
|
||||
"""
|
||||
if not weak:
|
||||
return fun
|
||||
|
||||
if inspect.ismethod(fun):
|
||||
return WeakMethod(fun)
|
||||
else:
|
||||
return ref(fun)
|
||||
|
||||
def __repr__(self):
|
||||
return ('<{0} --> {1!r}>' if self.fun else '<{0}>').format(
|
||||
f'{type(self).__name__}@0x{id(self):x}', self.fun,
|
||||
)
|
||||
|
||||
def cancel(self):
|
||||
self.cancelled = True
|
||||
try:
|
||||
if self._svpending is not None:
|
||||
self._svpending.cancel()
|
||||
if self._lvpending is not None:
|
||||
for pending in self._lvpending:
|
||||
pending.cancel()
|
||||
if isinstance(self.on_error, Thenable):
|
||||
self.on_error.cancel()
|
||||
finally:
|
||||
self._svpending = self._lvpending = self.on_error = None
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
retval = None
|
||||
if self.cancelled:
|
||||
return
|
||||
final_args = self.args + args if args else self.args
|
||||
final_kwargs = dict(self.kwargs, **kwargs) if kwargs else self.kwargs
|
||||
# self.fun may be a weakref
|
||||
fun = self._fun_is_alive(self.fun)
|
||||
if fun is not None:
|
||||
try:
|
||||
if self.ignore_result:
|
||||
fun(*final_args, **final_kwargs)
|
||||
ca = ()
|
||||
ck = {}
|
||||
else:
|
||||
retval = fun(*final_args, **final_kwargs)
|
||||
self.value = (ca, ck) = (retval,), {}
|
||||
except Exception:
|
||||
return self.throw()
|
||||
else:
|
||||
self.value = (ca, ck) = final_args, final_kwargs
|
||||
self.ready = True
|
||||
svpending = self._svpending
|
||||
if svpending is not None:
|
||||
try:
|
||||
svpending(*ca, **ck)
|
||||
finally:
|
||||
self._svpending = None
|
||||
else:
|
||||
lvpending = self._lvpending
|
||||
try:
|
||||
while lvpending:
|
||||
p = lvpending.popleft()
|
||||
p(*ca, **ck)
|
||||
finally:
|
||||
self._lvpending = None
|
||||
return retval
|
||||
|
||||
def _fun_is_alive(self, fun):
|
||||
return fun() if self.weak else self.fun
|
||||
|
||||
def then(self, callback, on_error=None):
|
||||
if not isinstance(callback, Thenable):
|
||||
callback = promise(callback, on_error=on_error)
|
||||
if self.cancelled:
|
||||
callback.cancel()
|
||||
return callback
|
||||
if self.failed:
|
||||
callback.throw(self.reason)
|
||||
elif self.ready:
|
||||
args, kwargs = self.value
|
||||
callback(*args, **kwargs)
|
||||
if self._lvpending is None:
|
||||
svpending = self._svpending
|
||||
if svpending is not None:
|
||||
self._svpending, self._lvpending = None, deque([svpending])
|
||||
else:
|
||||
self._svpending = callback
|
||||
return callback
|
||||
self._lvpending.append(callback)
|
||||
return callback
|
||||
|
||||
def throw1(self, exc=None):
|
||||
if not self.cancelled:
|
||||
exc = exc if exc is not None else sys.exc_info()[1]
|
||||
self.failed, self.reason = True, exc
|
||||
if self.on_error:
|
||||
self.on_error(*self.args + (exc,), **self.kwargs)
|
||||
|
||||
def throw(self, exc=None, tb=None, propagate=True):
|
||||
if not self.cancelled:
|
||||
current_exc = sys.exc_info()[1]
|
||||
exc = exc if exc is not None else current_exc
|
||||
try:
|
||||
self.throw1(exc)
|
||||
svpending = self._svpending
|
||||
if svpending is not None:
|
||||
try:
|
||||
svpending.throw1(exc)
|
||||
finally:
|
||||
self._svpending = None
|
||||
else:
|
||||
lvpending = self._lvpending
|
||||
try:
|
||||
while lvpending:
|
||||
lvpending.popleft().throw1(exc)
|
||||
finally:
|
||||
self._lvpending = None
|
||||
finally:
|
||||
if self.on_error is None and propagate:
|
||||
if tb is None and (exc is None or exc is current_exc):
|
||||
raise
|
||||
reraise(type(exc), exc, tb)
|
||||
|
||||
@property
|
||||
def listeners(self):
|
||||
if self._lvpending:
|
||||
return self._lvpending
|
||||
return [self._svpending]
|
||||
Reference in New Issue
Block a user