Home | History | Annotate | Download | only in server2
      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