TwistedEventLoop modified to properly stop the reactor. urwid 0.9.9 and 0.9.9.1 crash the reactor on ExitMainLoop instead of stopping it. One obvious way this breaks is if anything used the reactor's thread pool: that thread pool is not shut down if the reactor is no
| 98 | if urwid.VERSION < (1, 0, 0) and hasattr(urwid, "TwistedEventLoop"): |
| 99 | |
| 100 | class TwistedEventLoop(urwid.TwistedEventLoop): |
| 101 | """TwistedEventLoop modified to properly stop the reactor. |
| 102 | |
| 103 | urwid 0.9.9 and 0.9.9.1 crash the reactor on ExitMainLoop instead |
| 104 | of stopping it. One obvious way this breaks is if anything used |
| 105 | the reactor's thread pool: that thread pool is not shut down if |
| 106 | the reactor is not stopped, which means python hangs on exit |
| 107 | (joining the non-daemon threadpool threads that never exit). And |
| 108 | the default resolver is the ThreadedResolver, so if we looked up |
| 109 | any names we hang on exit. That is bad enough that we hack up |
| 110 | urwid a bit here to exit properly. |
| 111 | """ |
| 112 | |
| 113 | def handle_exit(self, f): |
| 114 | def wrapper(*args, **kwargs): |
| 115 | try: |
| 116 | return f(*args, **kwargs) |
| 117 | except urwid.ExitMainLoop: |
| 118 | # This is our change. |
| 119 | self.reactor.stop() |
| 120 | except: |
| 121 | # This is the same as in urwid. |
| 122 | # We are obviously not supposed to ever hit this. |
| 123 | print(sys.exc_info()) |
| 124 | self._exc_info = sys.exc_info() |
| 125 | self.reactor.crash() |
| 126 | |
| 127 | return wrapper |
| 128 | |
| 129 | else: |
| 130 | TwistedEventLoop = getattr(urwid, "TwistedEventLoop", None) |