@@ -293,14 +293,15 @@ It is recommended that coroutines use ``try/finally`` blocks to robustly
293
293
perform clean-up logic. In case :exc: `asyncio.CancelledError `
294
294
is explicitly caught, it should generally be propagated when
295
295
clean-up is complete. Most code can safely ignore :exc: `asyncio.CancelledError `.
296
- If a task needs to continue despite receiving an :exc: `asyncio.CancelledError `,
297
- it should :func: `uncancel itself <asyncio.Task.uncancel> `.
298
296
299
- Important asyncio components, like :class: `asyncio.TaskGroup ` and the
300
- :func: `asyncio.timeout ` context manager, are implemented using cancellation
301
- internally and might misbehave if a coroutine swallows
302
- :exc: `asyncio.CancelledError `.
297
+ asyncio components that enable structured concurrency, like
298
+ :class: `asyncio.TaskGroup ` and the :func: `asyncio.timeout ` context manager,
299
+ are implemented using cancellation internally and might misbehave if
300
+ a coroutine swallows :exc: `asyncio.CancelledError `. In particular,
301
+ they might :func: `uncancel <asyncio.Task.uncancel> ` a task to properly
302
+ isolate cancelling only a given structured block within the task's body.
303
303
304
+ .. _taskgroups :
304
305
305
306
Task Groups
306
307
===========
@@ -996,106 +997,6 @@ Task Object
996
997
Deprecation warning is emitted if *loop * is not specified
997
998
and there is no running event loop.
998
999
999
- .. method :: cancel(msg=None)
1000
-
1001
- Request the Task to be cancelled.
1002
-
1003
- This arranges for a :exc: `CancelledError ` exception to be thrown
1004
- into the wrapped coroutine on the next cycle of the event loop.
1005
-
1006
- The coroutine then has a chance to clean up or even deny the
1007
- request by suppressing the exception with a :keyword: `try ` ...
1008
- ... ``except CancelledError `` ... :keyword: `finally ` block.
1009
- Therefore, unlike :meth: `Future.cancel `, :meth: `Task.cancel ` does
1010
- not guarantee that the Task will be cancelled, although
1011
- suppressing cancellation completely is not common and is actively
1012
- discouraged.
1013
-
1014
- .. versionchanged :: 3.9
1015
- Added the *msg * parameter.
1016
-
1017
- .. deprecated-removed :: 3.11 3.14
1018
- *msg * parameter is ambiguous when multiple :meth: `cancel `
1019
- are called with different cancellation messages.
1020
- The argument will be removed.
1021
-
1022
- .. _asyncio_example_task_cancel :
1023
-
1024
- The following example illustrates how coroutines can intercept
1025
- the cancellation request::
1026
-
1027
- async def cancel_me():
1028
- print('cancel_me(): before sleep')
1029
-
1030
- try:
1031
- # Wait for 1 hour
1032
- await asyncio.sleep(3600)
1033
- except asyncio.CancelledError:
1034
- print('cancel_me(): cancel sleep')
1035
- raise
1036
- finally:
1037
- print('cancel_me(): after sleep')
1038
-
1039
- async def main():
1040
- # Create a "cancel_me" Task
1041
- task = asyncio.create_task(cancel_me())
1042
-
1043
- # Wait for 1 second
1044
- await asyncio.sleep(1)
1045
-
1046
- task.cancel()
1047
- try:
1048
- await task
1049
- except asyncio.CancelledError:
1050
- print("main(): cancel_me is cancelled now")
1051
-
1052
- asyncio.run(main())
1053
-
1054
- # Expected output:
1055
- #
1056
- # cancel_me(): before sleep
1057
- # cancel_me(): cancel sleep
1058
- # cancel_me(): after sleep
1059
- # main(): cancel_me is cancelled now
1060
-
1061
- .. method :: cancelled()
1062
-
1063
- Return ``True `` if the Task is *cancelled *.
1064
-
1065
- The Task is *cancelled * when the cancellation was requested with
1066
- :meth: `cancel ` and the wrapped coroutine propagated the
1067
- :exc: `CancelledError ` exception thrown into it.
1068
-
1069
- .. method :: cancelling()
1070
-
1071
- Return the number of cancellation requests to this Task, i.e.,
1072
- the number of calls to :meth: `cancel `.
1073
-
1074
- Note that if this number is greater than zero but the Task is
1075
- still executing, :meth: `cancelled ` will still return ``False ``.
1076
- It's because this number can be lowered by calling :meth: `uncancel `,
1077
- which can lead to the task not being cancelled after all if the
1078
- cancellation requests go down to zero.
1079
-
1080
- .. method :: uncancel()
1081
-
1082
- Decrement the count of cancellation requests to this Task.
1083
-
1084
- Returns the remaining number of cancellation requests.
1085
-
1086
- This should be used by tasks that catch :exc: `CancelledError `
1087
- and wish to continue indefinitely until they are cancelled again::
1088
-
1089
- async def resilient_task():
1090
- try:
1091
- await do_work()
1092
- except asyncio.CancelledError:
1093
- asyncio.current_task().uncancel()
1094
- await do_work()
1095
-
1096
- Note that once execution of a cancelled task completed, further
1097
- calls to :meth: `uncancel ` are ineffective.
1098
-
1099
1000
.. method :: done()
1100
1001
1101
1002
Return ``True `` if the Task is *done *.
@@ -1209,3 +1110,153 @@ Task Object
1209
1110
in the :func: `repr ` output of a task object.
1210
1111
1211
1112
.. versionadded :: 3.8
1113
+
1114
+ .. method :: cancel(msg=None)
1115
+
1116
+ Request the Task to be cancelled.
1117
+
1118
+ This arranges for a :exc: `CancelledError ` exception to be thrown
1119
+ into the wrapped coroutine on the next cycle of the event loop.
1120
+
1121
+ The coroutine then has a chance to clean up or even deny the
1122
+ request by suppressing the exception with a :keyword: `try ` ...
1123
+ ... ``except CancelledError `` ... :keyword: `finally ` block.
1124
+ Therefore, unlike :meth: `Future.cancel `, :meth: `Task.cancel ` does
1125
+ not guarantee that the Task will be cancelled, although
1126
+ suppressing cancellation completely is not common and is actively
1127
+ discouraged.
1128
+
1129
+ .. versionchanged :: 3.9
1130
+ Added the *msg * parameter.
1131
+
1132
+ .. deprecated-removed :: 3.11 3.14
1133
+ *msg * parameter is ambiguous when multiple :meth: `cancel `
1134
+ are called with different cancellation messages.
1135
+ The argument will be removed.
1136
+
1137
+ .. _asyncio_example_task_cancel :
1138
+
1139
+ The following example illustrates how coroutines can intercept
1140
+ the cancellation request::
1141
+
1142
+ async def cancel_me():
1143
+ print('cancel_me(): before sleep')
1144
+
1145
+ try:
1146
+ # Wait for 1 hour
1147
+ await asyncio.sleep(3600)
1148
+ except asyncio.CancelledError:
1149
+ print('cancel_me(): cancel sleep')
1150
+ raise
1151
+ finally:
1152
+ print('cancel_me(): after sleep')
1153
+
1154
+ async def main():
1155
+ # Create a "cancel_me" Task
1156
+ task = asyncio.create_task(cancel_me())
1157
+
1158
+ # Wait for 1 second
1159
+ await asyncio.sleep(1)
1160
+
1161
+ task.cancel()
1162
+ try:
1163
+ await task
1164
+ except asyncio.CancelledError:
1165
+ print("main(): cancel_me is cancelled now")
1166
+
1167
+ asyncio.run(main())
1168
+
1169
+ # Expected output:
1170
+ #
1171
+ # cancel_me(): before sleep
1172
+ # cancel_me(): cancel sleep
1173
+ # cancel_me(): after sleep
1174
+ # main(): cancel_me is cancelled now
1175
+
1176
+ .. method :: cancelled()
1177
+
1178
+ Return ``True `` if the Task is *cancelled *.
1179
+
1180
+ The Task is *cancelled * when the cancellation was requested with
1181
+ :meth: `cancel ` and the wrapped coroutine propagated the
1182
+ :exc: `CancelledError ` exception thrown into it.
1183
+
1184
+ .. method :: cancelling()
1185
+
1186
+ Return the number of cancellation requests to this Task, i.e.,
1187
+ the number of calls to :meth: `cancel `.
1188
+
1189
+ Note that if this number is greater than zero but the Task is
1190
+ still executing, :meth: `cancelled ` will still return ``False ``.
1191
+ It's because this number can be lowered by calling :meth: `uncancel `,
1192
+ which can lead to the task not being cancelled after all if the
1193
+ cancellation requests go down to zero.
1194
+
1195
+ .. versionadded :: 3.11
1196
+
1197
+ .. method :: uncancel()
1198
+
1199
+ Decrement the count of cancellation requests to this Task.
1200
+
1201
+ Returns the remaining number of cancellation requests.
1202
+
1203
+ Note that once execution of a cancelled task completed, further
1204
+ calls to :meth: `uncancel ` are ineffective.
1205
+
1206
+ .. versionadded :: 3.11
1207
+
1208
+ This method is used by asyncio's internals and isn't expected to be
1209
+ used by end-user code. In particular, if a Task gets successfully
1210
+ uncancelled, this allows for elements of structured concurrency like
1211
+ :ref: `taskgroups ` or and :func: `asyncio.timeout ` to continue running,
1212
+ isolating cancellation to the respective structured block.
1213
+ For example::
1214
+
1215
+ async def make_request_with_timeout():
1216
+ try:
1217
+ async with asyncio.timeout(1):
1218
+ # Structured block affected by the timeout:
1219
+ await make_request()
1220
+ await make_another_request()
1221
+ except TimeoutError:
1222
+ log("There was a timeout")
1223
+ # Outer code not affected by the timeout:
1224
+ await unrelated_code()
1225
+
1226
+ While the block with ``make_request() `` and ``make_another_request() ``
1227
+ might get cancelled due to the timeout, ``unrelated_code() `` should
1228
+ continue running even in case of the timeout. This can be
1229
+ implemented with :meth: `uncancel ` as follows::
1230
+
1231
+ async def make_request_with_timeout():
1232
+ task = asyncio.current_task()
1233
+ loop = task.get_loop()
1234
+ i_called_cancel = False
1235
+
1236
+ def on_timeout():
1237
+ nonlocal i_called_cancel
1238
+ i_called_cancel = True
1239
+ task.cancel()
1240
+
1241
+ timeout_handle = loop.call_later(1, on_timeout)
1242
+ try:
1243
+ try:
1244
+ # Structured block affected by the timeout
1245
+ await make_request()
1246
+ await make_another_request()
1247
+ finally:
1248
+ timeout_handle.cancel()
1249
+ if (
1250
+ i_called_cancel
1251
+ and task.uncancel() == 0
1252
+ and sys.exc_info()[0] is asyncio.CancelledError
1253
+ ):
1254
+ raise TimeoutError
1255
+ except TimeoutError:
1256
+ log("There was a timeout")
1257
+
1258
+ # Outer code not affected by the timeout:
1259
+ await unrelated_code()
1260
+
1261
+ :class: `TaskGroup ` context managers use :func: `uncancel ` in
1262
+ a similar fashion.
0 commit comments