@@ -201,25 +201,25 @@ def cancel(self, msg=None):
201
201
self ._log_traceback = False
202
202
if self .done ():
203
203
return False
204
+ # Once set, we keep trying to cancel the Task at every
205
+ # safe opportunity. There's currently no way to stop this,
206
+ # but maybe we could add CancelledError.abort() or something
207
+ # for tasks to explicitly stop cancellation.
208
+ self ._must_cancel = True
204
209
if self ._fut_waiter is not None :
205
210
if self ._fut_waiter .cancel (msg = msg ):
206
211
# Leave self._fut_waiter; it may be a Task that
207
212
# catches and ignores the cancellation so we may have
208
213
# to cancel it again later.
209
214
return True
210
215
# It must be the case that self.__step is already scheduled.
211
- self ._must_cancel = True
212
216
self ._cancel_message = msg
213
217
return True
214
218
215
219
def __step (self , exc = None ):
216
220
if self .done ():
217
221
raise exceptions .InvalidStateError (
218
222
f'_step(): already done: { self !r} , { exc !r} ' )
219
- if self ._must_cancel :
220
- if not isinstance (exc , exceptions .CancelledError ):
221
- exc = self ._make_cancelled_error ()
222
- self ._must_cancel = False
223
223
coro = self ._coro
224
224
self ._fut_waiter = None
225
225
@@ -234,11 +234,13 @@ def __step(self, exc=None):
234
234
result = coro .throw (exc )
235
235
except StopIteration as exc :
236
236
if self ._must_cancel :
237
- # Task is cancelled right before coro stops.
237
+ # We were cancelled, but we're done, so we just pretend
238
+ # that the cancellation came in too late and the result
239
+ # was already ready. If the calling Task was the source
240
+ # of the cancellation, then its own _must_cancel will
241
+ # still be set, so it will still keep trying to cancel.
238
242
self ._must_cancel = False
239
- super ().cancel (msg = self ._cancel_message )
240
- else :
241
- super ().set_result (exc .value )
243
+ super ().set_result (exc .value )
242
244
except exceptions .CancelledError as exc :
243
245
# Save the original exception so we can chain it later.
244
246
self ._cancelled_exc = exc
@@ -272,7 +274,12 @@ def __step(self, exc=None):
272
274
if self ._must_cancel :
273
275
if self ._fut_waiter .cancel (
274
276
msg = self ._cancel_message ):
275
- self ._must_cancel = False
277
+ # Even if we cancelled the future, we
278
+ # leave _must_cancel set because the
279
+ # future might racily return a result
280
+ # at the same time as the cancellation,
281
+ # so we'll need to retry cancellation.
282
+ pass
276
283
else :
277
284
3D35
new_exc = RuntimeError (
278
285
f'yield was used instead of yield from '
0 commit comments