@@ -66,94 +66,103 @@ async def __aenter__(self):
6666 return self
6767
6868 async def __aexit__ (self , et , exc , tb ):
69- self ._exiting = True
69+ try :
70+ self ._exiting = True
7071
71- if (exc is not None and
72- self ._is_base_error (exc ) and
73- self ._base_error is None ):
74- self ._base_error = exc
72+ if (exc is not None and
73+ self ._is_base_error (exc ) and
74+ self ._base_error is None ):
75+ self ._base_error = exc
7576
76- if et is not None and issubclass (et , exceptions .CancelledError ):
77- propagate_cancellation_error = exc
78- else :
79- propagate_cancellation_error = None
77+ if et is not None and issubclass (et , exceptions .CancelledError ):
78+ propagate_cancellation_error = exc
79+ else :
80+ propagate_cancellation_error = None
8081
81- if et is not None :
82- if not self ._aborting :
83- # Our parent task is being cancelled:
84- #
85- # async with TaskGroup() as g:
86- # g.create_task(...)
87- # await ... # <- CancelledError
88- #
89- # or there's an exception in "async with":
90- #
91- # async with TaskGroup() as g:
92- # g.create_task(...)
93- # 1 / 0
94- #
95- self ._abort ()
96-
97- # We use while-loop here because "self._on_completed_fut"
98- # can be cancelled multiple times if our parent task
99- # is being cancelled repeatedly (or even once, when
100- # our own cancellation is already in progress)
101- while self ._tasks :
102- if self ._on_completed_fut is None :
103- self ._on_completed_fut = self ._loop .create_future ()
104-
105- try :
106- await self ._on_completed_fut
107- except exceptions .CancelledError as ex :
82+ if et is not None :
10883 if not self ._aborting :
10984 # Our parent task is being cancelled:
11085 #
111- # async def wrapper():
112- # async with TaskGroup() as g:
113- # g.create_task(foo)
86+ # async with TaskGroup() as g:
87+ # g.create_task(...)
88+ # await ... # <- CancelledError
89+ #
90+ # or there's an exception in "async with":
91+ #
92+ # async with TaskGroup() as g:
93+ # g.create_task(...)
94+ # 1 / 0
11495 #
115- # "wrapper" is being cancelled while "foo" is
116- # still running.
117- propagate_cancellation_error = ex
11896 self ._abort ()
11997
120- self ._on_completed_fut = None
121-
122- assert not self ._tasks
123-
124- if self ._base_error is not None :
125- raise self ._base_error
126-
127- if self ._parent_cancel_requested :
128- # If this flag is set we *must* call uncancel().
129- if self ._parent_task .uncancel () == 0 :
130- # If there are no pending cancellations left,
131- # don't propagate CancelledError.
132- propagate_cancellation_error = None
133-
134- # Propagate CancelledError if there is one, except if there
135- # are other errors -- those have priority.
136- if propagate_cancellation_error is not None and not self ._errors :
137- raise propagate_cancellation_error
138-
139- if et is not None and not issubclass (et , exceptions .CancelledError ):
140- self ._errors .append (exc )
141-
142- if self ._errors :
143- # If the parent task is being cancelled from the outside
144- # of the taskgroup, un-cancel and re-cancel the parent task,
145- # which will keep the cancel count stable.
146- if self ._parent_task .cancelling ():
147- self ._parent_task .uncancel ()
148- self ._parent_task .cancel ()
98+ # We use while-loop here because "self._on_completed_fut"
99+ # can be cancelled multiple times if our parent task
100+ # is being cancelled repeatedly (or even once, when
101+ # our own cancellation is already in progress)
102+ while self ._tasks :
103+ if self ._on_completed_fut is None :
104+ self ._on_completed_fut = self ._loop .create_future ()
105+
106+ try :
107+ await self ._on_completed_fut
108+ except exceptions .CancelledError as ex :
109+ if not self ._aborting :
110+ # Our parent task is being cancelled:
111+ #
112+ # async def wrapper():
113+ # async with TaskGroup() as g:
114+ # g.create_task(foo)
115+ #
116+ # "wrapper" is being cancelled while "foo" is
117+ # still running.
118+ propagate_cancellation_error = ex
119+ self ._abort ()
120+
121+ self ._on_completed_fut = None
122+
123+ assert not self ._tasks
124+
125+ if self ._base_error is not None :
126+ raise self ._base_error
127+
128+ if self ._parent_cancel_requested :
129+ # If this flag is set we *must* call uncancel().
130+ if self ._parent_task .uncancel () == 0 :
131+ # If there are no pending cancellations left,
132+ # don't propagate CancelledError.
133+ propagate_cancellation_error = None
134+
135+ # Propagate CancelledError if there is one, except if there
136+ # are other errors -- those have priority.
137+ if propagate_cancellation_error is not None and not self ._errors :
138+ raise propagate_cancellation_error
139+
140+ if et is not None and not issubclass (et , exceptions .CancelledError ):
141+ self ._errors .append (exc )
142+
143+ if self ._errors :
144+ # If the parent task is being cancelled from the outside
145+ # of the taskgroup, un-cancel and re-cancel the parent task,
146+ # which will keep the cancel count stable.
147+ if self ._parent_task .cancelling ():
148+ self ._parent_task .uncancel ()
149+ self ._parent_task .cancel ()
150+ raise BaseExceptionGroup (
151+ 'unhandled errors in a TaskGroup' ,
152+ self ._errors ,
153+ ) from None
154+ finally :
149155 # Exceptions are heavy objects that can have object
150156 # cycles (bad for GC); let's not keep a reference to
151157 # a bunch of them.
152- try :
153- me = BaseExceptionGroup ('unhandled errors in a TaskGroup' , self ._errors )
154- raise me from None
155- finally :
156- self ._errors = None
158+ propagate_cancellation_error = None
159+ self ._parent_task = None
160+ self ._errors = None
161+ self ._base_error = None
162+ et = None
163+ exc = None
164+ tb = None
165+
157166
158167 def create_task (self , coro , * , name = None , context = None ):
159168 """Create a new task in this group and return it.
0 commit comments