M
Michele Simionato
I have always been curious about how people implement mainloops (or,
in Twisted terminology, reactors). So I sit down and I wrote the
following simple implementation:
import itertools
class SimpleReactor(object):
DELAY = 0.001 # seconds
def __init__(self):
self._event = {} # action id -> (scheduled time, action, args)
self._counter = itertools.count(1) # action id generator
self.running = False
def callLater(self, deltat, action, *args):
"""Schedule an action with arguments args in deltat seconds.
Return the action id"""
now = time.time()
i = self._counter.next()
self._event = now + deltat, action, args
return i
def cancelCallLater(self, action_id):
"Cancel the action identified by action_id"
del self._event[action_id]
def default_action(self): # to be overridden
"Invoked at each lap in the mainloop"
time.sleep(self.DELAY) # don't run too fast, rest a bit
def cleanup_action(self): # to be overridden
"Invoked at the end of the mainloop"
def manage_exc(self, e):
"Invoked at each call"
raise e
def dooneevent(self):
"Perfom scheduled actions"
now = time.time()
for i, (start_time, action, args) in self._event.items():
if now >= start_time: # it's time to start the action
self.cancelCallLater(i) # don't run it again
try:
action(*args)
except Exception, e:
self.manage_exc(e)
def run(self):
"Run the main loop"
self.running = True
try:
while self.running:
self.default_action()
self.dooneevent()
except KeyboardInterrupt:
print 'Stopped via CTRL-C'
finally:
self.cleanup_action()
def stop(self):
self.running = False
Notice that I copied the Twisted terminology, but
I did not look at Twisted implementation because I did not want to
use a select (I assume that the GUI mainloops do not use it either).
The trick I use is to store the actions to perform (which are
callables identified by an integer) in an event dictionary and
to run them in the mainlooop if the current time is greater than
the scheduled time.
I had to add a time.sleep(.001) call in the default_action to avoid
consuming 100%
of the CPU in the loop.
I wonder if real mainloops are done in this way and how bad/good is
this implementation compared to a serious one. Any suggestion/hint/
advice
is well appreciated. Thanks,
Michele Simionato
in Twisted terminology, reactors). So I sit down and I wrote the
following simple implementation:
import itertools
class SimpleReactor(object):
DELAY = 0.001 # seconds
def __init__(self):
self._event = {} # action id -> (scheduled time, action, args)
self._counter = itertools.count(1) # action id generator
self.running = False
def callLater(self, deltat, action, *args):
"""Schedule an action with arguments args in deltat seconds.
Return the action id"""
now = time.time()
i = self._counter.next()
self._event = now + deltat, action, args
return i
def cancelCallLater(self, action_id):
"Cancel the action identified by action_id"
del self._event[action_id]
def default_action(self): # to be overridden
"Invoked at each lap in the mainloop"
time.sleep(self.DELAY) # don't run too fast, rest a bit
def cleanup_action(self): # to be overridden
"Invoked at the end of the mainloop"
def manage_exc(self, e):
"Invoked at each call"
raise e
def dooneevent(self):
"Perfom scheduled actions"
now = time.time()
for i, (start_time, action, args) in self._event.items():
if now >= start_time: # it's time to start the action
self.cancelCallLater(i) # don't run it again
try:
action(*args)
except Exception, e:
self.manage_exc(e)
def run(self):
"Run the main loop"
self.running = True
try:
while self.running:
self.default_action()
self.dooneevent()
except KeyboardInterrupt:
print 'Stopped via CTRL-C'
finally:
self.cleanup_action()
def stop(self):
self.running = False
Notice that I copied the Twisted terminology, but
I did not look at Twisted implementation because I did not want to
use a select (I assume that the GUI mainloops do not use it either).
The trick I use is to store the actions to perform (which are
callables identified by an integer) in an event dictionary and
to run them in the mainlooop if the current time is greater than
the scheduled time.
I had to add a time.sleep(.001) call in the default_action to avoid
consuming 100%
of the CPU in the loop.
I wonder if real mainloops are done in this way and how bad/good is
this implementation compared to a serious one. Any suggestion/hint/
advice
is well appreciated. Thanks,
Michele Simionato