A Python repeatable threading.Timer class
I had a function that I wanted to be called repeatedly every x seconds but couldn't find a nice way of doing this. The python threading module has a Timer class which will wait for the given number of seconds and then execute the passed in callable object. Unfortunately, once it has executed that code it is no longer useable. So based upon that class I whipped up the following RepeatableTimer. It's not "hard real-time" or anything since it doesn't account for the time taken to execute the given function, but it's good enough for what I needed. Feel free to use it however you see fit as I'm distributing it under the MIT license.
Usage:
Create a new RepeatTimer object passing it required arguments and any optional ones.
Required Arguments:
- interval -- floating point number specifying the number of seconds to wait before executing function
- function -- the function (or callable object) to be executed
Optional Arguments:
- iterations -- integer specifying the number of iterations to perform
- args -- list of positional arguments passed to function
- kwargs -- dictionary of keyword arguments passed to function
Examples:
Execute the hello function every 5 seconds:
def hello(): print "Hello World!" r = RepeatTimer(5.0, hello) r.start() |
Execute the hello function every 5 seconds but terminate when it has executed 10 times:
def hello(): print "Hello World!" r = RepeatTimer(5.0, hello, 10) r.start() |
# Copyright (c) 2009 Geoffrey Foster # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation # files (the "Software"), to deal in the Software without # restriction, including without limitation the rights to use, # copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following # conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. from threading import Event, Thread class RepeatTimer(Thread): def __init__(self, interval, function, iterations=0, args=[], kwargs={}): Thread.__init__(self) self.interval = interval self.function = function self.iterations = iterations self.args = args self.kwargs = kwargs self.finished = Event() def run(self): count = 0 while not self.finished.is_set() and (self.iterations <= 0 or count < self.iterations): self.finished.wait(self.interval) if not self.finished.is_set(): self.function(*self.args, **self.kwargs) count += 1 def cancel(self): self.finished.set() |
Updated Oct 29, 2009: Removed extra unneeded Event object.




June 2nd, 2009 - 15:45
Code doesnt work for me unless I change .is_set for isSet
def run(self):
count = 0
while not self.finished.isSet() and (self.iterations <= 0 or count < self.iterations):
self.event.wait(self.interval)
if not self.finished.isSet() and not self.event.isSet():
self.function(*self.args, **self.kwargs)
count += 1
June 9th, 2009 - 00:32
What version of python are you running? The code was written with 2.6 in mind which “provides PEP 8 compliant aliases and properties to replace the camelCase names that were inspired by Java’s threading API”. Previous versions will not have the is_set method (although isSet will do the exact same thing).
July 28th, 2009 - 13:39
Hi. Maybe this is a newbe question but I’m trying to use this timer within a class.
I have a function which accepts no parameters and the timer I initialized within the body of the class. A second function (a button press event) successfully starts the timer, but when it comes time to run the function, I get an error. Here’s the code:
def Log_In(self):
print “Loggin in!”
myTimer = repeattimer.RepeatTimer(5, Log_In)
here’s the error:
Exception in thread Thread-1:
Traceback (most recent call last):
File “C:\Python26\lib\threading.py”, line 525, in __bootstrap_inner
self.run()
File “C:\Python26\myPY\JCT Dorms\repeattimer.py”, line 43, in run
self.function(*self.args, **self.kwargs)
TypeError: Log_In() takes exactly 1 argument (0 given)
running windows vista, Python 2.6.2
Thank you!
July 28th, 2009 - 13:47
Your function Log_In is taking 1 argument (self), which is unnecessary because it’s a function on its own and not a method (where a method would be a function that is called on an object which is an instance of a class).
Try:
def Log_In():
print "Loggin in!"
for your function
October 28th, 2009 - 18:28
Hi, thanks for putting this up. I’m wondering why you have used two Event objects, self.event and self.finished. It seems to me that only one should be needed, and I was able to have it work with just one.
But I’m fairly new to Python, and if there is a reason why you used two separate Event objects, could you mention why?
Thanks!
October 29th, 2009 - 19:11
I can’t actually remember why I had the two Event objects but it would seem that you’re right at the extra one is unneeded. I’ve updated the code to reflect this. Thanks!
November 7th, 2009 - 21:17
When I’m running this code on MacOSX10.6 with Python 2.6.1 I can’t break script execution by pressing ctrl+c in terminal.
Is there any way to make this work?
November 10th, 2009 - 20:20
You can break from a script using Ctrl+C (sends and EOF). Ctrl+C is for sending a KeyboardInterrupt.
You can also cancel the execution of the timer by entering the command (for the above example script) r.cancel() and then exit normally.
The interaction of KeyboardInterrupt and multi-threaded python is mentioned in the python docs, particularly:
As mentioned in http://docs.python.org/library/thread.html
January 27th, 2010 - 16:33
I don’t understand why I can’t run your timer… I use it just to display a message every minute. The first time it run fine… but after that, an error occur and I don’t know why.
File “/usr/lib/python2.6/threading.py”, line 525, in __bootstrap_inner
self.run()
File “./radio.py”, line 308, in run
self.function(*self.args, **self.kwargs)
TypeError: ‘NoneType’ object is not callable
That’s the method :
def summaryRefresh (self, message, startIt) :
self.waiting_seconds = self.waiting_seconds – 60
endIt = self.textBuffer.get_end_iter()
self.textBuffer.delete(startIt, endIt)
if (self.waiting_seconds < 60) :
self.textBuffer.insert(startIt, "* Starting in a few moments…\n")
else :
message_displayed = message % (int(self.waiting_seconds / 60))
self.textBuffer.insert(startIt, message_displayed)
Could you help me please?
February 5th, 2010 - 10:40
This is exactly what I need for my wallpaper script, except I’d like to be able to start and stop it. I saw in your last post you you can use r.cancel() to stop it, but you can’t just restart with r.start() since it gives the error “RuntimeError: thread already started”.
Is there a simple way to restart the timer after it’s been stopped?
February 6th, 2010 - 14:51
I keep getting this error:
Traceback (most recent call last):
File “MentionPoll.py”, line 51, in
initTwitterPolling()
File “MentionPoll.py”, line 47, in initTwitterPolling
r = RepeatTimer(10.0, sinceIDReply, args=since_id)
TypeError: ‘module’ object is not callable
Any ideas what it could be?
February 6th, 2010 - 18:06
No, like all python threads once it has been stopped it is no longer valid and can’t be restarted. You could of course add something into the run method that would check a “paused” boolean value, which, if true, could then cause the thread to sleep for a certain period of time
February 6th, 2010 - 18:10
Based upon the error and the snippet of code (without seeing more of it) I’d have to guess that sinceIDReply is a module and not a function or object (the object must implement the __call__ method for it to be callable)
February 17th, 2010 - 12:31
Hi, I have used the code with python 2.5 and it works ok (with the change of IsSet) but when I generate an executable with pyexe, and I execute the application I get an error like this:
Unhandled exception in thread started by <bound method RepeatTimer.__bootstrap of >
Traceback (most recent call last):
File “threading.pyc”, line 462, in __bootstrap
File “threading.pyc”, line 527, in __bootstrap_inner
AttributeError: ‘NoneType’ object has no attribute ‘acquire’
Can anybody help me?
May 5th, 2010 - 20:32
Wow, thank you so much. This is exactly what I needed for my gps/nmea logger. I’m not a programmer and it took me all day just to get those 15 lines working. I thought it would take me another two to get threading sorted.
Thanks again