-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdecorate.py
executable file
·111 lines (101 loc) · 4 KB
/
decorate.py
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
from functools import wraps
from twisted.internet.defer import (
Deferred, DeferredList, fail, maybeDeferred, succeed)
from twisted.python.failure import Failure
def _replace(value, location, args):
args[location] = value
return value
def callback(func):
"""
A decorator that turns a regular function into one that can accept
arguments that are C{Deferred}s. If the deferreds (if any) all fire
without error, the decorated function is called with the result of the
deferreds, and a deferred is returned that fires with the result. If
any of the deferreds fails, a deferred is returned that will fail with
the failure.
"""
@wraps(func)
def wrapper(*args, **kw):
fargs = []
fkw = {}
deferreds = []
for index, arg in enumerate(args):
if isinstance(arg, Deferred):
fargs.append(None)
deferreds.append(arg.addCallback(_replace, index, fargs))
else:
fargs.append(arg)
for key, arg in kw.iteritems():
if isinstance(arg, Deferred):
deferreds.append(arg.addCallback(_replace, key, fkw))
else:
fkw[key] = arg
if deferreds:
if len(deferreds) == 1:
return deferreds[0].addCallback(lambda _: func(*fargs, **fkw))
else:
def canceler(result):
for i, deferred in enumerate(deferreds):
if i != result.value.index:
deferred.cancel()
return result.value.subFailure
return DeferredList(
deferreds, fireOnOneErrback=True, consumeErrors=True
).addCallbacks(lambda _: func(*fargs, **fkw), errback=canceler)
else:
return maybeDeferred(func, *args, **kw)
return wrapper
class ErrbackDecoratorError(Exception):
"""
Raised if a function decorated with L{errback} is called with no
positional arguments.
"""
def errback(func):
"""
A decorator that turns a regular function into one that can accept
arguments that are C{Deferred}s. If any of the deferreds (if any)
fail, the decorated function is called with the result of the
deferreds, and a deferred is returned that fires with the result. If
all the deferreds succeed, the wrapped function is not called and a
deferred is returned that will succeed with just the first argument. In
this last case, the decorator acts like a pass-through that returns its
non-failure result.
"""
@wraps(func)
def wrapper(*args, **kw):
if not args:
return fail(ErrbackDecoratorError(
'@errback decorated function %r invoked with '
'no positional arguments.' % wrapper.__name__))
fargs = []
fkw = {}
deferreds = []
for index, arg in enumerate(args):
if isinstance(arg, Deferred):
fargs.append(None)
deferreds.append(arg.addBoth(_replace, index, fargs))
else:
fargs.append(arg)
for key, arg in kw.iteritems():
if isinstance(arg, Deferred):
deferreds.append(arg.addBoth(_replace, key, fkw))
else:
fkw[key] = arg
if deferreds:
def finish(ignore):
if any(isinstance(v, Failure) for v in fargs + fkw.values()):
return func(*fargs, **fkw)
else:
return fargs[0]
if len(deferreds) == 1:
return deferreds[0].addBoth(finish)
else:
return DeferredList(deferreds, consumeErrors=True).addCallback(
finish)
else:
if any(isinstance(v, Failure) for v in fargs + fkw.values()):
return maybeDeferred(func, *fargs, **fkw)
else:
return succeed(fargs[0])
return wrapper
__all__ = ['callback', 'errback', 'ErrbackDecoratorError']