I suspect that you're seeing a combination of the real traceback and a
saved traceback due to the interrupted event handler. KeyboardInterrupt is
a "BaseException" and those are generally ignored by the asyncio library
(maybe we can do something better?).

I would recommend using the signal handler on UNIX and the other approach
on Windows (the add_signal_handler() call raises ValueError on Windows).
Signal handling on Windows is a horrible emulation in the C runtime and
does not have the same semantics as on UNIX, that's why we couldn't
implement add_signal_handler() there.


On Mon, Mar 31, 2014 at 6:16 AM, Florian Rüchel
<[email protected]>wrote:

> So I have already posted some mails to this list and here is one more.
> This time it is about cleanly exiting the event loop. The problem is pretty
> simple: I run my event loop using run_forever and it really does run
> forever, in an infinite loop until the user requests a stop. The stop is
> generally requested by hitting ^C, i.e. raising a KeyboardInterrupt.
> Contrary to a single-threaded application, an event loop that runs
> everything will always receive the KeyboardInterrupt exception (unless
> caught by a task/coroutine). Because of that I literally didn't include any
> handling inside the running loop for it. Instead I have code like this:
>
> try:
>     loop.run_forever()
> except KeyboardInterrupt:
>     pass
> finally:
>     loop.run_until_complete(shutdown())
>
> Where shutdown would be a coroutine that handles cleaning up all
> still-running tasks by cancelling them (I fact there is a single task that
> is cancelled and cancels all tasks it started in turn). This way, here is
> the only place where I catch a KeyboardInterrupt since I assumed it would
> travel up the stack without anything catching it. And that certainly seems
> to be the case: Hitting ^C actually exits the loop and starts the shutdown
> coroutine. But here is where it gets weird: The shutdown fails because a
> KeyboardInterrupt is raised, even though I only sent one and already caught
> that! And even more weird: The stack trace actually looks like one loop was
> running inside the other:
>
> Traceback (most recent call last):
>   File "/home/javex/mypkg/__init__.py", line 261, in run
>     event_loop.run_until_complete(shutdown())
>   File
> "/home/javex/.virtualenvs/misc/lib/python3.3/site-packages/asyncio/base_events.py",
> line 203, in run_until_complete
>     self.run_forever()
>   ...
>   File
> "/home/javex/.virtualenvs/misc/lib/python3.3/site-packages/asyncio/futures.py",
> line 243, in result
>     raise self._exception
>   File "/home/javex/mypkg/__init__.py", line 245, in run
>     asyncio.get_event_loop().run_forever()
>   ...
>   File
> "/home/javex/.virtualenvs/misc/lib/python3.3/site-packages/asyncio/unix_events.py",
> line 487, in _start
>     universal_newlines=False, bufsize=bufsize, **kwargs)
>   File "/usr/lib64/python3.3/subprocess.py", line 819, in __init__
>     restore_signals, start_new_session)
>   File "/usr/lib64/python3.3/subprocess.py", line 1409, in _execute_child
>     part = _eintr_retry_call(os.read, errpipe_read, 50000)
>   File "/usr/lib64/python3.3/subprocess.py", line 479, in _eintr_retry_call
>     return func(*args)
> KeyboardInterrupt
> Full Trace <http://pastebin.com/jGppikdZ>
>
> If you look at the stack trace, you can see that we are inside the
> run_until_complete part (above inside the finally block) and that it in
> turn winds up inside the run_forever part from above. Of course the bottom
> part of the stack trace always varies as it depends on where the function
> is currently working. However, the part above is always the same: It looks
> like one loop runs inside the other.
>
> So here I am stuck. I don't know how to handle this cleanly. Previously I
> did try a solution similar to the one described in Detect exceptions not
> consumed<https://docs.python.org/3.4/library/asyncio-dev.html#detect-exceptions-not-consumed>
>  where
> I forced myself to wrap any task in it. That worked but it was error prone
> and felt dirty. Instead, I now handle exception for each task individually
> which means I can react to them much better. However, additionally to
> handling normal exceptions, I handled a KeyboardInterrupt here, calling
> loop.stop(). However, that would only trigger when inside one of those
> tasks, not when running polling of the loop. So I had to handle this
> additionally similar to the above try-except-finally block which
> essentially meant having two different code paths handling the same thing
> while it feels like there should be a central point.
>
> After reading the PEP; this list and searching for a solution, I could not
> find anything on it. So here it is: How do I exit a running loop cleanly by
> sending a KeyboardInterrupt? Note that I am aware of the Example: Set
> signal handlers for SIGINT and 
> SIGTERM<https://docs.python.org/3.4/library/asyncio-eventloop.html#example-set-signal-handlers-for-sigint-and-sigterm>
>  but
> that is Unix specific and my application should also run on Windows in the
> future. Otherwise this would be the perfect solution (btw: Why isn't
> Windows supported? From the 
> signal<https://docs.python.org/3.4/library/signal.html#signal.signal> 
> documentation
> it seems that some signals (including SIGINT) can be caught on Windows as
> well.).
>
> Thanks in advance for any suggestion on how to acheive the desired
> behavior.
> Regards,
> Florian
>



-- 
--Guido van Rossum (python.org/~guido)

Reply via email to