Runtime error cannot close a running event loop

You can exit the asyncio event loop by returning from the main coroutine used as the entry point for the asyncio program. In this tutorial, you will discover how to exit the asyncio event loop in P…

You can exit the asyncio event loop by returning from the main coroutine used as the entry point for the asyncio program.

In this tutorial, you will discover how to exit the asyncio event loop in Python.

Let’s get started.

Asyncio programs are executed by an event loop.

The event loop is responsible for running tasks, executing callbacks and managing non-blocking I/O.

The event loop is the core of every asyncio application. Event loops run asynchronous tasks and callbacks, perform network IO operations, and run subprocesses.

— Asyncio Event Loop

You can learn more about the asyncio event loop in the tutorial:

  • What is the Asyncio Event Loop

We execute asyncio programs using the high-level API via the asyncio.run() function.

This function takes a coroutine as an argument which is used as the entry point to the asyncio program.

For example:

...

# run an asyncio program

asyncio.run(main())

You can learn more about how to run asyncio programs in the tutorial:

  • How to Run an Asyncio Program in Python

Running the event loop is blocking, this means it will block the calling thread until the event loop is terminated.

This raises the question, how do we exit the asyncio event loop?

We may need to exit the asyncio event loop from within the asyncio program for many reasons, such as:

  • User request.
  • Failure of a required resource.
  • Early completion of tasks.

How can an asyncio program exit the event loop?

It also raises related questions, such as:

  • Does a running task prevent the asyncio event loop from exiting normally?
  • Does a running subprocess prevent the asyncio event loop from exiting normally?

We can explore these questions with worked examples.

How to Exit the Asyncio Event Loop

An asyncio event loop can be exited by returning from the main coroutine.

We may refer to the coroutine that is passed to asyncio.run() to run the asyncio event loop as the main coroutine, for lack of a better name.

When this coroutine exits, the event loop will terminate.

This is the normal way for an asyncio program to end.

There are three ways this may occur, they are:

  • The main coroutine completes, e.g. reaches the end and returns,
  • The main coroutine returns early, e.g. the return expression.
  • The main coroutine raises an unhandled exception, e.g. directly or indirectly.

The asyncio event loop does provide methods for stopping the event loop. These are the close() method and the stop() method.

The close() method can only be called once all tasks have stopped running otherwise a RuntimeError will be raised. It cannot be called safely from a running coroutine directly. The stop() method will schedule an attempt to close the event loop if run_forever() was called, but it too requires that all tasks have been completed otherwise it will raise a RuntimeError.

These are in a low-level API and are generally not intended for application developers, instead, they are reserved for framework developers. You may be able to trigger a close using these methods, e.g. via another thread, but they are not recommended.

Generally, these methods are intended to be called from outside the event loop, e.g. by a managing thread or process.

Now that we know how to exit the asyncio event loop, let’s look at some worked examples.

Confused by the asyncio module API?
Download my FREE PDF cheat sheet

Example of Exiting the Event Loop

The recommended way to exit the asyncio event loop is to exit the main coroutine.

In this section, we will explore the different common ways of exiting the main coroutine.

Exit the Event Loop After the Main Coroutine Finishes

We can explore the normal case of closing the asyncio event loop by letting the main coroutine run to completion.

In this example, the main coroutine will schedule a task coroutine and wait for it to complete. The task will report a message, sleep for two seconds, then report a final message. Once the task is complete, the main coroutine will resume and report a final message for exiting.

The complete example is listed below.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

# SuperFastPython.com

# example of exiting the event loop normally

import asyncio

# task coroutine

async def work():

    # report a message

    print(‘Task starting’)

    # simulate work

    await asyncio.sleep(2)

    # report a message

    print(‘Task done’)

# main coroutine

async def main():

    # schedule the task

    task = asyncio.create_task(work())

    # suspend a moment

    await task

    # report a message

    print(‘Main done’)

