summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Ward <ian@excess.org>2014-11-23 10:19:48 -0500
committerIan Ward <ian@excess.org>2014-11-23 10:19:48 -0500
commit1f430acd00dc5732298684e420e1fac84b71a1b0 (patch)
tree11744d89f11fb25e6a3c6a5ac1bd4a8f4b3791a1
parentb851b905cb8c6c46a1757d0ccca2adbd02740ba7 (diff)
downloadurwid-remove-enter-idle.tar.gz
WIP: remove enter_idle from event loopsremove-enter-idle
-rwxr-xr-xurwid/main_loop.py252
1 files changed, 10 insertions, 242 deletions
diff --git a/urwid/main_loop.py b/urwid/main_loop.py
index 77022bf..5475a92 100755
--- a/urwid/main_loop.py
+++ b/urwid/main_loop.py
@@ -128,6 +128,8 @@ class MainLoop(object):
self.event_loop = event_loop
self._watch_pipes = {}
+ self._alarms = []
+ self._watch_files = {}
def _set_widget(self, widget):
self._widget = widget
@@ -583,8 +585,6 @@ class SelectEventLoop(object):
def __init__(self):
self._alarms = []
self._watch_files = {}
- self._idle_handle = 0
- self._idle_callbacks = {}
def alarm(self, seconds, callback):
"""
@@ -637,42 +637,12 @@ class SelectEventLoop(object):
return True
return False
- def enter_idle(self, callback):
- """
- Add a callback for entering idle.
-
- Returns a handle that may be passed to remove_idle()
- """
- self._idle_handle += 1
- self._idle_callbacks[self._idle_handle] = callback
- return self._idle_handle
-
- def remove_enter_idle(self, handle):
- """
- Remove an idle callback.
-
- Returns True if the handle was removed.
- """
- try:
- del self._idle_callbacks[handle]
- except KeyError:
- return False
- return True
-
- def _entering_idle(self):
- """
- Call all the registered idle callbacks.
- """
- for callback in self._idle_callbacks.values():
- callback()
-
def run(self):
"""
Start the event loop. Exit the loop when any callback raises
an exception. If ExitMainLoop is raised, exit cleanly.
"""
try:
- self._did_something = True
while True:
try:
self._loop()
@@ -688,32 +658,21 @@ class SelectEventLoop(object):
A single iteration of the event loop
"""
fds = self._watch_files.keys()
- if self._alarms or self._did_something:
- if self._alarms:
- tm = self._alarms[0][0]
- timeout = max(0, tm - time.time())
- if self._did_something and (not self._alarms or
- (self._alarms and timeout > 0)):
- timeout = 0
- tm = 'idle'
+ if self._alarms:
+ tm = self._alarms[0][0]
+ timeout = max(0, tm - time.time())
ready, w, err = select.select(fds, [], fds, timeout)
else:
tm = None
ready, w, err = select.select(fds, [], fds)
- if not ready:
- if tm == 'idle':
- self._entering_idle()
- self._did_something = False
- elif tm is not None:
- # must have been a timeout
- tm, alarm_callback = self._alarms.pop(0)
- alarm_callback()
- self._did_something = True
+ if not ready and tm is not None:
+ # must have been a timeout
+ tm, alarm_callback = self._alarms.pop(0)
+ alarm_callback()
for fd in ready:
self._watch_files[fd]()
- self._did_something = True
class GLibEventLoop(object):
@@ -726,12 +685,8 @@ class GLibEventLoop(object):
self.GLib = GLib
self._alarms = []
self._watch_files = {}
- self._idle_handle = 0
- self._glib_idle_enabled = False # have we called glib.idle_add?
- self._idle_callbacks = {}
self._loop = GLib.MainLoop()
self._exc_info = None
- self._enable_glib_idle()
def alarm(self, seconds, callback):
"""
@@ -746,7 +701,6 @@ class GLibEventLoop(object):
@self.handle_exit
def ret_false():
callback()
- self._enable_glib_idle()
return False
fd = self.GLib.timeout_add(int(seconds*1000), ret_false)
self._alarms.append(fd)
@@ -778,7 +732,6 @@ class GLibEventLoop(object):
@self.handle_exit
def io_callback(source, cb_condition):
callback()
- self._enable_glib_idle()
return True
self._watch_files[fd] = \
self.GLib.io_add_watch(fd,self.GLib.IO_IN,io_callback)
@@ -796,40 +749,6 @@ class GLibEventLoop(object):
return True
return False
- def enter_idle(self, callback):
- """
- Add a callback for entering idle.
-
- Returns a handle that may be passed to remove_enter_idle()
- """
- self._idle_handle += 1
- self._idle_callbacks[self._idle_handle] = callback
- return self._idle_handle
-
- def _enable_glib_idle(self):
- if self._glib_idle_enabled:
- return
- self.GLib.idle_add(self._glib_idle_callback)
- self._glib_idle_enabled = True
-
- def _glib_idle_callback(self):
- for callback in self._idle_callbacks.values():
- callback()
- self._glib_idle_enabled = False
- return False # ask glib not to call again (or we would be called
-
- def remove_enter_idle(self, handle):
- """
- Remove an idle callback.
-
- Returns True if the handle was removed.
- """
- try:
- del self._idle_callbacks[handle]
- except KeyError:
- return False
- return True
-
def run(self):
"""
Start the event loop. Exit the loop when any callback raises
@@ -873,69 +792,12 @@ class TornadoEventLoop(object):
""" This is an Urwid-specific event loop to plug into its MainLoop.
It acts as an adaptor for Tornado's IOLoop which does all
heavy lifting except idle-callbacks.
-
- Notice, since Tornado has no concept of idle callbacks we
- monkey patch ioloop._impl.poll() function to be able to detect
- potential idle periods.
"""
- _ioloop_registry = WeakKeyDictionary() # {<ioloop> : {<handle> : <idle_func>}}
- _max_idle_handle = 0
-
- class PollProxy(object):
- """ A simple proxy for a Python's poll object that wraps the .poll() method
- in order to detect idle periods and call Urwid callbacks
- """
- def __init__(self, poll_obj, idle_map):
- self.__poll_obj = poll_obj
- self.__idle_map = idle_map
- self._idle_done = False
- self._prev_timeout = 0
-
- def __getattr__(self, name):
- return getattr(self.__poll_obj, name)
-
- def poll(self, timeout):
- if timeout > self._prev_timeout:
- # if timeout increased we assume a timer event was handled
- self._idle_done = False
- self._prev_timeout = timeout
- start = time.time()
-
- # any IO pending wins
- events = self.__poll_obj.poll(0)
- if events:
- self._idle_done = False
- return events
-
- # our chance to enter idle
- if not self._idle_done:
- for callback in self.__idle_map.values():
- callback()
- self._idle_done = True
-
- # then complete the actual request (adjusting timeout)
- timeout = max(0, min(timeout, timeout + start - time.time()))
- events = self.__poll_obj.poll(timeout)
- if events:
- self._idle_done = False
- return events
-
- @classmethod
- def _patch_poll_impl(cls, ioloop):
- """ Wraps original poll object in the IOLoop's poll object
- """
- if ioloop in cls._ioloop_registry:
- return # we already patched this instance
-
- cls._ioloop_registry[ioloop] = idle_map = {}
- ioloop._impl = cls.PollProxy(ioloop._impl, idle_map)
-
def __init__(self, ioloop=None):
if not ioloop:
from tornado.ioloop import IOLoop
ioloop = IOLoop.instance()
self._ioloop = ioloop
- self._patch_poll_impl(ioloop)
self._pending_alarms = {}
self._watch_handles = {} # {<watch_handle> : <file_descriptor>}
self._max_watch_handle = 0
@@ -979,18 +841,6 @@ class TornadoEventLoop(object):
self._ioloop.remove_handler(fd)
return True
- def enter_idle(self, callback):
- self._max_idle_handle += 1
- handle = self._max_idle_handle
- idle_map = self._ioloop_registry[self._ioloop]
- idle_map[handle] = callback
- return handle
-
- def remove_enter_idle(self, handle):
- idle_map = self._ioloop_registry[self._ioloop]
- cb = idle_map.pop(handle, None)
- return cb is not None
-
def handle_exit(self, func):
@wraps(func)
def wrapper(*args, **kw):
@@ -1033,8 +883,6 @@ class TwistedEventLoop(object):
"""
Event loop based on Twisted_
"""
- _idle_emulation_delay = 1.0/256 # a short time (in seconds)
-
def __init__(self, reactor=None, manage_reactor=True):
"""
:param reactor: reactor to use
@@ -1061,12 +909,8 @@ class TwistedEventLoop(object):
self.reactor = reactor
self._alarms = []
self._watch_files = {}
- self._idle_handle = 0
- self._twisted_idle_enabled = False
- self._idle_callbacks = {}
self._exc_info = None
self.manage_reactor = manage_reactor
- self._enable_twisted_idle()
def alarm(self, seconds, callback):
"""
@@ -1124,49 +968,6 @@ class TwistedEventLoop(object):
return True
return False
- def enter_idle(self, callback):
- """
- Add a callback for entering idle.
-
- Returns a handle that may be passed to remove_enter_idle()
- """
- self._idle_handle += 1
- self._idle_callbacks[self._idle_handle] = callback
- return self._idle_handle
-
- def _enable_twisted_idle(self):
- """
- Twisted's reactors don't have an idle or enter-idle callback
- so the best we can do for now is to set a timer event in a very
- short time to approximate an enter-idle callback.
-
- .. WARNING::
- This will perform worse than the other event loops until we can find a
- fix or workaround
- """
- if self._twisted_idle_enabled:
- return
- self.reactor.callLater(self._idle_emulation_delay,
- self.handle_exit(self._twisted_idle_callback, enable_idle=False))
- self._twisted_idle_enabled = True
-
- def _twisted_idle_callback(self):
- for callback in self._idle_callbacks.values():
- callback()
- self._twisted_idle_enabled = False
-
- def remove_enter_idle(self, handle):
- """
- Remove an idle callback.
-
- Returns True if the handle was removed.
- """
- try:
- del self._idle_callbacks[handle]
- except KeyError:
- return False
- return True
-
def run(self):
"""
Start the event loop. Exit the loop when any callback raises
@@ -1181,7 +982,7 @@ class TwistedEventLoop(object):
self._exc_info = None
raise exc_info[0], exc_info[1], exc_info[2]
- def handle_exit(self, f, enable_idle=True):
+ def handle_exit(self, f):
"""
Decorator that cleanly exits the :class:`TwistedEventLoop` if
:class:`ExitMainLoop` is thrown inside of the wrapped function. Store the
@@ -1203,8 +1004,6 @@ class TwistedEventLoop(object):
self._exc_info = sys.exc_info()
if self.manage_reactor:
self.reactor.crash()
- if enable_idle:
- self._enable_twisted_idle()
return rval
return wrapper
@@ -1219,8 +1018,6 @@ class AsyncioEventLoop(object):
"""
_we_started_event_loop = False
- _idle_emulation_delay = 1.0/256 # a short time (in seconds)
-
def __init__(self, **kwargs):
if 'loop' in kwargs:
self._loop = kwargs.pop('loop')
@@ -1271,35 +1068,6 @@ class AsyncioEventLoop(object):
"""
return self._loop.remove_reader(handle)
- def enter_idle(self, callback):
- """
- Add a callback for entering idle.
-
- Returns a handle that may be passed to remove_idle()
- """
- # XXX there's no such thing as "idle" in most event loops; this fakes
- # it the same way as Twisted, by scheduling the callback to be called
- # repeatedly
- mutable_handle = [None]
- def faux_idle_callback():
- callback()
- mutable_handle[0] = self._loop.call_later(
- self._idle_emulation_delay, faux_idle_callback)
-
- mutable_handle[0] = self._loop.call_later(
- self._idle_emulation_delay, faux_idle_callback)
-
- return mutable_handle
-
- def remove_enter_idle(self, handle):
- """
- Remove an idle callback.
-
- Returns True if the handle was removed.
- """
- # `handle` is just a list containing the current actual handle
- return self.remove_alarm(handle[0])
-
_exc_info = None
def _exception_handler(self, loop, context):