Stop pyzmq receiver by KeyboardInterrupt
Asked Answered
F

4

9

Following this example in the ØMQ docs, I'm trying to create a simple receiver. The example uses infinite loop. Everything works just fine. However, on MS Windows, when I hit CTRL+C to raise KeyboardInterrupt, the loop does not break. It seems that recv() method somehow ignores the exception. However, I'd love to exit the process by hiting CTRL+C instead of killing it. Is that possible?

Fourposter answered 18/6, 2013 at 16:19 Comment(0)
T
4

A zmq.Poller object seems to help:

def poll_socket(socket, timetick = 100):
    poller = zmq.Poller()
    poller.register(socket, zmq.POLLIN)
    # wait up to 100msec
    try:
        while True:
            obj = dict(poller.poll(timetick))
            if socket in obj and obj[socket] == zmq.POLLIN:
                yield socket.recv()
    except KeyboardInterrupt:
        pass
    # Escape while loop if there's a keyboard interrupt.

Then you can do things like:

for message in poll_socket(socket):
    handle_message(message)

and the for-loop will automatically terminate on Ctrl-C. It looks like the translation from Ctrl-C to a Python KeyboardInterrupt only happens when the interpreter is active and Python has not yielded control to low-level C code; the pyzmq recv() call apparently blocks while in low-level C code, so Python never gets a chance to issue the KeyboardInterrupt. But if you use zmq.Poller then it will stop at a timeout and give the interpreter a chance to issue the KeyboardInterrupt after the timeout is complete.

Trilbee answered 23/1, 2014 at 17:0 Comment(1)
As of today, I'm still sometimes forced to kill my applications instead of hitting CTRL+C -- very uncomfortable... I'm also working with zmq.Poller (thanks for the "for..in" pattern, btw.!), but there are situations where the Poller does not help: 1) using built-in devices (zmq.device()), 2) using REQ sockets in case that REP worker breaks. I discovered the following: zguide.zeromq.org/py:interrupt, but have found no use of it.Fourposter
F
17

In response to the @Cyclone's request, I suggest the following as a possible solution:

import signal
    
signal.signal(signal.SIGINT, signal.SIG_DFL)
# any pyzmq-related code, such as `reply = socket.recv()`
Fourposter answered 15/10, 2014 at 21:53 Comment(1)
Excelent. This worked for me (Windows 10). Haven't tried the accepted answer, but it seems you're also using this for your fix.Windflower
T
4

A zmq.Poller object seems to help:

def poll_socket(socket, timetick = 100):
    poller = zmq.Poller()
    poller.register(socket, zmq.POLLIN)
    # wait up to 100msec
    try:
        while True:
            obj = dict(poller.poll(timetick))
            if socket in obj and obj[socket] == zmq.POLLIN:
                yield socket.recv()
    except KeyboardInterrupt:
        pass
    # Escape while loop if there's a keyboard interrupt.

Then you can do things like:

for message in poll_socket(socket):
    handle_message(message)

and the for-loop will automatically terminate on Ctrl-C. It looks like the translation from Ctrl-C to a Python KeyboardInterrupt only happens when the interpreter is active and Python has not yielded control to low-level C code; the pyzmq recv() call apparently blocks while in low-level C code, so Python never gets a chance to issue the KeyboardInterrupt. But if you use zmq.Poller then it will stop at a timeout and give the interpreter a chance to issue the KeyboardInterrupt after the timeout is complete.

Trilbee answered 23/1, 2014 at 17:0 Comment(1)
As of today, I'm still sometimes forced to kill my applications instead of hitting CTRL+C -- very uncomfortable... I'm also working with zmq.Poller (thanks for the "for..in" pattern, btw.!), but there are situations where the Poller does not help: 1) using built-in devices (zmq.device()), 2) using REQ sockets in case that REP worker breaks. I discovered the following: zguide.zeromq.org/py:interrupt, but have found no use of it.Fourposter
S
2

Don't know if this going to work in Windows, but in Linux I did something like this:

if signal.signal(signal.SIGINT, signal.SIG_DFL):
    sys.exit()
Subheading answered 18/6, 2013 at 17:56 Comment(3)
This does not work for me exactly, sys.exit() is always called -- in the docs, I've found that signal.signal(...) only sets the signal handler. However, putting signal.signal(signal.SIGINT, signal.SIG_DFL); before the infinite loop solved my problem and it is now possible to terminate the receiver using CTRL+C! So thanks a lot for hint!Fourposter
@Tregoreg, you should post your comment as an answer. I'll upvote it... worked for me, and a lot cleaner than changing how my code handles request handling.Ampliate
@Ampliate Thanks for feedback, I did as you suggested.Fourposter
B
1

Try ctrl+break (as in the key above Page Up, I had to look it up, I don't think I've ever touched that key before) suggested near the bottom of this thread. I haven't done anything too fancy, but this seems to work well enough in the cases I've tried.

Bless answered 22/3, 2014 at 7:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.