# run the asyncio program

asyncio.run(main())

Running the example creates the main() coroutine and runs it as the entry point for the asyncio program.

The main() coroutine runs and creates the work() coroutine and schedules it as a task. It then awaits the task and suspends it until the task is completed.

The task runs, reports a message, sleeps for 2 seconds, then reports a final message.

The main() coroutine resumes and reports a final message before ending normally.

The asyncio event loop is then terminated and the main thread is closed.

This highlights how we can exit the asyncio event loop normally by completing the main coroutine.

Task starting

Task done

Main done

Exit the Event Loop With an Early Return

We can explore how to exit the asyncio event loop with an early return statement in the main coroutine.

In this example, the main coroutine will schedule an independent task, suspend a moment to allow the task to begin executing, then issue an early return before the independent task has had a chance to complete.

The complete example is listed below.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

# SuperFastPython.com

# forcing the exit of the event loop

import asyncio

# task coroutine

async def work():

    # report a message

    print(‘Task starting’)

    # simulate work

    await asyncio.sleep(2)

    # report a message

    print(‘Task done’)

# main coroutine

async def main():

    # schedule the task

    task = asyncio.create_task(work())

    # suspend a moment

    await asyncio.sleep(1)

    # exit the event loop

    return

    # report a message

    print(‘Main done’)

# run the asyncio program

asyncio.run(main())

Running the example creates the main() coroutine and runs it as the entry point for the asyncio program.

The main() coroutine runs and creates the work() coroutine and schedules it as a task.

It then sleeps for one second, suspending and allowing the task to run.

The task begins running, reporting a message, and sleeping.

The main() coroutine resumes and returns.

This terminates the asyncio event loop and the main thread. The running task is terminated along with the event loop and is not given an opportunity to complete.

This highlights how returning from the main coroutine at any point will terminate the asyncio event loop.

Exit the Event Loop With an Exception

We can explore how to exit the asyncio event loop by raising an unhandled exception.

This will terminate the main coroutine and cause it to return, terminating the asyncio event loop.

In this example, the main coroutine will schedule an independent task and suspend a moment to allow the task to begin executing. It will then raise an exception that is not handled, causing the main coroutine to terminate and in turn close the event loop.

The complete example is listed below.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

# SuperFastPython.com

# forcing the exit of the event loop

import asyncio

# task coroutine

async def work():

    # report a message

    print(‘Task starting’)

    # simulate work

    await asyncio.sleep(2)

    # report a message

    print(‘Task done’)

# main coroutine

async def main():

    # schedule the task

    task = asyncio.create_task(work())

    # suspend a moment

    await asyncio.sleep(1)

    # exit the event loop

    raise Exception(‘Stop this thing’)

    # report a message

    print(‘Main done’)

# run the asyncio program

asyncio.run(main())

Running the example creates the main() coroutine and runs it as the entry point for the asyncio program.

The main() coroutine runs and creates the work() coroutine and schedules it as a task.

It then sleeps for one second, suspending and allowing the task to run.

The task begins running, reporting a message, and sleeping.

The main() coroutine resumes and raises an unhandled exception.

This exits the main() coroutine and terminates the asyncio event loop. The running task is not given an opportunity to complete.

This highlights how we may exit the asyncio event loop by raising an unhandled exception.

Task starting

Traceback (most recent call last):

  …

Exception: Stop this thing

The main coroutine does not have to raise an exception directly in order to terminate.

An exception can be raised in a called function or awaited coroutine.

If unheralded, the exception can propagate back to the main coroutine and in turn terminate the event loop.

The example below demonstrates this.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

# SuperFastPython.com

# forcing the exit of the event loop

import asyncio

# task coroutine

async def work():

    # report a message

    print(‘Task starting’)

    # simulate work

    await asyncio.sleep(2)

    # exit the event loop

    raise Exception(‘Stop this thing’)

    # report a message

    print(‘Task done’)

