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