Wait for a future, shielding it from cancellation. The statement task = asyncio.create_task(something()) res = await shield(task) is exactly equivalent to the statement res = await something() *except* that if the coroutine containing it is cancelled, the
(arg)
| 928 | |
| 929 | |
| 930 | def shield(arg): |
| 931 | """Wait for a future, shielding it from cancellation. |
| 932 | |
| 933 | The statement |
| 934 | |
| 935 | task = asyncio.create_task(something()) |
| 936 | res = await shield(task) |
| 937 | |
| 938 | is exactly equivalent to the statement |
| 939 | |
| 940 | res = await something() |
| 941 | |
| 942 | *except* that if the coroutine containing it is cancelled, the |
| 943 | task running in something() is not cancelled. From the POV of |
| 944 | something(), the cancellation did not happen. But its caller is |
| 945 | still cancelled, so the yield-from expression still raises |
| 946 | CancelledError. Note: If something() is cancelled by other means |
| 947 | this will still cancel shield(). |
| 948 | |
| 949 | If you want to completely ignore cancellation (not recommended) |
| 950 | you can combine shield() with a try/except clause, as follows: |
| 951 | |
| 952 | task = asyncio.create_task(something()) |
| 953 | try: |
| 954 | res = await shield(task) |
| 955 | except CancelledError: |
| 956 | res = None |
| 957 | |
| 958 | Save a reference to tasks passed to this function, to avoid |
| 959 | a task disappearing mid-execution. The event loop only keeps |
| 960 | weak references to tasks. A task that isn't referenced elsewhere |
| 961 | may get garbage collected at any time, even before it's done. |
| 962 | """ |
| 963 | inner = ensure_future(arg) |
| 964 | if inner.done(): |
| 965 | # Shortcut. |
| 966 | return inner |
| 967 | loop = futures._get_loop(inner) |
| 968 | outer = loop.create_future() |
| 969 | |
| 970 | if loop is not None and (cur_task := current_task(loop)) is not None: |
| 971 | futures.future_add_to_awaited_by(inner, cur_task) |
| 972 | else: |
| 973 | cur_task = None |
| 974 | |
| 975 | def _clear_awaited_by_callback(inner): |
| 976 | futures.future_discard_from_awaited_by(inner, cur_task) |
| 977 | |
| 978 | def _inner_done_callback(inner): |
| 979 | if outer.cancelled(): |
| 980 | return |
| 981 | |
| 982 | if inner.cancelled(): |
| 983 | outer.cancel() |
| 984 | else: |
| 985 | exc = inner.exception() |
| 986 | if exc is not None: |
| 987 | outer.set_exception(exc) |
nothing calls this directly
no test coverage detected
searching dependent graphs…