# main coroutine

async def main():

    # schedule the task

    task = asyncio.create_task(work())

    # suspend a moment

    await task

    # report a message

    print(‘Main done’)

# run the asyncio program

asyncio.run(main())

Running the example creates the main() coroutine and runs it as the entry point for the asyncio program.

The main() coroutine runs and creates the work() coroutine and schedules it as a task. It then awaits the task and suspends it until the task is completed.

The task runs, reports a message, sleeps for 2 seconds, then raises an exception.

The exception propagates from the work() coroutine to the main() coroutine and then exits the main() coroutine, terminating the asyncio event loop.

This highlights that we may exit the asyncio event loop by allowing exceptions to bubble up from coroutines to the main coroutine.

Task starting

Traceback (most recent call last):

  …

Exception: Stop this thing


Free Python Asyncio Course

Download my asyncio API cheat sheet and as a bonus you will get FREE access to my 7-day email course on asyncio.

Discover how to use the Python asyncio module including how to define, create, and run new coroutines and how to use non-blocking I/O.

Learn more
 


Cannot Directly Stop the Event Loop While a Task is Running

We cannot directly close or stop the event loop from within the event loop.

The low-level event loop provides close() and stop() methods intended for stopping an asyncio event loop from the outside, such as by the owning thread or process.

These methods generally cannot be called directly from coroutines and tasks running in the event loop.

We can explore this with some worked examples.

Cannot Call close() From Within a Coroutine

We can explore calling the close() method from the main coroutine.

In this example, we will schedule a task in the background, suspend it for a moment to allow the task to start, then call the close() method on the currently running event loop.

We expect this to fail with a RuntimeError exception as we cannot close the event loop while tasks are executing.

The complete example is listed below.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

# SuperFastPython.com

# forcing the exit of the event loop

import asyncio

# task coroutine

async def work():

    # report a message

    print(‘Task starting’)

    # simulate work

    await asyncio.sleep(2)

    # report a message

    print(‘Task done’)

# main coroutine

async def main():

    # schedule the task

    task = asyncio.create_task(work())

    # suspend a moment

    await asyncio.sleep(1)

    # get the event loop

    loop = asyncio.get_running_loop()

    # schedule a stop to the event loop

    loop.close()

    # report a message

    print(‘Main done’)

# run the asyncio program

asyncio.run(main())

Running the example creates the main() coroutine and runs it as the entry point for the asyncio program.

The main() coroutine runs and creates the work() coroutine and schedules it as a task.

It then sleeps for one second, suspending and allowing the task to run.

The main() coroutine then gets access to the running event loop and calls the close() method.

This fails with a RuntimeError exception, as we expected, indicating that we cannot close a running event loop.

Task starting

Traceback (most recent call last):

  …

RuntimeError: Cannot close a running event loop

Cannot Call stop() From Within a Coroutine

We can explore calling the stop() method from the main coroutine.

This method is intended to be called on an event loop that is running forever, via the run_forever() method.

In this example, we will schedule a task in the background, suspend it for a moment to allow the task to start, then call the stop() method on the currently running event loop.

We expect this to fail with a RuntimeError exception as we cannot stop the event loop while tasks are executing.

The complete example is listed below.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

# SuperFastPython.com

# forcing the exit of the event loop

import asyncio

# task coroutine

async def work():

    # report a message

    print(‘Task starting’)

    # simulate work

    await asyncio.sleep(2)

    # report a message

    print(‘Task done’)

# main coroutine

async def main():

    # schedule the task

    task = asyncio.create_task(work())

    # suspend a moment

    await asyncio.sleep(1)

    # get the event loop

    loop = asyncio.get_running_loop()

    # schedule a stop to the event loop

    loop.stop()

    # report a message

    print(‘Main done’)

# run the asyncio program

asyncio.run(main())

Running the example creates the main() coroutine and runs it as the entry point for the asyncio program.

The main() coroutine runs and creates the work() coroutine and schedules it as a task.

It then sleeps for one second, suspending and allowing the task to run.

The main() coroutine then gets access to the running event loop and calls the stop() method.

This fails with a RuntimeError exception, as we expected, indicating the event loop was stopped while tasks were still incomplete.

Task starting

Main done

Traceback (most recent call last):

RuntimeError: Event loop stopped before Future completed.

Does a Task Stop the Event Loop Exiting

We can explore if the event loop will stop if an independent task is still executing.

This can be achieved by scheduling the task, allowing it to start running, then having the main coroutine return.

This will either block, allowing the independent task to complete, or return immediately, terminating the event loop and the running task.

The example below explores this scenario.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

# SuperFastPython.com

# test if a task stops the event loop from exiting

import asyncio

# task coroutine

async def work():

    # report a message

    print(‘Task starting’)

    # simulate work

    await asyncio.sleep(2)

    # report a message

    print(‘Task done’)

# main coroutine

async def main():

    # schedule the task

    task = asyncio.create_task(work())

    # suspend a moment

    await asyncio.sleep(0)

    # report a message

    print(‘Main done’)

# run the asyncio program

asyncio.run(main())

Running the example creates the main() coroutine and runs it as the entry point for the asyncio program.

The main() coroutine runs and creates the work() coroutine and schedules it as a task.

It then sleeps for zero seconds, suspending and allowing the task to run.

The task runs, reports a message, and starts to sleep.

The main() coroutine resumes, reports a message, and exits normally.

It closes the event loop and then the main thread of the program. The running independent task is terminated along with the event loop.

This highlights that a running task will not prevent the event loop from exiting.

Does a Subprocess Stop the Event Loop Exiting

We can explore if the event loop will stop if a command is being executed in a subprocess.

A command is a program on the operating system that may be executed directly or via the shell. If executed by the shell, we can use shell scripting language elements and variables.

We can run a command from an asyncio program using the asyncio.create_subprocess_shell() function.

You can learn more about running commands from asyncio via the shell in the tutorial:

  • Asyncio Subprocess With create_subprocess_shell()

This will run the command and return a subprocess.

We can execute a command from the main coroutine, suspend to allow the command to execute, then return from the main coroutine. The command may continue to run and may or may not block the event loop, preventing it from terminating.

The example below explores this scenario.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

# SuperFastPython.com

# test if a subprocess stops the event loop from exiting

import asyncio

# task coroutine

async def work():

    # report a message

    print(‘Task starting’)

    # run a subprocess

    process = await asyncio.create_subprocess_shell(‘sleep 2;echo hi’)

    # wait for the subprocess to finish

    await process.wait()

    # report a message

    print(‘Task done’)

# main coroutine

async def main():

    # schedule the task

    task = asyncio.create_task(work())

    # suspend a moment, allow the subprocess to start

    await asyncio.sleep(1)

    # report a message

    print(‘Main done’)

# run the asyncio program

asyncio.run(main())

# report a message

print(‘Event loop is closed’)

Running the example creates the main() coroutine and runs it as the entry point for the asyncio program.

The main() coroutine runs and creates the work() coroutine and schedules it as a task.

It then sleeps for one second, suspending and allowing the task to run.

The task runs, reports a message, then executes the command and waits for the command to complete.

The command begins executing, running the “sleep” command for two seconds.

The main() coroutine resumes and exits.

This terminates the event loop and the asyncio task.

The subprocess running the command continues to run. It finishes the sleep command and runs the echo command reporting a message on standard out.

This example highlights that exiting the asyncio event loop does not terminate running subprocesses.

Task starting

Main done

Event loop is closed

hi

Does a Thread Stop the Event Loop From Exiting

We can explore whether running a function in a new thread will stop the event loop from executing.

A blocking function can be run in a new thread from asyncio via the asyncio.to_thread() function. This returns a coroutine that can be awaited or scheduled as a task.

You can learn more about running blocking functions in a new thread in the tutorial:

  • How to Use Asyncio to_thread()

We can execute a blocking function in a new thread and schedule it as a task, suspend to allow it to begin executing, then return from the main coroutine.

The blocking function may continue to run or not, and it may or may not prevent the asyncio event loop from terminating immediately.

The example below explores this scenario.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

# SuperFastPython.com

# test if a task stops the event loop from exiting

import time

import asyncio

# task coroutine

def work():

    # report a message

    print(‘Task starting’)

    # simulate work

    time.sleep(2)

    # report a message

    print(‘Task done’)

# main coroutine

async def main():

    # run function in a separate thread

    coro = asyncio.to_thread(work)

    # schedule the task

    task = asyncio.create_task(coro)

    # suspend a moment, allow the new thread to start

    await asyncio.sleep(1)

    # report a message

    print(‘Main done’)

# run the asyncio program

asyncio.run(main())

# report a message

print(‘Event loop is closed’)

Running the example creates the main() coroutine and runs it as the entry point for the asyncio program.

The main() coroutine runs and creates a coroutine to run the blocking function in a new thread.

It then schedules the coroutine as an independent task and sleeps for one second, suspending and allowing the task to execute.

The task begins running in a new thread, reporting a message, and sleeping.

The main() coroutine resumes, reports a message, and exits.

This does not terminate the event loop. The event loop blocks, waiting for the new thread to complete.

The blocking function in the new thread resumes and reports a final message before exiting.

The event loop terminates and a final message is reported that the event loop has been closed and the main thread is back in control.

This highlights that a blocking thread running in the asyncio event loop can prevent the event loop from exiting.

Task starting

Main done

Task done

Event loop is closed

Further Reading

This section provides additional resources that you may find helpful.

Books

  • Python Asyncio Jump-Start, Jason Brownlee, 2022 (my book).
  • Python Asyncio Interview Questions
  • Asyncio Module API Cheat Sheet

I also recommend the following books:

  • Python Concurrency with asyncio, Matthew Fowler, 2022.
  • Using Asyncio in Python, Caleb Hattingh, 2020.

Guides

  • Python Asyncio: The Complete Guide

APIs

  • asyncio — Asynchronous I/O
  • Asyncio Coroutines and Tasks
  • Asyncio Streams
  • Asyncio Subprocesses
  • Asyncio Queues
  • Asyncio Synchronization Primitives

References

  • Asynchronous I/O, Wikipedia.
  • Coroutine, Wikipedia.

Takeaways

You now know how to exit the asyncio event loop in Python.

Do you have any questions?
Ask your questions in the comments below and I will do my best to answer.

Photo by Anurag singh on Unsplash

There are so many solutions to this Runtime error on Stack Overflow that don’t work. If you write Python code using asyncio, you either have or most likely will run into this runtime error at some point. I personally came across this error while running asynchronous API requests. When you’re running an async/await function using the asyncio library, you may get a RuntimeError: Event Loop is closed error even after your loop is already done running. This problem may not always affect the functionality of a single script but will affect the functionality of multiple scripts. 

In this post we’ll go over:

  • The asyncio run vs loop.run_until_complete commands
  • The Runtime Error: Event loop is closed Problem
    • Error logs from the asyncio library
  • The Runtime Error: Event loop is closed Solution
    • Editing the Source Code of the asyncio library locally.

Click here for the source code you can copy and paste directly into your program → asyncio Runtime Error: Event Loop is closed Solution

The asyncio Library and run vs loop.run_until_complete

The aforementioned issue can come up when using the asyncio.run() or the loop.run_until_complete() functions. The documentation suggests using run() over run_until_complete() because run() handles the setting and closing of the event loop object. The run() command is actually just a wrapper around the run_until_complete() command. For a more thorough explanation, I suggest reading this guide on run() vs run_until_complete().

Runtime Error: Event Loop is closed Problem

Usually, this error can be fixed by replacing the asyncio.run() command with the command asyncio.new_event_loop().run_until_complete(). However, when used in conjunction with the aiohttp library to make multiple API requests, that alternative will not work. This is due to multiple reasons. First, a TCP connector problem, second an SSL protocol problem, and thirdly an issue with the Proactor transport object. Let’s take a look at what the error logs for this problem may look like prior to the RuntimeError: Event Loop is closed line.

Error Logs from the asyncio Library

Here is what the logs look like for this error:

Unclosed connector
connections: ['[(<aiohttp.client_proto.ResponseHandler object at 0x00000254D4590280>, 25425.812)]']
connector: <aiohttp.connector.TCPConnector object at 0x00000254D4584730>
Fatal error on SSL transport
protocol: <asyncio.sslproto.SSLProtocol object at 0x00000254D459E1C0>
transport: <_ProactorSocketTransport fd=916 read=<_OverlappedFuture cancelled>>
…
AttributeError: 'NoneType' object has no attribute 'send'
Exception ignored in: <function _SSLProtocolTransport.__del__ at 0x00000254D4095E50>
Traceback (most recent call last):
…
RuntimeError: Event loop is closed

Runtime Error: Event Loop is closed Solution

The problem with this error being raised isn’t so much that we can’t run the function. It’s more so we have an error exit code, can’t run multiple functions in sequence, and get yelled at by the command line. The asyncio library not the most stable. So, this error is not too surprising, but you know what is surprising? This error has been around for a while. I found so many solutions on Stack Overflow that don’t work. Theoretically run() should close the event loop gracefully. Gracefully means no errors. Let’s look at how we can change the source code to force a graceful shutdown of the function.

Edit the Source Code of the asyncio Library run Command Locally

How are we going to fix the error? We’re going to wrap a modification around the class for the _ProactorBasePipeTransport’s delete method. This is the method that shuts down the event loop throwing the error. To do this, we’re going to import the wraps annotation from functools and the _ProactorBasePipeTransport from asyncio.proactor_events. Technically we don’t have to import the Proactor class, but we’ll import for ease.

Let’s create our helper function to shut the interpreter up after we’ve already finished our loop. Our function will take one parameter, the function we’re wrapping. We’ll annotate a wrapper function that wraps the passed in function parameter. The inner wrapper function will take itself as an argument, and any number of unnamed and named arguments.

All the wrapper function does is try to execute the function as normal, except when there’s a RuntimeError, it’s not raised. After defining the functions, we’ll edit the __del__ function of the Proactor object and set it to the silenced version. Now the closing of the loop will not raise errors in the console.

"""fix yelling at me error"""
from functools import wraps
 
from asyncio.proactor_events import _ProactorBasePipeTransport
 
def silence_event_loop_closed(func):
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        try:
            return func(self, *args, **kwargs)
        except RuntimeError as e:
            if str(e) != 'Event loop is closed':
                raise
    return wrapper
 
_ProactorBasePipeTransport.__del__ = silence_event_loop_closed(_ProactorBasePipeTransport.__del__)
"""fix yelling at me error end"""

Summary fixing the RuntimeError: Event Loop is closed asyncio Error

The problem we encountered was the program shutdown process raising an error when running asyncio.run() on an async event loop when it shouldn’t have. The solution we implemented doesn’t directly solve the issue of the program not closing all its processes gracefully, but it does protect us from the problem. We directly imported the responsible object and wrote a wrapper around it with the functools Python library. The wrapper function silences the RuntimeError.

Further Reading

  • Python Speech Recognition with SpeechRecognition Library
  • Build Your Own AI Text Summarizer
  • Web Scraping with Python Selenium and Beautiful Soup 4
  • The Best Way to do Named Entity Recognition (NER)
  • How to Send an Email with an Attachment in Python

