55 'FIRST_COMPLETED' , 'FIRST_EXCEPTION' , 'ALL_COMPLETED' ,
66 'wait' , 'wait_for' , 'as_completed' , 'sleep' ,
77 'gather' , 'shield' , 'ensure_future' , 'run_coroutine_threadsafe' ,
8+ 'current_task' , 'all_tasks' ,
9+ '_register_task' , '_unregister_task' , '_enter_task' , '_leave_task' ,
810)
911
1012import concurrent .futures
2123from .coroutines import coroutine
2224
2325
26+ def current_task (loop = None ):
27+ """Return a currently executed task."""
28+ if loop is None :
29+ loop = events .get_running_loop ()
30+ return _current_tasks .get (loop )
31+
32+
33+ def all_tasks (loop = None ):
34+ """Return a set of all tasks for the loop."""
35+ if loop is None :
36+ loop = events .get_event_loop ()
37+ return {t for t , l in _all_tasks .items () if l is loop }
38+
39+
2440class Task (futures .Future ):
2541 """A coroutine wrapped in a Future."""
2642
@@ -33,13 +49,6 @@ class Task(futures.Future):
3349 # _wakeup(). When _fut_waiter is not None, one of its callbacks
3450 # must be _wakeup().
3551
36- # Weak set containing all tasks alive.
37- _all_tasks = weakref .WeakSet ()
38-
39- # Dictionary containing tasks that are currently active in
40- # all running event loops. {EventLoop: Task}
41- _current_tasks = {}
42-
4352 # If False, don't log a message if the task is destroyed whereas its
4453 # status is still pending
4554 _log_destroy_pending = True
@@ -52,19 +61,25 @@ def current_task(cls, loop=None):
5261
5362 None is returned when called not in the context of a Task.
5463 """
64+ warnings .warn ("Task.current_task() is deprecated, "
65+ "use asyncio.current_task() instead" ,
66+ PendingDeprecationWarning ,
67+ stacklevel = 2 )
5568 if loop is None :
5669 loop = events .get_event_loop ()
57- return cls . _current_tasks . get (loop )
70+ return current_task (loop )
5871
5972 @classmethod
6073 def all_tasks (cls , loop = None ):
6174 """Return a set of all tasks for an event loop.
6275
6376 By default all tasks for the current event loop are returned.
6477 """
65- if loop is None :
66- loop = events .get_event_loop ()
67- return {t for t in cls ._all_tasks if t ._loop is loop }
78+ warnings .warn ("Task.all_tasks() is deprecated, "
79+ "use asyncio.all_tasks() instead" ,
80+ PendingDeprecationWarning ,
81+ stacklevel = 2 )
82+ return all_tasks (loop )
6883
6984 def __init__ (self , coro , * , loop = None ):
7085 super ().__init__ (loop = loop )
@@ -81,7 +96,7 @@ def __init__(self, coro, *, loop=None):
8196 self ._coro = coro
8297
8398 self ._loop .call_soon (self ._step )
84- self .__class__ . _all_tasks . add ( self )
99+ _register_task ( self ._loop , self )
85100
86101 def __del__ (self ):
87102 if self ._state == futures ._PENDING and self ._log_destroy_pending :
@@ -173,7 +188,7 @@ def _step(self, exc=None):
173188 coro = self ._coro
174189 self ._fut_waiter = None
175190
176- self .__class__ . _current_tasks [ self . _loop ] = self
191+ _enter_task ( self ._loop , self )
177192 # Call either coro.throw(exc) or coro.send(None).
178193 try :
179194 if exc is None :
@@ -237,7 +252,7 @@ def _step(self, exc=None):
237252 new_exc = RuntimeError (f'Task got bad yield: { result !r} ' )
238253 self ._loop .call_soon (self ._step , new_exc )
239254 finally :
240- self . __class__ . _current_tasks . pop (self ._loop )
255+ _leave_task (self ._loop , self )
241256 self = None # Needed to break cycles when an exception occurs.
242257
243258 def _wakeup (self , future ):
@@ -715,3 +730,61 @@ def callback():
715730
716731 loop .call_soon_threadsafe (callback )
717732 return future
733+
734+
735+ # WeakKeyDictionary of {Task: EventLoop} containing all tasks alive.
736+ # Task should be a weak reference to remove entry on task garbage
737+ # collection, EventLoop is required
738+ # to not access to private task._loop attribute.
739+ _all_tasks = weakref .WeakKeyDictionary ()
740+
741+ # Dictionary containing tasks that are currently active in
742+ # all running event loops. {EventLoop: Task}
743+ _current_tasks = {}
744+
745+
746+ def _register_task (loop , task ):
747+ """Register a new task in asyncio as executed by loop.
748+
749+ Returns None.
750+ """
751+ _all_tasks [task ] = loop
752+
753+
754+ def _enter_task (loop , task ):
755+ current_task = _current_tasks .get (loop )
756+ if current_task is not None :
757+ raise RuntimeError (f"Cannot enter into task { task !r} while another "
758+ f"task { current_task !r} is being executed." )
759+ _current_tasks [loop ] = task
760+
761+
762+ def _leave_task (loop , task ):
763+ current_task = _current_tasks .get (loop )
764+ if current_task is not task :
765+ raise RuntimeError (f"Leaving task { task !r} does not match "
766+ f"the current task { current_task !r} ." )
767+ del _current_tasks [loop ]
768+
769+
770+ def _unregister_task (loop , task ):
771+ _all_tasks .pop (task , None )
772+
773+
774+ _py_register_task = _register_task
775+ _py_unregister_task = _unregister_task
776+ _py_enter_task = _enter_task
777+ _py_leave_task = _leave_task
778+
779+
780+ try :
781+ from _asyncio import (_register_task , _unregister_task ,
782+ _enter_task , _leave_task ,
783+ _all_tasks , _current_tasks )
784+ except ImportError :
785+ pass
786+ else :
787+ _c_register_task = _register_task
788+ _c_unregister_task = _unregister_task
789+ _c_enter_task = _enter_task
790+ _c_leave_task = _leave_task
0 commit comments