Commit 19aeb439 authored by R David Murray's avatar R David Murray

Issue #17435: Don't use mutable default values in Timer.

Patch by Denver Coneybeare with some test modifications by me.
parent 8028a5cf
...@@ -839,10 +839,12 @@ For example:: ...@@ -839,10 +839,12 @@ For example::
t.start() # after 30 seconds, "hello, world" will be printed t.start() # after 30 seconds, "hello, world" will be printed
.. class:: Timer(interval, function, args=[], kwargs={}) .. class:: Timer(interval, function, args=None, kwargs=None)
Create a timer that will run *function* with arguments *args* and keyword Create a timer that will run *function* with arguments *args* and keyword
arguments *kwargs*, after *interval* seconds have passed. arguments *kwargs*, after *interval* seconds have passed.
If *args* is None (the default) then an empty list will be used.
If *kwargs* is None (the default) then an empty dict will be used.
.. versionchanged:: 3.3 .. versionchanged:: 3.3
changed from a factory function to a class. changed from a factory function to a class.
......
...@@ -786,6 +786,32 @@ class ThreadingExceptionTests(BaseTestCase): ...@@ -786,6 +786,32 @@ class ThreadingExceptionTests(BaseTestCase):
self.assertEqual(p.returncode, 0, "Unexpected error: " + stderr.decode()) self.assertEqual(p.returncode, 0, "Unexpected error: " + stderr.decode())
self.assertEqual(data, expected_output) self.assertEqual(data, expected_output)
class TimerTests(BaseTestCase):
def setUp(self):
BaseTestCase.setUp(self)
self.callback_args = []
self.callback_event = threading.Event()
def test_init_immutable_default_args(self):
# Issue 17435: constructor defaults were mutable objects, they could be
# mutated via the object attributes and affect other Timer objects.
timer1 = threading.Timer(0.01, self._callback_spy)
timer1.start()
self.callback_event.wait()
timer1.args.append("blah")
timer1.kwargs["foo"] = "bar"
self.callback_event.clear()
timer2 = threading.Timer(0.01, self._callback_spy)
timer2.start()
self.callback_event.wait()
self.assertEqual(len(self.callback_args), 2)
self.assertEqual(self.callback_args, [((), {}), ((), {})])
def _callback_spy(self, *args, **kwargs):
self.callback_args.append((args[:], kwargs.copy()))
self.callback_event.set()
class LockTests(lock_tests.LockTests): class LockTests(lock_tests.LockTests):
locktype = staticmethod(threading.Lock) locktype = staticmethod(threading.Lock)
...@@ -815,16 +841,5 @@ class BoundedSemaphoreTests(lock_tests.BoundedSemaphoreTests): ...@@ -815,16 +841,5 @@ class BoundedSemaphoreTests(lock_tests.BoundedSemaphoreTests):
class BarrierTests(lock_tests.BarrierTests): class BarrierTests(lock_tests.BarrierTests):
barriertype = staticmethod(threading.Barrier) barriertype = staticmethod(threading.Barrier)
def test_main():
test.support.run_unittest(LockTests, PyRLockTests, CRLockTests, EventTests,
ConditionAsRLockTests, ConditionTests,
SemaphoreTests, BoundedSemaphoreTests,
ThreadTests,
ThreadJoinOnShutdown,
ThreadingExceptionTests,
BarrierTests,
)
if __name__ == "__main__": if __name__ == "__main__":
test_main() unittest.main()
...@@ -802,17 +802,17 @@ class Thread: ...@@ -802,17 +802,17 @@ class Thread:
class Timer(Thread): class Timer(Thread):
"""Call a function after a specified number of seconds: """Call a function after a specified number of seconds:
t = Timer(30.0, f, args=[], kwargs={}) t = Timer(30.0, f, args=None, kwargs=None)
t.start() t.start()
t.cancel() # stop the timer's action if it's still waiting t.cancel() # stop the timer's action if it's still waiting
""" """
def __init__(self, interval, function, args=[], kwargs={}): def __init__(self, interval, function, args=None, kwargs=None):
Thread.__init__(self) Thread.__init__(self)
self.interval = interval self.interval = interval
self.function = function self.function = function
self.args = args self.args = args if args is not None else []
self.kwargs = kwargs self.kwargs = kwargs if kwargs is not None else {}
self.finished = Event() self.finished = Event()
def cancel(self): def cancel(self):
......
...@@ -15,6 +15,9 @@ Core and Builtins ...@@ -15,6 +15,9 @@ Core and Builtins
Library Library
------- -------
- Issue #17435: threading.Timer's __init__ method no longer uses mutable
default values for the args and kwargs parameters.
- Issue #17526: fix an IndexError raised while passing code without filename to - Issue #17526: fix an IndexError raised while passing code without filename to
inspect.findsource(). Initial patch by Tyler Doyle. inspect.findsource(). Initial patch by Tyler Doyle.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment