1
1
import typing as T
2
2
import asyncio
3
3
from datetime import datetime
4
+ from concurrent .futures import Future
5
+ import threading
4
6
5
7
from ..utils import CheckAttrRange , ExecutorError
6
8
7
9
8
10
if T .TYPE_CHECKING :
9
11
from .base import Job
10
12
11
-
12
13
JobStatusType = T .Literal ['pending' , 'running' , 'failed' , 'done' , 'cancelled' ]
13
14
valid_job_statuses : T .List [JobStatusType ] = [
14
15
'pending' , 'running' , 'failed' , 'done' , 'cancelled' ]
@@ -38,36 +39,48 @@ def __init__(self, job: "Job", valid_status: T.List[JobStatusType]):
38
39
39
40
40
41
_T = T .TypeVar ("_T" )
42
+ _thread_locals = threading .local ()
41
43
42
44
43
45
def _gen_initializer (gen_func , args = tuple (), kwargs = {}): # pragma: no cover
44
- global _generator
45
- _generator = gen_func (* args , ** kwargs )
46
+ global _thread_locals
47
+ if "_thread_locals" not in globals ():
48
+ # avoid conflict for ThreadJob
49
+ _thread_locals = threading .local ()
50
+ _thread_locals ._generator = gen_func (* args , ** kwargs )
46
51
47
52
48
- def _gen_next (): # pragma: no cover
49
- global _generator
50
- return next (_generator )
53
+ def _gen_next (fut = None ): # pragma: no cover
54
+ global _thread_locals
55
+ if fut is None :
56
+ return next (_thread_locals ._generator )
57
+ else :
58
+ return next (fut )
51
59
52
60
53
- def _gen_anext (): # pragma: no cover
54
- global _generator
55
- return asyncio .run (_generator .__anext__ ())
61
+ def _gen_anext (fut = None ): # pragma: no cover
62
+ global _thread_locals
63
+ if fut is None :
64
+ return asyncio .run (_thread_locals ._generator .__anext__ ())
65
+ else :
66
+ return asyncio .run (fut .__anext__ ())
56
67
57
68
58
69
class GeneratorWrapper (T .Generic [_T ]):
59
70
"""
60
71
wrap a generator in executor pool
61
72
"""
62
- def __init__ (self , job : "Job" ):
73
+
74
+ def __init__ (self , job : "Job" , fut : T .Optional [Future ] = None ):
63
75
self ._job = job
76
+ self ._fut = fut
64
77
65
78
def __iter__ (self ):
66
79
return self
67
80
68
81
def __next__ (self ) -> _T :
69
82
try :
70
- return self ._job ._executor .submit (_gen_next ).result ()
83
+ return self ._job ._executor .submit (_gen_next , self . _fut ).result ()
71
84
except Exception as e :
72
85
engine = self ._job .engine
73
86
if engine is None :
@@ -87,7 +100,7 @@ def __aiter__(self):
87
100
88
101
async def __anext__ (self ) -> _T :
89
102
try :
90
- fut = self ._job ._executor .submit (_gen_anext )
103
+ fut = self ._job ._executor .submit (_gen_anext , self . _fut )
91
104
res = await asyncio .wrap_future (fut )
92
105
return res
93
106
except Exception as e :
0 commit comments