66 'wait' , 'wait_for' , 'as_completed' , 'sleep' ,
77 'gather' , 'shield' , 'ensure_future' , 'run_coroutine_threadsafe' ,
88 'current_task' , 'all_tasks' ,
9+ 'create_eager_task_factory' , 'eager_task_factory' ,
910 '_register_task' , '_unregister_task' , '_enter_task' , '_leave_task' ,
1011)
1112
@@ -75,6 +76,8 @@ def _set_task_name(task, name):
7576 set_name (name )
7677
7778
79+ _NOT_SET = object ()
80+
7881class Task (futures ._PyFuture ): # Inherit Python Task implementation
7982 # from a Python Future implementation.
8083
@@ -93,7 +96,8 @@ class Task(futures._PyFuture): # Inherit Python Task implementation
9396 # status is still pending
9497 _log_destroy_pending = True
9598
96- def __init__ (self , coro , * , loop = None , name = None , context = None ):
99+ def __init__ (self , coro , * , loop = None , name = None , context = None ,
100+ coro_result = _NOT_SET ):
97101 super ().__init__ (loop = loop )
98102 if self ._source_traceback :
99103 del self ._source_traceback [- 1 ]
@@ -117,7 +121,10 @@ def __init__(self, coro, *, loop=None, name=None, context=None):
117121 else :
118122 self ._context = context
119123
120- self ._loop .call_soon (self .__step , context = self ._context )
124+ if coro_result is _NOT_SET :
125+ self ._loop .call_soon (self .__step , context = self ._context )
126+ else :
127+ self .__step_handle_result (coro_result )
121128 _register_task (self )
122129
123130 def __del__ (self ):
@@ -287,55 +294,58 @@ def __step(self, exc=None):
287294 except BaseException as exc :
288295 super ().set_exception (exc )
289296 else :
290- blocking = getattr (result , '_asyncio_future_blocking' , None )
291- if blocking is not None :
297+ self .__step_handle_result (result )
298+ finally :
299+ _leave_task (self ._loop , self )
300+ self = None # Needed to break cycles when an exception occurs.
301+
302+ def __step_handle_result (self , result ):
303+ blocking = getattr (result , '_asyncio_future_blocking' , None )
304+ if blocking is not None :
292305 # Yielded Future must come from Future.__iter__().
293- if futures ._get_loop (result ) is not self ._loop :
306+ if futures ._get_loop (result ) is not self ._loop :
307+ new_exc = RuntimeError (
308+ f'Task { self !r} got Future '
309+ f'{ result !r} attached to a different loop' )
310+ self ._loop .call_soon (
311+ self .__step , new_exc , context = self ._context )
312+ elif blocking :
313+ if result is self :
294314 new_exc = RuntimeError (
295- f'Task { self !r} got Future '
296- f'{ result !r} attached to a different loop' )
315+ f'Task cannot await on itself: { self !r} ' )
297316 self ._loop .call_soon (
298317 self .__step , new_exc , context = self ._context )
299- elif blocking :
300- if result is self :
301- new_exc = RuntimeError (
302- f'Task cannot await on itself: { self !r} ' )
303- self ._loop .call_soon (
304- self .__step , new_exc , context = self ._context )
305- else :
306- result ._asyncio_future_blocking = False
307- result .add_done_callback (
308- self .__wakeup , context = self ._context )
309- self ._fut_waiter = result
310- if self ._must_cancel :
311- if self ._fut_waiter .cancel (
312- msg = self ._cancel_message ):
313- self ._must_cancel = False
314318 else :
315- new_exc = RuntimeError (
316- f'yield was used instead of yield from '
317- f'in task { self !r} with { result !r} ' )
318- self ._loop .call_soon (
319- self .__step , new_exc , context = self ._context )
320-
321- elif result is None :
322- # Bare yield relinquishes control for one event loop iteration.
323- self ._loop .call_soon (self .__step , context = self ._context )
324- elif inspect .isgenerator (result ):
325- # Yielding a generator is just wrong.
326- new_exc = RuntimeError (
327- f'yield was used instead of yield from for '
328- f'generator in task { self !r} with { result !r} ' )
329- self ._loop .call_soon (
330- self .__step , new_exc , context = self ._context )
319+ result ._asyncio_future_blocking = False
320+ result .add_done_callback (
321+ self .__wakeup , context = self ._context )
322+ self ._fut_waiter = result
323+ if self ._must_cancel :
324+ if self ._fut_waiter .cancel (
325+ msg = self ._cancel_message ):
326+ self ._must_cancel = False
331327 else :
332- # Yielding something else is an error.
333- new_exc = RuntimeError (f'Task got bad yield: { result !r} ' )
328+ new_exc = RuntimeError (
329+ f'yield was used instead of yield from '
330+ f'in task { self !r} with { result !r} ' )
334331 self ._loop .call_soon (
335332 self .__step , new_exc , context = self ._context )
336- finally :
337- _leave_task (self ._loop , self )
338- self = None # Needed to break cycles when an exception occurs.
333+
334+ elif result is None :
335+ # Bare yield relinquishes control for one event loop iteration.
336+ self ._loop .call_soon (self .__step , context = self ._context )
337+ elif inspect .isgenerator (result ):
338+ # Yielding a generator is just wrong.
339+ new_exc = RuntimeError (
340+ f'yield was used instead of yield from for '
341+ f'generator in task { self !r} with { result !r} ' )
342+ self ._loop .call_soon (
343+ self .__step , new_exc , context = self ._context )
344+ else :
345+ # Yielding something else is an error.
346+ new_exc = RuntimeError (f'Task got bad yield: { result !r} ' )
347+ self ._loop .call_soon (
348+ self .__step , new_exc , context = self ._context )
339349
340350 def __wakeup (self , future ):
341351 try :
@@ -897,6 +907,35 @@ def callback():
897907 return future
898908
899909
910+ def create_eager_task_factory (custom_task_constructor ):
911+
912+ def factory (loop , coro , * , name = None , context = None ):
913+ loop ._check_closed ()
914+ if not loop .is_running ():
915+ return custom_task_constructor (coro , loop = loop , name = name , context = context )
916+
917+ try :
918+ result = coro .send (None )
919+ except StopIteration as si :
920+ fut = loop .create_future ()
921+ fut .set_result (si .value )
922+ return fut
923+ except Exception as ex :
924+ fut = loop .create_future ()
925+ fut .set_exception (ex )
926+ return fut
927+ else :
928+ task = custom_task_constructor (
929+ coro , loop = loop , name = name , context = context , coro_result = result )
930+ if task ._source_traceback :
931+ del task ._source_traceback [- 1 ]
932+ return task
933+
934+ return factory
935+
936+ eager_task_factory = create_eager_task_factory (Task )
937+
938+
900939# WeakSet containing all alive tasks.
901940_all_tasks = weakref .WeakSet ()
902941
0 commit comments