mirror of
https://github.com/python/cpython.git
synced 2025-10-10 00:43:41 +00:00
Merge #17435: Don't use mutable default values in Timer.
Patch by Denver Coneybeare with some test modifications by me.
This commit is contained in:
commit
5cbf3a0d6e
4 changed files with 37 additions and 17 deletions
|
@ -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.
|
||||||
|
|
|
@ -787,6 +787,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)
|
||||||
|
|
||||||
|
@ -816,16 +842,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()
|
||||||
|
|
|
@ -807,17 +807,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):
|
||||||
|
|
|
@ -297,6 +297,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.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue