Skip to content

Commit 7895315

Browse files
committed
Change in threading.Timer class: Continue periodical execution until action returns True.
1 parent f15fa87 commit 7895315

File tree

3 files changed

+36
-15
lines changed

3 files changed

+36
-15
lines changed

Doc/library/threading.rst

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -831,29 +831,41 @@ method. The :meth:`~Event.wait` method blocks until the flag is true.
831831
Timer Objects
832832
-------------
833833

834-
This class represents an action that should be run only after a certain amount
835-
of time has passed --- a timer. :class:`Timer` is a subclass of :class:`Thread`
834+
This class represents an action that should be run after a certain amount
835+
of time has passed --- a timer. It also can run periodically: each run happens
836+
after specified time that passed since previous run until the action returns True.
837+
:class:`Timer` is a subclass of :class:`Thread`
836838
and as such also functions as an example of creating custom threads.
837839

838840
Timers are started, as with threads, by calling their :meth:`~Timer.start`
839841
method. The timer can be stopped (before its action has begun) by calling the
840-
:meth:`~Timer.cancel` method. The interval the timer will wait before
841-
executing its action may not be exactly the same as the interval specified by
842-
the user.
842+
:meth:`~Timer.cancel` method. If action has returned True then next run is
843+
scheduled after the timer interval. The interval the timer will wait before
844+
executing its action may not be exactly the same as the interval specified
845+
by the user.
846+
843847

844848
For example::
845849

846-
def hello():
847-
print("hello, world")
850+
def star():
851+
global cnt
852+
print("*")
853+
cnt -= 1
854+
if cnt > 0:
855+
return True
856+
857+
cnt = 5
858+
t = Timer(1.0, star)
859+
t.start() # it prints five "*" with 1 sec waiting between prints
848860

849-
t = Timer(30.0, hello)
850-
t.start() # after 30 seconds, "hello, world" will be printed
861+
851862

852863

853864
.. class:: Timer(interval, function, args=None, kwargs=None)
854865

855866
Create a timer that will run *function* with arguments *args* and keyword
856-
arguments *kwargs*, after *interval* seconds have passed.
867+
arguments *kwargs*, after *interval* seconds have passed and continues
868+
periodically run *function* until *function* returns True.
857869
If *args* is ``None`` (the default) then an empty list will be used.
858870
If *kwargs* is ``None`` (the default) then an empty dict will be used.
859871

Lib/test/test_threading.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,7 @@ class TimerTests(BaseTestCase):
10671067
def setUp(self):
10681068
BaseTestCase.setUp(self)
10691069
self.callback_args = []
1070+
self.callback_cnt = 3
10701071
self.callback_event = threading.Event()
10711072

10721073
def test_init_immutable_default_args(self):
@@ -1081,13 +1082,20 @@ def test_init_immutable_default_args(self):
10811082
timer2 = threading.Timer(0.01, self._callback_spy)
10821083
timer2.start()
10831084
self.callback_event.wait()
1084-
self.assertEqual(len(self.callback_args), 2)
1085-
self.assertEqual(self.callback_args, [((), {}), ((), {})])
1085+
1086+
def test_continuous_execution(self):
1087+
timer = threading.Timer(0.01, self._callback_cont)
1088+
self.callback_event.wait(0.1)
1089+
self.assertEqual(self.callback_cnt, 0)
10861090

10871091
def _callback_spy(self, *args, **kwargs):
10881092
self.callback_args.append((args[:], kwargs.copy()))
10891093
self.callback_event.set()
10901094

1095+
def _callback_cont(self):
1096+
self.callback_cnt -= 1
1097+
return self.callback_cnt > 0
1098+
10911099
class LockTests(lock_tests.LockTests):
10921100
locktype = staticmethod(threading.Lock)
10931101

Lib/threading.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,9 +1177,10 @@ def cancel(self):
11771177
self.finished.set()
11781178

11791179
def run(self):
1180-
self.finished.wait(self.interval)
1181-
if not self.finished.is_set():
1182-
self.function(*self.args, **self.kwargs)
1180+
"""Continue execution after wait until function returns True"""
1181+
while(not self.finished.wait(self.interval)):
1182+
if not self.function(*self.args, **self.kwargs):
1183+
break
11831184
self.finished.set()
11841185

11851186
# Special thread class to represent the main thread

0 commit comments

Comments
 (0)