Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(262)

Unified Diff: mojo/public/python/mojo/bindings/promise.py

Issue 610613002: mojo: Add promises for python bindings. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Follow review Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « mojo/public/python/BUILD.gn ('k') | mojo/python/tests/promise_unittest.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: mojo/public/python/mojo/bindings/promise.py
diff --git a/mojo/public/python/mojo/bindings/promise.py b/mojo/public/python/mojo/bindings/promise.py
new file mode 100644
index 0000000000000000000000000000000000000000..c5d7d7cb641945e14af53d1b1d5e681db57d21b2
--- /dev/null
+++ b/mojo/public/python/mojo/bindings/promise.py
@@ -0,0 +1,188 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Promise used by the python bindings.
+
+The API is following the ECMAScript 6 API for promises.
+"""
+
+
+class Promise(object):
+ """The promise object."""
+
+ STATE_PENDING = 0
+ STATE_FULLFILLED = 1
+ STATE_REJECTED = 2
+ STATE_BOUND = 3
+
+ def __init__(self, generator_function):
+ """
+ Constructor.
+
+ Args:
+ generator_function: A function taking 2 arguments: resolve and reject.
+ When |resolve| is called, the promise is fullfilled with the given value.
+ When |reject| is called, the promise is rejected with the given value.
+ A promise can only be resolved or rejected once, all following calls will
+ have no effect.
+ """
+ self._onCatched = []
+ self._onFulfilled = []
+ self._onRejected = []
+ self._state = Promise.STATE_PENDING
+ self._result = None
+ if generator_function:
+ generator_function(self._Resolve, self._Reject)
+
+ @staticmethod
+ def Resolve(value):
+ """
+ If value is a promise, make a promise that have the same behavior as value,
+ otherwise make a promise that fulfills to value.
+ """
+ if isinstance(value, Promise):
+ return value
+ return Promise(lambda x, y: x(value))
+
+ @staticmethod
+ def Reject(reason):
+ "Make a promise that rejects to reason."""
+ return Promise(lambda x, y: y(reason))
+
+ @staticmethod
+ def All(*iterable):
+ """
+ Make a promise that fulfills when every item in the array fulfills, and
+ rejects if (and when) any item rejects. Each array item is passed to
+ Promise.resolve, so the array can be a mixture of promise-like objects and
+ other objects. The fulfillment value is an array (in order) of fulfillment
+ values. The rejection value is the first rejection value.
+ """
+ def GeneratorFunction(resolve, reject):
+ state = {
+ 'rejected': False,
+ 'nb_resolved': 0,
+ }
+ promises = [Promise.Resolve(x) for x in iterable]
+ results = [None for x in promises]
+ def OnFullfilled(i):
+ def OnFullfilled(res):
+ if state['rejected']:
+ return
+ results[i] = res
+ state['nb_resolved'] = state['nb_resolved'] + 1
+ if state['nb_resolved'] == len(results):
+ resolve(results)
+ return OnFullfilled
+ def OnRejected(reason):
+ if state['rejected']:
+ return
+ state['rejected'] = True
+ reject(reason)
+
+ for (i, promise) in enumerate(promises):
+ promise.Then(OnFullfilled(i), OnRejected)
+ return Promise(GeneratorFunction)
+
+ @staticmethod
+ def Race(*iterable):
+ """
+ Make a Promise that fulfills as soon as any item fulfills, or rejects as
+ soon as any item rejects, whichever happens first.
+ """
+ def GeneratorFunction(resolve, reject):
+ state = {
+ 'ended': False
+ }
+ def OnEvent(callback):
+ def OnEvent(res):
+ if state['ended']:
+ return
+ state['ended'] = True
+ callback(res)
+ return OnEvent
+ for promise in [Promise.Resolve(x) for x in iterable]:
+ promise.Then(OnEvent(resolve), OnEvent(reject))
+ return Promise(GeneratorFunction)
+
+ @property
+ def state(self):
+ if isinstance(self._result, Promise):
+ return self._result.state
+ return self._state
+
+ def Then(self, onFullfilled=None, onRejected=None):
+ """
+ onFulfilled is called when/if this promise resolves. onRejected is called
+ when/if this promise rejects. Both are optional, if either/both are omitted
+ the next onFulfilled/onRejected in the chain is called. Both callbacks have
+ a single parameter, the fulfillment value or rejection reason. |Then|
+ returns a new promise equivalent to the value you return from
+ onFulfilled/onRejected after being passed through Resolve. If an
+ error is thrown in the callback, the returned promise rejects with that
+ error.
+ """
+ if isinstance(self._result, Promise):
+ return self._result.Then(onFullfilled, onRejected)
+ def GeneratorFunction(resolve, reject):
+ if self._state == Promise.STATE_PENDING:
+ self._onFulfilled.append(_Delegate(resolve, reject, onFullfilled))
+ self._onRejected.append(_Delegate(reject, reject, onRejected))
+ if self._state == self.STATE_FULLFILLED:
+ _Delegate(resolve, reject, onFullfilled)(self._result)
+ if self._state == self.STATE_REJECTED:
+ recover = reject
+ if onRejected:
+ recover = resolve
+ _Delegate(recover, reject, onRejected)(self._result)
+ return Promise(GeneratorFunction)
+
+ def Catch(self, onCatched):
+ """Equivalent to |Then(None, onCatched)|"""
+ return self.Then(None, onCatched)
+
+ def _Resolve(self, value):
+ if self.state != Promise.STATE_PENDING:
+ return
+ self._result = value
+ if isinstance(value, Promise):
+ self._state = Promise.STATE_BOUND
+ self._result.Then(_IterateAction(self._onFulfilled),
+ _IterateAction(self._onRejected))
+ return
+ self._state = Promise.STATE_FULLFILLED
+ for f in self._onFulfilled:
+ f(value)
+ self._onFulfilled = None
+ self._onRejected = None
+
+ def _Reject(self, reason):
+ if self.state != Promise.STATE_PENDING:
+ return
+ self._result = reason
+ self._state = Promise.STATE_REJECTED
+ for f in self._onRejected:
+ f(reason)
+ self._onFulfilled = None
+ self._onRejected = None
+
+
+def _IterateAction(iterable):
+ def _Run(x):
+ for f in iterable:
+ f(x)
+ return _Run
+
+
+def _Delegate(resolve, reject, action):
+ def _Run(x):
+ try:
+ if action:
+ resolve(action(x))
+ else:
+ resolve(x)
+ except Exception as e:
+ reject(e)
+ return _Run
« no previous file with comments | « mojo/public/python/BUILD.gn ('k') | mojo/python/tests/promise_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698