Skip to content

Instantly share code, notes, and snippets.

@waveform80
Created November 10, 2020 19:54
Show Gist options
  • Save waveform80/1be2a233bc1f20dbff0ef2be10ee2f40 to your computer and use it in GitHub Desktop.
Save waveform80/1be2a233bc1f20dbff0ef2be10ee2f40 to your computer and use it in GitHub Desktop.

ZMQEventLoop / SelectEventLoop "hang"

The following scripts can be used to reproduce the ZMQEventLoop (and the default SelectEventLoop) in urwid "hanging" when watched queues (or file descriptors) are ready but aren't serviced by their respective handlers.

The zmq_send.py and zmq_recv_1.py scripts can be run together to demonstrate a simple urwid application that receives messages from a SUB socket and displays them next to a clock. The zmq_recv_2.py script demonstrates what happens when the SUB socket is "ready" but isn't cleared by the handler; it's simply called repeatedly in preference to anything else. The display doesn't update unless draw_screen() is explicitly called because by default this is only done in the idle handler, which never fires as long as one queue or file descriptor is "ready".

Likewise sel_send.py, sel_recv_1.py, and sel_recv_2.py demonstrate the same thing but with the default SelectEventLoop (unsurprisingly given I modelled ZMQEventLoop.loop pretty closely on SelectEventLoop.loop). Please note in the case of the socket examples, the receiving end must be started first (ZMQ doesn't care).

import os
import datetime as dt
import urwid
from socket import *
class Application:
def build_ui(self):
self.clock = urwid.Text(('bold', ' 1970-01-01 00:00:00 '), align='center')
self.messages = urwid.Text(('default', ' Messages '), align='center')
palette = [
('default', 'black', 'white'),
('bold', 'bold,black', 'white'),
('bg', 'black', 'dark blue'),
]
return urwid.AttrMap(
urwid.Filler(
urwid.Pile([
self.clock,
urwid.Divider(),
self.messages,
])
), 'bg'), palette
def update_clock(self):
self.clock.set_text(('bold', dt.datetime.now().strftime(' %Y-%m-%d %H:%M:%S ')))
self.event_loop.alarm(1, self.update_clock)
def update_messages(self):
self.messages.set_text(('default', self.sock.recvmsg(512)[0].decode('utf-8')))
def __call__(self):
self.sock = socket(AF_UNIX, SOCK_DGRAM)
try:
try:
os.unlink('/tmp/s')
except FileNotFoundError:
pass
self.sock.bind('/tmp/s')
self.count = 0
self.event_loop = urwid.SelectEventLoop()
self.event_loop.alarm(1, self.update_clock)
self.event_loop.watch_file(self.sock, self.update_messages)
top, palette = self.build_ui()
self.main_loop = urwid.MainLoop(top, palette, event_loop=self.event_loop)
self.main_loop.run()
finally:
self.sock.close()
if __name__ == '__main__':
main = Application()
main()
import os
import datetime as dt
import urwid
from socket import *
class Application:
def build_ui(self):
self.clock = urwid.Text(('bold', ' 1970-01-01 00:00:00 '), align='center')
self.messages = urwid.Text(('default', ' Messages '), align='center')
palette = [
('default', 'black', 'white'),
('bold', 'bold,black', 'white'),
('bg', 'black', 'dark blue'),
]
return urwid.AttrMap(
urwid.Filler(
urwid.Pile([
self.clock,
urwid.Divider(),
self.messages,
])
), 'bg'), palette
def update_clock(self):
self.clock.set_text(('bold', dt.datetime.now().strftime(' %Y-%m-%d %H:%M:%S ')))
self.event_loop.alarm(1, self.update_clock)
def update_messages(self):
self.messages.set_text(('default', ' %s ' % self.count))
self.count += 1
self.main_loop.draw_screen()
def __call__(self):
self.sock = socket(AF_UNIX, SOCK_DGRAM)
try:
try:
os.unlink('/tmp/s')
except FileNotFoundError:
pass
self.sock.bind('/tmp/s')
self.count = 0
self.event_loop = urwid.SelectEventLoop()
self.event_loop.alarm(1, self.update_clock)
self.event_loop.watch_file(self.sock, self.update_messages)
top, palette = self.build_ui()
self.main_loop = urwid.MainLoop(top, palette, event_loop=self.event_loop)
self.main_loop.run()
finally:
self.sock.close()
if __name__ == '__main__':
main = Application()
main()
from socket import *
sock = socket(AF_UNIX, SOCK_DGRAM)
sock.connect('/tmp/s')
try:
while True:
sock.sendmsg([input().encode('utf-8')])
finally:
sock.close()
import datetime as dt
import urwid
import zmq
class Application:
def build_ui(self):
self.clock = urwid.Text(('bold', ' 1970-01-01 00:00:00 '), align='center')
self.messages = urwid.Text(('default', ' Messages '), align='center')
palette = [
('default', 'black', 'white'),
('bold', 'bold,black', 'white'),
('bg', 'black', 'dark blue'),
]
return urwid.AttrMap(
urwid.Filler(
urwid.Pile([
self.clock,
urwid.Divider(),
self.messages,
])
), 'bg'), palette
def update_clock(self):
self.clock.set_text(('bold', dt.datetime.now().strftime(' %Y-%m-%d %H:%M:%S ')))
self.event_loop.alarm(1, self.update_clock)
def update_messages(self):
self.messages.set_text(('default', self.sock.recv_string()))
def __call__(self):
ctx = zmq.Context()
self.sock = ctx.socket(zmq.SUB)
try:
self.sock.connect('ipc:///tmp/q')
self.sock.setsockopt(zmq.SUBSCRIBE, b'')
self.event_loop = urwid.ZMQEventLoop()
self.event_loop.alarm(1, self.update_clock)
self.event_loop.watch_queue(self.sock, self.update_messages)
top, palette = self.build_ui()
self.main_loop = urwid.MainLoop(top, palette, event_loop=self.event_loop)
self.main_loop.run()
finally:
self.sock.close()
ctx.term()
if __name__ == '__main__':
main = Application()
main()
import datetime as dt
import urwid
import zmq
class Application:
def build_ui(self):
self.clock = urwid.Text(('bold', ' 1970-01-01 00:00:00 '), align='center')
self.messages = urwid.Text(('default', ' Messages '), align='center')
palette = [
('default', 'black', 'white'),
('bold', 'bold,black', 'white'),
('bg', 'black', 'dark blue'),
]
return urwid.AttrMap(
urwid.Filler(
urwid.Pile([
self.clock,
urwid.Divider(),
self.messages,
])
), 'bg'), palette
def update_clock(self):
self.clock.set_text(('bold', dt.datetime.now().strftime(' %Y-%m-%d %H:%M:%S ')))
self.event_loop.alarm(1, self.update_clock)
def update_messages(self):
self.messages.set_text(('default', ' %s ' % self.count))
self.count += 1
self.main_loop.draw_screen()
def __call__(self):
ctx = zmq.Context()
self.sock = ctx.socket(zmq.SUB)
try:
self.sock.connect('ipc:///tmp/q')
self.sock.setsockopt(zmq.SUBSCRIBE, b'')
self.count = 0
self.event_loop = urwid.ZMQEventLoop()
self.event_loop.alarm(1, self.update_clock)
self.event_loop.watch_queue(self.sock, self.update_messages)
top, palette = self.build_ui()
self.main_loop = urwid.MainLoop(top, palette, event_loop=self.event_loop)
self.main_loop.run()
finally:
self.sock.close()
ctx.term()
if __name__ == '__main__':
main = Application()
main()
import zmq
ctx = zmq.Context()
sock = ctx.socket(zmq.PUB)
sock.bind('ipc:///tmp/q')
try:
while True:
sock.send_string(input())
finally:
sock.close()
ctx.term()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment