15
15
16
16
# We need map_unordered for the use_backups implementation
17
17
async def map_unordered (
18
- app_function , input , use_backups = False , return_stats = False , name = None , ** kwargs
18
+ app_function ,
19
+ input ,
20
+ use_backups = False ,
21
+ backup_function = None ,
22
+ return_stats = False ,
23
+ name = None ,
24
+ ** kwargs ,
19
25
):
20
26
"""
21
27
Apply a function to items of an input list, yielding results as they are completed
@@ -42,6 +48,8 @@ async def map_unordered(
42
48
yield result
43
49
return
44
50
51
+ backup_function = backup_function or app_function
52
+
45
53
tasks = {
46
54
asyncio .ensure_future (app_function .call .aio (i , ** kwargs )): i for i in input
47
55
}
@@ -55,12 +63,14 @@ async def map_unordered(
55
63
finished , pending = await asyncio .wait (
56
64
pending , return_when = asyncio .FIRST_COMPLETED , timeout = 2
57
65
)
58
- # print("finished", finished)
59
- # print("pending", pending)
60
-
61
66
for task in finished :
62
67
# TODO: use exception groups in Python 3.11 to handle case of multiple task exceptions
63
68
if task .exception ():
69
+ # if the task has a backup that is not done, or is done with no exception, then don't raise this exception
70
+ backup = backups .get (task , None )
71
+ if backup :
72
+ if not backup .done () or not backup .exception ():
73
+ continue
64
74
raise task .exception ()
65
75
end_times [task ] = time .monotonic ()
66
76
if return_stats :
@@ -76,9 +86,11 @@ async def map_unordered(
76
86
if use_backups :
77
87
backup = backups .get (task , None )
78
88
if backup :
79
- pending .remove (backup )
89
+ if backup in pending :
90
+ pending .remove (backup )
80
91
del backups [task ]
81
92
del backups [backup ]
93
+ backup .cancel ()
82
94
83
95
if use_backups :
84
96
now = time .monotonic ()
@@ -87,8 +99,11 @@ async def map_unordered(
87
99
task , now , start_times , end_times
88
100
):
89
101
# launch backup task
102
+ print ("Launching backup task" )
90
103
i = tasks [task ]
91
- new_task = asyncio .ensure_future (app_function .call .aio (i , ** kwargs ))
104
+ new_task = asyncio .ensure_future (
105
+ backup_function .call .aio (i , ** kwargs )
106
+ )
92
107
tasks [new_task ] = i
93
108
start_times [new_task ] = time .monotonic ()
94
109
pending .add (new_task )
0 commit comments