1 # Copyright (c) 2012 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 import logging 6 import sys 7 import traceback 8 9 _no_value = object() 10 11 12 def _DefaultErrorHandler(error): 13 raise error 14 15 16 def All(futures, except_pass=None, except_pass_log=False): 17 '''Creates a Future which returns a list of results from each Future in 18 |futures|. 19 20 If any Future raises an error other than those in |except_pass| the returned 21 Future will raise as well. 22 23 If any Future raises an error in |except_pass| then None will be inserted as 24 its result. If |except_pass_log| is True then the exception will be logged. 25 ''' 26 def resolve(): 27 resolved = [] 28 for f in futures: 29 try: 30 resolved.append(f.Get()) 31 # "except None" will simply not catch any errors. 32 except except_pass: 33 if except_pass_log: 34 logging.error(traceback.format_exc()) 35 resolved.append(None) 36 pass 37 return resolved 38 return Future(callback=resolve) 39 40 41 def Race(futures, except_pass=None, default=_no_value): 42 '''Returns a Future which resolves to the first Future in |futures| that 43 either succeeds or throws an error apart from those in |except_pass|. 44 45 If all Futures throw errors in |except_pass| then |default| is returned, 46 if specified. If |default| is not specified then one of the passed errors 47 will be re-thrown, for a nice stack trace. 48 ''' 49 def resolve(): 50 first_future = None 51 for future in futures: 52 if first_future is None: 53 first_future = future 54 try: 55 return future.Get() 56 # "except None" will simply not catch any errors. 57 except except_pass: 58 pass 59 if default is not _no_value: 60 return default 61 # Everything failed and there is no default value, propagate the first 62 # error even though it was caught by |except_pass|. 63 return first_future.Get() 64 return Future(callback=resolve) 65 66 67 class Future(object): 68 '''Stores a value, error, or callback to be used later. 69 ''' 70 def __init__(self, value=_no_value, callback=None, exc_info=None): 71 self._value = value 72 self._callback = callback 73 self._exc_info = exc_info 74 if (self._value is _no_value and 75 self._callback is None and 76 self._exc_info is None): 77 raise ValueError('Must have either a value, error, or callback.') 78 79 def Then(self, callback, error_handler=_DefaultErrorHandler): 80 '''Creates and returns a future that runs |callback| on the value of this 81 future, or runs optional |error_handler| if resolving this future results in 82 an exception. 83 84 If |callback| returns a non-Future value then the returned Future will 85 resolve to that value. 86 87 If |callback| returns a Future then it gets chained to the current Future. 88 This means that the returned Future will resolve to *that* Future's value. 89 This behaviour is transitive. 90 91 For example, 92 93 def fortytwo(): 94 return Future(value=42) 95 96 def inc(x): 97 return x + 1 98 99 def inc_future(x): 100 return Future(value=x + 1) 101 102 fortytwo().Then(inc).Get() ==> 43 103 fortytwo().Then(inc_future).Get() ==> 43 104 fortytwo().Then(inc_future).Then(inc_future).Get() ==> 44 105 ''' 106 def then(): 107 val = None 108 try: 109 val = self.Get() 110 except Exception as e: 111 val = error_handler(e) 112 else: 113 val = callback(val) 114 return val.Get() if isinstance(val, Future) else val 115 return Future(callback=then) 116 117 def Get(self): 118 '''Gets the stored value, error, or callback contents. 119 ''' 120 if self._value is not _no_value: 121 return self._value 122 if self._exc_info is not None: 123 self._Raise() 124 try: 125 self._value = self._callback() 126 return self._value 127 except: 128 self._exc_info = sys.exc_info() 129 self._Raise() 130 131 def _Raise(self): 132 exc_info = self._exc_info 133 raise exc_info[0], exc_info[1], exc_info[2] 134