15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 56e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import logging 6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import sys 76e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import traceback 8c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)_no_value = object() 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)def _DefaultErrorHandler(error): 135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) raise error 145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)def All(futures, except_pass=None, except_pass_log=False): 175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) '''Creates a Future which returns a list of results from each Future in 185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) |futures|. 195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) If any Future raises an error other than those in |except_pass| the returned 215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Future will raise as well. 226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) If any Future raises an error in |except_pass| then None will be inserted as 246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) its result. If |except_pass_log| is True then the exception will be logged. 255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ''' 260529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch def resolve(): 270529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch resolved = [] 280529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch for f in futures: 290529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch try: 300529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch resolved.append(f.Get()) 315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # "except None" will simply not catch any errors. 320529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch except except_pass: 336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if except_pass_log: 346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) logging.error(traceback.format_exc()) 356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) resolved.append(None) 360529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch pass 370529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch return resolved 380529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch return Future(callback=resolve) 394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)def Race(futures, except_pass=None, default=_no_value): 425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) '''Returns a Future which resolves to the first Future in |futures| that 435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) either succeeds or throws an error apart from those in |except_pass|. 445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) If all Futures throw errors in |except_pass| then |default| is returned, 466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if specified. If |default| is not specified then one of the passed errors 476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) will be re-thrown, for a nice stack trace. 485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ''' 495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def resolve(): 505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) first_future = None 515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for future in futures: 525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if first_future is None: 535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) first_future = future 545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) try: 555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return future.Get() 565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # "except None" will simply not catch any errors. 575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) except except_pass: 585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) pass 596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if default is not _no_value: 606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return default 616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) # Everything failed and there is no default value, propagate the first 626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) # error even though it was caught by |except_pass|. 635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return first_future.Get() 645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return Future(callback=resolve) 655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Future(object): 68effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch '''Stores a value, error, or callback to be used later. 69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) ''' 70effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def __init__(self, value=_no_value, callback=None, exc_info=None): 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._value = value 72effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self._callback = callback 734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self._exc_info = exc_info 744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (self._value is _no_value and 75effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self._callback is None and 764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) self._exc_info is None): 77effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch raise ValueError('Must have either a value, error, or callback.') 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def Then(self, callback, error_handler=_DefaultErrorHandler): 805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) '''Creates and returns a future that runs |callback| on the value of this 815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) future, or runs optional |error_handler| if resolving this future results in 825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) an exception. 835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) If |callback| returns a non-Future value then the returned Future will 855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) resolve to that value. 865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) If |callback| returns a Future then it gets chained to the current Future. 885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) This means that the returned Future will resolve to *that* Future's value. 895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) This behaviour is transitive. 905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) For example, 925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def fortytwo(): 945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return Future(value=42) 955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def inc(x): 975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return x + 1 985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def inc_future(x): 1005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return Future(value=x + 1) 1015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) fortytwo().Then(inc).Get() ==> 43 1035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) fortytwo().Then(inc_future).Get() ==> 43 1045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) fortytwo().Then(inc_future).Then(inc_future).Get() ==> 44 1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ''' 1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def then(): 1075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) val = None 1085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) try: 1095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) val = self.Get() 1105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) except Exception as e: 1115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) val = error_handler(e) 1125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) else: 1135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) val = callback(val) 1145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return val.Get() if isinstance(val, Future) else val 1155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return Future(callback=then) 1165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def Get(self): 118effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch '''Gets the stored value, error, or callback contents. 119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) ''' 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if self._value is not _no_value: 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self._value 122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if self._exc_info is not None: 123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) self._Raise() 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 125effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self._value = self._callback() 126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return self._value 127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) except: 128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) self._exc_info = sys.exc_info() 129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) self._Raise() 130c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 131c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) def _Raise(self): 132c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) exc_info = self._exc_info 133c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) raise exc_info[0], exc_info[1], exc_info[2] 134