4

For some reason this program prints the following warning:

Task exception was never retrieved
future: <Task finished coro=<coro() done, defined at /usr/lib64/python3.4/asyncio/coroutines.py:139> exception=SystemExit(2,)>

even though the exception is clearly retrieved and propagated, as caught SystemExit! is printed to the terminal, and process status code becomes 2.

The same thing happens with Python 2 and trollius.

Am I missing something?

#!/usr/bin/env python3

import asyncio

@asyncio.coroutine
def comain():
    raise SystemExit(2)

def main():
    loop = asyncio.get_event_loop()
    task = loop.create_task(comain())
    try:
        loop.run_until_complete(task)
    except SystemExit:
        print("caught SystemExit!")
        raise
    finally:
        loop.close()

if __name__ == "__main__":
    main()
3
  • 1
    is there any reason not to use loop.run_until_complete(comain()) which works as expected? Commented Dec 23, 2015 at 19:12
  • I need a task object because I call task.cancel() in a KeyboardInterrupt handler (not shown in this snippet). Commented Dec 23, 2015 at 19:18
  • 2
    you could add if not task.cancelled(): task.result() into the finally block. Though I don't know whether it is the intended behavior that you have to call task.result() or task.exception() manually (I would expect run_until_complete() do it for you). Commented Dec 23, 2015 at 19:29

2 Answers 2

7

SystemExit seems to be a special case. If, for example, you raise and catch an Exception, you won't see any errors. The way around this seems to be to manually retrieve the exception using Task.exception():

import asyncio

@asyncio.coroutine
def comain():
    raise SystemExit(2)

def main():
    loop = asyncio.get_event_loop()
    task = loop.create_task(comain())
    try:
        loop.run_until_complete(task)
    except SystemExit:
        print("caught SystemExit!")
        task.exception()
        raise
    finally:
        loop.close()

if __name__ == "__main__":
    main()

EDIT

Actually, all BaseException subclasses will behave this way.

Sign up to request clarification or add additional context in comments.

Comments

0

I think it's because SystemExit and KeyboardInterrupt are different cases. Normal Exception won't cause the same issue for example.

You can chain the comain coroutine inside another to consume the exception like this:

#!/usr/bin/env python3

import asyncio

@asyncio.coroutine
def comain():
    raise SystemExit(2)

@asyncio.coroutine
def another():
    try:
        yield from comain()
    except SystemExit:
        print ("consumed")

def main():
    loop = asyncio.get_event_loop()
    task = loop.create_task(another())
    try:
        loop.run_until_complete(task)
    except SystemExit:
        print("caught SystemExit!")
        raise
    finally:
        loop.close()

if __name__ == "__main__":
    main()

Here - I am calling another() instead of comain() and inside another() I am handling the exception from comain().

1 Comment

My intention is to let this exception escape to the top level, so the interpreter will be terminated.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.