Python promises library providing specialized promise-based asynchronous operations and lazy evaluation
—
Core abstract classes and proxy objects for creating thenable objects. These provide the foundation for vine's promise system and enable custom implementations of promise-like behavior.
Abstract base class defining the interface for all promise-like objects in vine.
class Thenable:
def then(self, on_success, on_error=None):
"""
Chain callback to execute when thenable is fulfilled.
Parameters:
- on_success: callable, function to execute on successful completion
- on_error: callable, optional error handler for exceptions
Returns:
Implementation-specific return value (typically a thenable)
"""
def throw(self, exc=None, tb=None, propagate=True):
"""
Throw exception through the thenable chain.
Parameters:
- exc: Exception, exception to throw (uses current if None)
- tb: traceback, traceback object for exception
- propagate: bool, whether to re-raise if no error handler
"""
def cancel(self):
"""
Cancel the thenable operation.
Stops execution and cleans up resources.
"""
@classmethod
def register(cls, other):
"""
Register a class as implementing Thenable interface.
Parameters:
- other: class to register as Thenable
Returns:
The registered class (enables use as decorator)
"""
@classmethod
def __subclasshook__(cls, C):
"""
Check if class C supports thenable interface.
Returns True if class has 'then' method in its MRO.
"""Usage Examples:
from vine import Thenable, promise
# Check if object is thenable
p = promise()
print(isinstance(p, Thenable)) # True
# Register custom class as thenable
@Thenable.register
class CustomThenable:
def then(self, on_success, on_error=None):
# Custom implementation
pass
def throw(self, exc=None, tb=None, propagate=True):
# Custom implementation
pass
def cancel(self):
# Custom implementation
pass
# Now CustomThenable instances are considered Thenable
custom = CustomThenable()
print(isinstance(custom, Thenable)) # TrueProxy class that delegates thenable operations to a target promise.
class ThenableProxy:
def _set_promise_target(self, p):
"""
Set the target promise to proxy operations to.
Parameters:
- p: promise object to use as proxy target
"""
def then(self, on_success, on_error=None):
"""
Delegate then() call to target promise.
Parameters:
- on_success: callable, success callback
- on_error: callable, error callback
Returns:
Result of target promise's then() method
"""
def cancel(self):
"""
Delegate cancel() call to target promise.
Returns:
Result of target promise's cancel() method
"""
def throw(self, exc=None, tb=None, propagate=True):
"""
Delegate throw() call to target promise.
Parameters:
- exc: Exception to throw
- tb: traceback object
- propagate: whether to re-raise
Returns:
Result of target promise's throw() method
"""
def throw1(self, exc=None):
"""
Delegate throw1() call to target promise.
Parameters:
- exc: Exception to throw
Returns:
Result of target promise's throw1() method
"""
@property
def cancelled(self) -> bool:
"""Proxy to target promise's cancelled property."""
@property
def ready(self) -> bool:
"""Proxy to target promise's ready property."""
@property
def failed(self) -> bool:
"""Proxy to target promise's failed property."""Usage Examples:
from vine import promise
from vine.abstract import ThenableProxy
# Create proxy for delayed promise creation
class DelayedPromiseProxy(ThenableProxy):
def __init__(self, promise_factory):
self.promise_factory = promise_factory
self._p = None
def _ensure_target(self):
if self._p is None:
self._p = self.promise_factory()
return self._p
def then(self, on_success, on_error=None):
target = self._ensure_target()
return target.then(on_success, on_error)
# Usage
def create_expensive_promise():
print("Creating expensive promise...")
return promise(lambda: expensive_computation())
# Proxy delays creation until actually needed
proxy = DelayedPromiseProxy(create_expensive_promise)
# Promise only created when then() is called
proxy.then(lambda result: print(f"Got: {result}"))from vine import Thenable
import asyncio
@Thenable.register
class AsyncThenable:
"""Thenable that wraps asyncio coroutines."""
def __init__(self, coro):
self.coro = coro
self.callbacks = []
self.error_callbacks = []
self._result = None
self._error = None
self._done = False
def then(self, on_success, on_error=None):
if self._done:
if self._error:
if on_error:
on_error(self._error)
else:
on_success(self._result)
else:
self.callbacks.append(on_success)
if on_error:
self.error_callbacks.append(on_error)
return self
def throw(self, exc=None, tb=None, propagate=True):
self._error = exc or Exception("Thenable error")
self._done = True
for callback in self.error_callbacks:
callback(self._error)
if propagate and not self.error_callbacks:
raise self._error
def cancel(self):
if hasattr(self.coro, 'cancel'):
self.coro.cancel()
self._done = True
async def run(self):
"""Execute the wrapped coroutine."""
try:
self._result = await self.coro
self._done = True
for callback in self.callbacks:
callback(self._result)
except Exception as e:
self.throw(e)
# Usage
async def async_operation():
await asyncio.sleep(1)
return "Async result"
# Wrap async operation in custom thenable
async_thenable = AsyncThenable(async_operation())
async_thenable.then(lambda result: print(f"Got: {result}"))
# Run the async operation
asyncio.run(async_thenable.run())# Vine automatically recognizes objects with then() method
class CustomPromiseLike:
def __init__(self, value):
self.value = value
self.ready = False
def then(self, callback, on_error=None):
if self.ready:
callback(self.value)
return self
def complete(self):
self.ready = True
# Duck typing - automatically recognized as Thenable
custom = CustomPromiseLike("test")
print(isinstance(custom, Thenable)) # True (due to __subclasshook__)
# Can be used with vine utilities
from vine import maybe_promise
p = maybe_promise(custom) # Returns custom object as-isclass LoggingThenableProxy(ThenableProxy):
"""Proxy that logs all thenable operations."""
def __init__(self, target):
self._set_promise_target(target)
self.operation_log = []
def then(self, on_success, on_error=None):
self.operation_log.append(f"then() called with {on_success}")
return super().then(on_success, on_error)
def cancel(self):
self.operation_log.append("cancel() called")
return super().cancel()
def throw(self, exc=None, tb=None, propagate=True):
self.operation_log.append(f"throw() called with {exc}")
return super().throw(exc, tb, propagate)
# Usage
original_promise = promise(lambda: "result")
logged_promise = LoggingThenableProxy(original_promise)
logged_promise.then(lambda x: print(x))
logged_promise()
print(logged_promise.operation_log) # Shows all operationsInstall with Tessl CLI
npx tessl i tessl/pypi-vine