- commit
- 7efd7accda07fa66c1286d38d4815407e1402c6a
- parent
- 39eca6b4579dbd97ffcb2f364ec86740e6762312
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2018-08-03 10:52
refactor promises
Diffstat
| R | laneya/deferred.py -> laneya/promise.py | 125 | +++++++++++++++++++------------------------------------------ |
| M | laneya/protocol.py | 26 | +++++++++++++------------- |
2 files changed, 52 insertions, 99 deletions
diff --git a/laneya/deferred.py b/laneya/promise.py
@@ -22,65 +22,27 @@ RESOLVED = 1 22 22 REJECTED = 2 23 23 24 2425 -1 class AlreadyDoneError(Exception):26 -1 """The deferred has already been resolved or rejected."""27 -128 -129 -1 class Deferred(object):30 -1 def __init__(self):31 -1 self.promise = Promise()32 -133 -1 def resolve(self, value, silent=False, status=RESOLVED):34 -1 """Resolve the deferred.35 -136 -1 All success callbacks that are registered on the promise will be37 -1 executed with ``value``.38 -139 -1 All success callbacks that will be registered on the promise will be40 -1 executed immediately.41 -142 -1 Throws :py:exc:`AlreadyDoneError` if already resolved/rejected.43 -144 -1 """45 -1 if self.promise._status == OPEN:46 -1 self.promise._status = status47 -1 self.promise._value = value48 -1 for d, callback, errback in self.promise._children:49 -1 if status == RESOLVED:50 -1 callback(value).then(d.resolve, d.reject)51 -1 else: # rejected52 -1 errback(value).then(d.resolve, d.reject)53 -1 elif not silent:54 -1 raise AlreadyDoneError55 -156 -1 def reject(self, value, silent=False):57 -1 """Reject the deferred.58 -159 -1 Works execatly like :py:meth:`resolve`, only that it triggers the60 -1 execution of error callbacks.61 -162 -1 """63 -1 return self.resolve(value, silent=silent, status=REJECTED)64 -165 -166 25 class Promise(object): 67 26 def __init__(self): 68 27 self._children = [] 69 28 self._status = OPEN 70 29 self._value = None 71 3072 -1 def then(self, callback, errback=None):73 -1 """Register callbacks.74 -175 -1 The registered callbacks will be executed as soon as the associated76 -1 deferred has either been resolved or rejected. If it already has been,77 -1 the corresponding callback is executed immediately.-1 31 def resolve(self, value): -1 32 if self._status == OPEN: -1 33 self._status = RESOLVED -1 34 self._value = value -1 35 for d, callback, errback in self._children: -1 36 callback(value).then(d.resolve, d.reject) 78 3779 -1 :py:meth:`then` always returns a promise which will be resolved with80 -1 the result of the callback. If the callback raises an exception, the81 -1 promise is rejected.-1 38 def reject(self, value, silent=False): -1 39 if self._status == OPEN: -1 40 self._status = REJECTED -1 41 self._value = value -1 42 for d, callback, errback in self._children: -1 43 errback(value).then(d.resolve, d.reject) 82 4483 -1 """-1 45 def then(self, callback, errback=None): 84 46 _callback = wrap(callback, default=when) 85 47 _errback = wrap(errback, default=reject) 86 48 @@ -89,50 +51,48 @@ class Promise(object): 89 51 elif self._status == REJECTED: 90 52 return _errback(self._value) 91 53 else: # OPEN92 -1 d = Deferred()93 -1 self._children.append((d, _callback, _errback))94 -1 return d.promise-1 54 promise = Promise() -1 55 self._children.append((promise, _callback, _errback)) -1 56 return promise 95 5796 -1 def _raise(self):97 -1 if self._status == REJECTED and isinstance(self._value, Exception):98 -1 raise self._value-1 58 def catch(self, errback): -1 59 return self.then(None, errback) 99 60 100 61 101 62 def when(value=None):102 -1 """Wrap value in a promise if it is not already one."""103 63 if isinstance(value, Promise): 104 64 return value 105 65 else:106 -1 d = Deferred()107 -1 d.resolve(value)108 -1 return d.promise-1 66 promise = Promise() -1 67 promise.resolve(value) -1 68 return promise 109 69 110 70 111 71 def reject(value=None):112 -1 """Shortcut for creating a rejecting promise."""113 -1 d = Deferred()114 -1 d.reject(value)115 -1 return d.promise-1 72 if isinstance(value, Promise): -1 73 return value -1 74 else: -1 75 promise = Promise() -1 76 promise.reject(value) -1 77 return promise 116 78 117 79118 -1 def wrap(fun, default=None):119 -1 """Wrap a function so that it always returns a promise."""120 -1 def _fun(*args, **kwargs):-1 80 def wrap(fn, default=None): -1 81 def wrapped(*args, **kwargs): 121 82 try:122 -1 result = fun(*args, **kwargs)-1 83 value = fn(*args, **kwargs) 123 84 except Exception as err: 124 85 return reject(err)125 -1 return when(result)-1 86 return when(value) 126 87127 -1 if fun is None:-1 88 if fn is None: 128 89 return default 129 90 else:130 -1 return _fun-1 91 return wrapped 131 92 132 93133 -1 def all(*promises):134 -1 """Convert a list of promises to a promise of a list."""135 -1 d = Deferred()-1 94 def all(promises): -1 95 result = Promise() 136 96 data = { 137 97 'count': len(promises), 138 98 'results': [None] * len(promises), @@ -144,18 +104,11 @@ def all(*promises): 144 104 data['count'] -= 1 145 105 146 106 if data['count'] == 0:147 -1 d.resolve(data['results'])-1 107 result.resolve(data['results']) -1 108 148 109 return success 149 110 150 111 for i, promise in enumerate(promises):151 -1 promise.then(success_factory(i), d.reject)152 -1153 -1 return d.promise154 -1155 -1156 -1 def fromTwisted(twistedDeferred):157 -1 """Convert a twisted deferred to a promise."""-1 112 promise.then(success_factory(i), result.reject) 158 113159 -1 d = Deferred()160 -1 twistedDeferred.addCallbacks(d.resolve, d.reject)161 -1 return d.promise-1 114 return result
diff --git a/laneya/protocol.py b/laneya/protocol.py
@@ -67,7 +67,7 @@ import logging 67 67 68 68 import trollius as asyncio 69 6970 -1 from . import deferred as q-1 70 from . import promise as q 71 71 from . import actions 72 72 73 73 logger = logging.getLogger('laneya') @@ -273,7 +273,7 @@ class ClientProtocol(BaseProtocol): 273 273 def __init__(self, factory): 274 274 super(ClientProtocol, self).__init__() 275 275 self.factory = factory276 -1 self._response_deferreds = {}-1 276 self._response_promises = {} 277 277 278 278 def connection_made(self, transport): 279 279 super(ClientProtocol, self).connection_made(transport) @@ -287,16 +287,16 @@ class ClientProtocol(BaseProtocol): 287 287 if message['type'] == 'response': 288 288 self.validate_message(message, ['data', 'key', 'status', 'type']) 289 289 key = message['key']290 -1 if key in self._response_deferreds:-1 290 if key in self._response_promises: 291 291 response = { 292 292 'status': message['status'], 293 293 'data': message['data'], 294 294 }295 -1 d = self._response_deferreds.pop(key)-1 295 promise = self._response_promises.pop(key) 296 296 if message['status'] == 'success':297 -1 d.resolve(response)-1 297 promise.resolve(response) 298 298 else:299 -1 d.reject(response)-1 299 promise.reject(response) 300 300 301 301 elif message['type'] == 'update': 302 302 self.validate_message(message, ['action', 'data', 'type']) @@ -309,10 +309,10 @@ class ClientProtocol(BaseProtocol): 309 309 def update_received(self, action, **kwargs): 310 310 self.factory.update_received(action, **kwargs) 311 311312 -1 def _timeout(self, d, key):-1 312 def _timeout(self, promise, key): 313 313 try:314 -1 d.reject('timeout', silent=True)315 -1 del self._response_deferreds[key]-1 314 promise.reject('timeout') -1 315 del self._response_promises[key] 316 316 except KeyError: 317 317 pass 318 318 @@ -326,10 +326,10 @@ class ClientProtocol(BaseProtocol): 326 326 } 327 327 self.send_json(data) 328 328329 -1 d = q.Deferred()330 -1 self._response_deferreds[data['key']] = d331 -1 self.factory.loop.call_later(2, self._timeout, d, data['key'])332 -1 return d.promise-1 329 promise = q.Promise() -1 330 self._response_promises[data['key']] = promise -1 331 self.factory.loop.call_later(2, self._timeout, promise, data['key']) -1 332 return promise 333 333 334 334 335 335 class ClientProtocolFactory(object):