| OLD | NEW |
| (Empty) |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 """ | |
| 6 Promise used by the python bindings. | |
| 7 | |
| 8 The API is following the ECMAScript 6 API for promises. | |
| 9 """ | |
| 10 | |
| 11 import sys | |
| 12 | |
| 13 | |
| 14 class Promise(object): | |
| 15 """The promise object.""" | |
| 16 | |
| 17 STATE_PENDING = 0 | |
| 18 STATE_FULLFILLED = 1 | |
| 19 STATE_REJECTED = 2 | |
| 20 STATE_BOUND = 3 | |
| 21 | |
| 22 def __init__(self, generator_function): | |
| 23 """ | |
| 24 Constructor. | |
| 25 | |
| 26 Args: | |
| 27 generator_function: A function taking 2 arguments: resolve and reject. | |
| 28 When |resolve| is called, the promise is fullfilled with the given value. | |
| 29 When |reject| is called, the promise is rejected with the given value. | |
| 30 A promise can only be resolved or rejected once, all following calls will | |
| 31 have no effect. | |
| 32 """ | |
| 33 self._onCatched = [] | |
| 34 self._onFulfilled = [] | |
| 35 self._onRejected = [] | |
| 36 self._state = Promise.STATE_PENDING | |
| 37 self._result = None | |
| 38 try: | |
| 39 generator_function(self._Resolve, self._Reject) | |
| 40 except Exception as e: | |
| 41 # Adding traceback similarly to python 3.0 (pep-3134) | |
| 42 e.__traceback__ = sys.exc_info()[2] | |
| 43 self._Reject(e) | |
| 44 | |
| 45 @staticmethod | |
| 46 def Resolve(value): | |
| 47 """ | |
| 48 If value is a promise, make a promise that have the same behavior as value, | |
| 49 otherwise make a promise that fulfills to value. | |
| 50 """ | |
| 51 if isinstance(value, Promise): | |
| 52 return value | |
| 53 return Promise(lambda x, y: x(value)) | |
| 54 | |
| 55 @staticmethod | |
| 56 def Reject(reason): | |
| 57 "Make a promise that rejects to reason.""" | |
| 58 return Promise(lambda x, y: y(reason)) | |
| 59 | |
| 60 @staticmethod | |
| 61 def All(*iterable): | |
| 62 """ | |
| 63 Make a promise that fulfills when every item in the array fulfills, and | |
| 64 rejects if (and when) any item rejects. Each array item is passed to | |
| 65 Promise.resolve, so the array can be a mixture of promise-like objects and | |
| 66 other objects. The fulfillment value is an array (in order) of fulfillment | |
| 67 values. The rejection value is the first rejection value. | |
| 68 """ | |
| 69 def GeneratorFunction(resolve, reject): | |
| 70 state = { | |
| 71 'rejected': False, | |
| 72 'nb_resolved': 0, | |
| 73 } | |
| 74 promises = [Promise.Resolve(x) for x in iterable] | |
| 75 results = [None for x in promises] | |
| 76 def OnFullfilled(i): | |
| 77 def OnFullfilled(res): | |
| 78 if state['rejected']: | |
| 79 return | |
| 80 results[i] = res | |
| 81 state['nb_resolved'] = state['nb_resolved'] + 1 | |
| 82 if state['nb_resolved'] == len(results): | |
| 83 resolve(results) | |
| 84 return OnFullfilled | |
| 85 def OnRejected(reason): | |
| 86 if state['rejected']: | |
| 87 return | |
| 88 state['rejected'] = True | |
| 89 reject(reason) | |
| 90 | |
| 91 for (i, promise) in enumerate(promises): | |
| 92 promise.Then(OnFullfilled(i), OnRejected) | |
| 93 return Promise(GeneratorFunction) | |
| 94 | |
| 95 @staticmethod | |
| 96 def Race(*iterable): | |
| 97 """ | |
| 98 Make a Promise that fulfills as soon as any item fulfills, or rejects as | |
| 99 soon as any item rejects, whichever happens first. | |
| 100 """ | |
| 101 def GeneratorFunction(resolve, reject): | |
| 102 state = { | |
| 103 'ended': False | |
| 104 } | |
| 105 def OnEvent(callback): | |
| 106 def OnEvent(res): | |
| 107 if state['ended']: | |
| 108 return | |
| 109 state['ended'] = True | |
| 110 callback(res) | |
| 111 return OnEvent | |
| 112 for promise in [Promise.Resolve(x) for x in iterable]: | |
| 113 promise.Then(OnEvent(resolve), OnEvent(reject)) | |
| 114 return Promise(GeneratorFunction) | |
| 115 | |
| 116 @property | |
| 117 def state(self): | |
| 118 if isinstance(self._result, Promise): | |
| 119 return self._result.state | |
| 120 return self._state | |
| 121 | |
| 122 def Then(self, onFullfilled=None, onRejected=None): | |
| 123 """ | |
| 124 onFulfilled is called when/if this promise resolves. onRejected is called | |
| 125 when/if this promise rejects. Both are optional, if either/both are omitted | |
| 126 the next onFulfilled/onRejected in the chain is called. Both callbacks have | |
| 127 a single parameter, the fulfillment value or rejection reason. |Then| | |
| 128 returns a new promise equivalent to the value you return from | |
| 129 onFulfilled/onRejected after being passed through Resolve. If an | |
| 130 error is thrown in the callback, the returned promise rejects with that | |
| 131 error. | |
| 132 """ | |
| 133 if isinstance(self._result, Promise): | |
| 134 return self._result.Then(onFullfilled, onRejected) | |
| 135 def GeneratorFunction(resolve, reject): | |
| 136 recover = reject | |
| 137 if onRejected: | |
| 138 recover = resolve | |
| 139 if self._state == Promise.STATE_PENDING: | |
| 140 self._onFulfilled.append(_Delegate(resolve, reject, onFullfilled)) | |
| 141 self._onRejected.append(_Delegate(recover, reject, onRejected)) | |
| 142 if self._state == self.STATE_FULLFILLED: | |
| 143 _Delegate(resolve, reject, onFullfilled)(self._result) | |
| 144 if self._state == self.STATE_REJECTED: | |
| 145 _Delegate(recover, reject, onRejected)(self._result) | |
| 146 return Promise(GeneratorFunction) | |
| 147 | |
| 148 def Catch(self, onCatched): | |
| 149 """Equivalent to |Then(None, onCatched)|""" | |
| 150 return self.Then(None, onCatched) | |
| 151 | |
| 152 def __getattr__(self, attribute): | |
| 153 """ | |
| 154 Allows to get member of a promise. It will return a promise that will | |
| 155 resolve to the member of the result. | |
| 156 """ | |
| 157 return self.Then(lambda v: getattr(v, attribute)) | |
| 158 | |
| 159 def __call__(self, *args, **kwargs): | |
| 160 """ | |
| 161 Allows to call this promise. It will return a promise that will resolved to | |
| 162 the result of calling the result of this promise with the given arguments. | |
| 163 """ | |
| 164 return self.Then(lambda v: v(*args, **kwargs)) | |
| 165 | |
| 166 | |
| 167 def _Resolve(self, value): | |
| 168 if self.state != Promise.STATE_PENDING: | |
| 169 return | |
| 170 self._result = value | |
| 171 if isinstance(value, Promise): | |
| 172 self._state = Promise.STATE_BOUND | |
| 173 self._result.Then(_IterateAction(self._onFulfilled), | |
| 174 _IterateAction(self._onRejected)) | |
| 175 return | |
| 176 self._state = Promise.STATE_FULLFILLED | |
| 177 for f in self._onFulfilled: | |
| 178 f(value) | |
| 179 self._onFulfilled = None | |
| 180 self._onRejected = None | |
| 181 | |
| 182 def _Reject(self, reason): | |
| 183 if self.state != Promise.STATE_PENDING: | |
| 184 return | |
| 185 self._result = reason | |
| 186 self._state = Promise.STATE_REJECTED | |
| 187 for f in self._onRejected: | |
| 188 f(reason) | |
| 189 self._onFulfilled = None | |
| 190 self._onRejected = None | |
| 191 | |
| 192 | |
| 193 def async(f): | |
| 194 def _ResolvePromises(*args, **kwargs): | |
| 195 keys = kwargs.keys() | |
| 196 values = kwargs.values() | |
| 197 all_args = list(args) + values | |
| 198 return Promise.All(*all_args).Then( | |
| 199 lambda r: f(*r[:len(args)], **dict(zip(keys, r[len(args):])))) | |
| 200 return _ResolvePromises | |
| 201 | |
| 202 | |
| 203 def _IterateAction(iterable): | |
| 204 def _Run(x): | |
| 205 for f in iterable: | |
| 206 f(x) | |
| 207 return _Run | |
| 208 | |
| 209 | |
| 210 def _Delegate(resolve, reject, action): | |
| 211 def _Run(x): | |
| 212 try: | |
| 213 if action: | |
| 214 resolve(action(x)) | |
| 215 else: | |
| 216 resolve(x) | |
| 217 except Exception as e: | |
| 218 # Adding traceback similarly to python 3.0 (pep-3134) | |
| 219 e.__traceback__ = sys.exc_info()[2] | |
| 220 reject(e) | |
| 221 return _Run | |
| OLD | NEW |