blob: 4092cf33d3da526a2021665cf250f4ab5b18d306 [file] [log] [blame]
Jim Fultond15dc062004-07-14 19:11:50 +00001import unittest
2from doctest import DocTestSuite
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003from test import support
Christian Heimes587c2bf2008-01-19 16:21:02 +00004import weakref
5import gc
6
Antoine Pitrouda991da2010-08-03 18:32:26 +00007# Modules under test
8_thread = support.import_module('_thread')
9threading = support.import_module('threading')
10import _threading_local
11
12
Christian Heimes587c2bf2008-01-19 16:21:02 +000013class Weak(object):
14 pass
15
16def target(local, weaklist):
17 weak = Weak()
18 local.weak = weak
19 weaklist.append(weakref.ref(weak))
20
Antoine Pitrouda991da2010-08-03 18:32:26 +000021
22class BaseLocalTest:
Christian Heimes587c2bf2008-01-19 16:21:02 +000023
24 def test_local_refs(self):
25 self._local_refs(20)
26 self._local_refs(50)
27 self._local_refs(100)
28
29 def _local_refs(self, n):
Antoine Pitrouda991da2010-08-03 18:32:26 +000030 local = self._local()
Christian Heimes587c2bf2008-01-19 16:21:02 +000031 weaklist = []
32 for i in range(n):
33 t = threading.Thread(target=target, args=(local, weaklist))
34 t.start()
35 t.join()
36 del t
37
38 gc.collect()
39 self.assertEqual(len(weaklist), n)
40
Antoine Pitrou5af4f4b2010-08-09 22:38:19 +000041 # XXX _threading_local keeps the local of the last stopped thread alive.
Christian Heimes587c2bf2008-01-19 16:21:02 +000042 deadlist = [weak for weak in weaklist if weak() is None]
Antoine Pitrou5af4f4b2010-08-09 22:38:19 +000043 self.assertIn(len(deadlist), (n-1, n))
Christian Heimes587c2bf2008-01-19 16:21:02 +000044
45 # Assignment to the same thread local frees it sometimes (!)
46 local.someothervar = None
47 gc.collect()
48 deadlist = [weak for weak in weaklist if weak() is None]
Benjamin Peterson577473f2010-01-19 00:09:57 +000049 self.assertIn(len(deadlist), (n-1, n), (n, len(deadlist)))
Jim Fultond15dc062004-07-14 19:11:50 +000050
Benjamin Peterson8a250ae2008-06-30 23:30:24 +000051 def test_derived(self):
52 # Issue 3088: if there is a threads switch inside the __init__
53 # of a threading.local derived class, the per-thread dictionary
54 # is created but not correctly set on the object.
55 # The first member set may be bogus.
56 import time
Antoine Pitrouda991da2010-08-03 18:32:26 +000057 class Local(self._local):
Benjamin Peterson8a250ae2008-06-30 23:30:24 +000058 def __init__(self):
59 time.sleep(0.01)
60 local = Local()
61
62 def f(i):
63 local.x = i
64 # Simply check that the variable is correctly set
65 self.assertEqual(local.x, i)
66
Serhiy Storchaka263dcd22015-04-01 13:01:14 +030067 with support.start_threads(threading.Thread(target=f, args=(i,))
68 for i in range(10)):
69 pass
Benjamin Peterson8a250ae2008-06-30 23:30:24 +000070
Philip Jenvey26713ca2009-09-29 04:57:18 +000071 def test_derived_cycle_dealloc(self):
72 # http://bugs.python.org/issue6990
Antoine Pitrouda991da2010-08-03 18:32:26 +000073 class Local(self._local):
Philip Jenvey26713ca2009-09-29 04:57:18 +000074 pass
75 locals = None
76 passed = False
77 e1 = threading.Event()
78 e2 = threading.Event()
79
80 def f():
81 nonlocal passed
82 # 1) Involve Local in a cycle
83 cycle = [Local()]
84 cycle.append(cycle)
85 cycle[0].foo = 'bar'
86
87 # 2) GC the cycle (triggers threadmodule.c::local_clear
88 # before local_dealloc)
89 del cycle
90 gc.collect()
91 e1.set()
92 e2.wait()
93
94 # 4) New Locals should be empty
95 passed = all(not hasattr(local, 'foo') for local in locals)
96
97 t = threading.Thread(target=f)
98 t.start()
99 e1.wait()
100
101 # 3) New Locals should recycle the original's address. Creating
102 # them in the thread overwrites the thread state and avoids the
103 # bug
104 locals = [Local() for i in range(10)]
105 e2.set()
106 t.join()
107
108 self.assertTrue(passed)
109
Jack Diederich561d5aa2010-02-22 19:55:46 +0000110 def test_arguments(self):
111 # Issue 1522237
Antoine Pitrouda991da2010-08-03 18:32:26 +0000112 class MyLocal(self._local):
113 def __init__(self, *args, **kwargs):
114 pass
Jack Diederich561d5aa2010-02-22 19:55:46 +0000115
Antoine Pitrouda991da2010-08-03 18:32:26 +0000116 MyLocal(a=1)
117 MyLocal(1)
118 self.assertRaises(TypeError, self._local, a=1)
119 self.assertRaises(TypeError, self._local, 1)
Jack Diederich561d5aa2010-02-22 19:55:46 +0000120
Antoine Pitrou1a9a9d52010-08-28 18:17:03 +0000121 def _test_one_class(self, c):
122 self._failed = "No error message set or cleared."
123 obj = c()
124 e1 = threading.Event()
125 e2 = threading.Event()
126
127 def f1():
128 obj.x = 'foo'
129 obj.y = 'bar'
130 del obj.y
131 e1.set()
132 e2.wait()
133
134 def f2():
135 try:
136 foo = obj.x
137 except AttributeError:
138 # This is expected -- we haven't set obj.x in this thread yet!
139 self._failed = "" # passed
140 else:
141 self._failed = ('Incorrectly got value %r from class %r\n' %
142 (foo, c))
143 sys.stderr.write(self._failed)
144
145 t1 = threading.Thread(target=f1)
146 t1.start()
147 e1.wait()
148 t2 = threading.Thread(target=f2)
149 t2.start()
150 t2.join()
151 # The test is done; just let t1 know it can exit, and wait for it.
152 e2.set()
153 t1.join()
154
155 self.assertFalse(self._failed, self._failed)
156
157 def test_threading_local(self):
158 self._test_one_class(self._local)
159
160 def test_threading_local_subclass(self):
161 class LocalSubclass(self._local):
162 """To test that subclasses behave properly."""
163 self._test_one_class(LocalSubclass)
164
165 def _test_dict_attribute(self, cls):
166 obj = cls()
167 obj.x = 5
168 self.assertEqual(obj.__dict__, {'x': 5})
169 with self.assertRaises(AttributeError):
170 obj.__dict__ = {}
171 with self.assertRaises(AttributeError):
172 del obj.__dict__
173
174 def test_dict_attribute(self):
175 self._test_dict_attribute(self._local)
176
177 def test_dict_attribute_subclass(self):
178 class LocalSubclass(self._local):
179 """To test that subclasses behave properly."""
180 self._test_dict_attribute(LocalSubclass)
181
Antoine Pitrou5af4f4b2010-08-09 22:38:19 +0000182 def test_cycle_collection(self):
183 class X:
184 pass
185
186 x = X()
187 x.local = self._local()
188 x.local.x = x
189 wr = weakref.ref(x)
190 del x
191 gc.collect()
Serhiy Storchaka8c0f0c52016-03-14 10:28:59 +0200192 self.assertIsNone(wr())
Antoine Pitrou5af4f4b2010-08-09 22:38:19 +0000193
Antoine Pitrou783eea72010-09-07 22:06:17 +0000194
195class ThreadLocalTest(unittest.TestCase, BaseLocalTest):
196 _local = _thread._local
197
Antoine Pitrouda991da2010-08-03 18:32:26 +0000198class PyThreadingLocalTest(unittest.TestCase, BaseLocalTest):
199 _local = _threading_local.local
Jack Diederich561d5aa2010-02-22 19:55:46 +0000200
Benjamin Peterson8a250ae2008-06-30 23:30:24 +0000201
Jim Fultond15dc062004-07-14 19:11:50 +0000202def test_main():
Christian Heimes587c2bf2008-01-19 16:21:02 +0000203 suite = unittest.TestSuite()
204 suite.addTest(DocTestSuite('_threading_local'))
Antoine Pitrouda991da2010-08-03 18:32:26 +0000205 suite.addTest(unittest.makeSuite(ThreadLocalTest))
206 suite.addTest(unittest.makeSuite(PyThreadingLocalTest))
Jim Fultond15dc062004-07-14 19:11:50 +0000207
Antoine Pitrou577ba7d2010-08-04 00:18:49 +0000208 local_orig = _threading_local.local
209 def setUp(test):
210 _threading_local.local = _thread._local
211 def tearDown(test):
212 _threading_local.local = local_orig
213 suite.addTest(DocTestSuite('_threading_local',
214 setUp=setUp, tearDown=tearDown)
215 )
Jim Fultond15dc062004-07-14 19:11:50 +0000216
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000217 support.run_unittest(suite)
Jim Fultond15dc062004-07-14 19:11:50 +0000218
219if __name__ == '__main__':
220 test_main()