I run this site to help you and others like you find cool projects and practice software skills. If this is helpful for you and you enjoy your ad free site, please help fund this site by donating below! If you can’t donate right now, please think of us next time.

Make a one-time donation

Your contribution is appreciated.

Donate


Make a monthly donation

Your contribution is appreciated.

Donate monthly


Make a yearly donation

Your contribution is appreciated.

Donate yearly

Yujian Tang

Чтобы ответить на вопрос в том виде, в котором он был изначально задан, нет необходимости в close() работающего цикла, вы можете повторно использовать один и тот же цикл для всей программы.

Учитывая код в обновлении, ваш queue_manager может выглядеть так:

try:
    self.loop.run_until_complete(future)
except Exception as e:
    self.check_in_records()
    self.reconnect()
    self.start_job()

Отмена future не требуется и, насколько я могу судить, не имеет никакого эффекта. Это отличается от упомянутый ответ, который специально реагирует на KeyboardInterrupt, особенно потому, что он вызывается самим asyncio. KeyboardInterrupt может распространяться с помощью run_until_complete без фактического завершения будущего. Правильная обработка Ctrl-C в asyncio очень сложна или даже невозможна (подробности см. В здесь), но, к счастью, вопрос вовсе не в Ctrl-C, а в исключениях, вызываемых сопрограммой. (Обратите внимание, что KeyboardInterrupt не наследуется от Exception, поэтому в случае Ctrl-C тело except даже не будет выполнено.)

I was canceling the future because in this instance there are remaining tasks pending and i want to essentially remove those tasks and start a fresh event loop.

Это правильно, но код в (обновленном) вопросе отменяет только одно будущее, уже переданное в run_until_complete. Напомним, что future — это заполнитель для значения результата, которое будет предоставлено позже. Как только значение предоставлено, его можно получить, позвонив в future.result(). Если «значение» будущего является исключением, future.result() вызовет это исключение. У run_until_complete есть контракт, согласно которому он будет запускать цикл событий столько, сколько потребуется для данного future-объекта для получения значения, а затем он вернет это значение. Если «значение» на самом деле является исключением для повышения, то run_until_complete повторно повысит его. Например:

loop = asyncio.get_event_loop()
fut = loop.create_future()
loop.call_soon(fut.set_exception, ZeroDivisionError)
# raises ZeroDivisionError, as that is the future's result,
# manually set
loop.run_until_complete(fut)

Когда рассматриваемое будущее на самом деле представляет собой Task, объект, специфичный для asyncio, который обертывает сопрограмму в Future, результатом такого будущего является объект, возвращаемый сопрограммой. Если сопрограмма вызывает исключение, то получение результата вызовет его повторно, как и run_until_complete:

async def fail():
    1/0

loop = asyncio.get_event_loop()
fut = loop.create_task(fail())
# raises ZeroDivisionError, as that is the future's result,
# because the coroutine raises it
loop.run_until_complete(fut)

При работе с задачей завершение run_until_complete означает, что сопрограмма также завершила свою работу, вернув значение или вызвав исключение, что определяется возвратом или возбуждением run_until_complete.

С другой стороны, отмена задачи работает за счет того, что задача возобновляется, а выражение await, которое приостановило ее, поднимает CancelledError. Если задача специально не улавливает и не подавляет это исключение (чего не должен делать корректный асинхронный код), задача прекратит выполнение, и ее результатом станет CancelledError. Однако, если сопрограмма уже завершена, когда вызывается cancel(), тогда cancel() не может ничего сделать, потому что нет ожидающих await, в которые можно было бы внедрить CancelledError.

Понравилась статья? Поделить с друзьями:
  • Runtime error c download
  • Runtime error at98 37
  • Runtime error at38446
  • Runtime error at348 4360
  • Runtime error at228 3826 out of string range