summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2010-08-28 18:29:13 (GMT)
committerAntoine Pitrou <solipsis@pitrou.net>2010-08-28 18:29:13 (GMT)
commita408350a08d4aadaec7573648ab91651c32a90df (patch)
tree717563d43e1d65ed46661ba04508513146946e52 /Lib
parent7e8fd5ed22f19ede1290fdc692c607556d4e16da (diff)
downloadcpython-a408350a08d4aadaec7573648ab91651c32a90df.zip
cpython-a408350a08d4aadaec7573648ab91651c32a90df.tar.gz
cpython-a408350a08d4aadaec7573648ab91651c32a90df.tar.bz2
Merged revisions 84344 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84344 | antoine.pitrou | 2010-08-28 20:17:03 +0200 (sam., 28 août 2010) | 4 lines Issue #1868: Eliminate subtle timing issues in thread-local objects by getting rid of the cached copy of thread-local attribute dictionary. ........
Diffstat (limited to 'Lib')
-rw-r--r--Lib/_threading_local.py8
-rw-r--r--Lib/test/test_threading_local.py62
2 files changed, 69 insertions, 1 deletions
diff --git a/Lib/_threading_local.py b/Lib/_threading_local.py
index e953597..09a3515 100644
--- a/Lib/_threading_local.py
+++ b/Lib/_threading_local.py
@@ -195,6 +195,10 @@ class local(_localbase):
lock.release()
def __setattr__(self, name, value):
+ if name == '__dict__':
+ raise AttributeError(
+ "%r object attribute '__dict__' is read-only"
+ % self.__class__.__name__)
lock = object.__getattribute__(self, '_local__lock')
lock.acquire()
try:
@@ -204,6 +208,10 @@ class local(_localbase):
lock.release()
def __delattr__(self, name):
+ if name == '__dict__':
+ raise AttributeError(
+ "%r object attribute '__dict__' is read-only"
+ % self.__class__.__name__)
lock = object.__getattribute__(self, '_local__lock')
lock.acquire()
try:
diff --git a/Lib/test/test_threading_local.py b/Lib/test/test_threading_local.py
index 35c0889..4c9f296 100644
--- a/Lib/test/test_threading_local.py
+++ b/Lib/test/test_threading_local.py
@@ -125,6 +125,67 @@ class BaseLocalTest:
self.assertRaises(TypeError, cls, a=1)
self.assertRaises(TypeError, cls, 1)
+ def _test_one_class(self, c):
+ self._failed = "No error message set or cleared."
+ obj = c()
+ e1 = threading.Event()
+ e2 = threading.Event()
+
+ def f1():
+ obj.x = 'foo'
+ obj.y = 'bar'
+ del obj.y
+ e1.set()
+ e2.wait()
+
+ def f2():
+ try:
+ foo = obj.x
+ except AttributeError:
+ # This is expected -- we haven't set obj.x in this thread yet!
+ self._failed = "" # passed
+ else:
+ self._failed = ('Incorrectly got value %r from class %r\n' %
+ (foo, c))
+ sys.stderr.write(self._failed)
+
+ t1 = threading.Thread(target=f1)
+ t1.start()
+ e1.wait()
+ t2 = threading.Thread(target=f2)
+ t2.start()
+ t2.join()
+ # The test is done; just let t1 know it can exit, and wait for it.
+ e2.set()
+ t1.join()
+
+ self.assertFalse(self._failed, self._failed)
+
+ def test_threading_local(self):
+ self._test_one_class(self._local)
+
+ def test_threading_local_subclass(self):
+ class LocalSubclass(self._local):
+ """To test that subclasses behave properly."""
+ self._test_one_class(LocalSubclass)
+
+ def _test_dict_attribute(self, cls):
+ obj = cls()
+ obj.x = 5
+ self.assertEqual(obj.__dict__, {'x': 5})
+ with self.assertRaises(AttributeError):
+ obj.__dict__ = {}
+ with self.assertRaises(AttributeError):
+ del obj.__dict__
+
+ def test_dict_attribute(self):
+ self._test_dict_attribute(self._local)
+
+ def test_dict_attribute_subclass(self):
+ class LocalSubclass(self._local):
+ """To test that subclasses behave properly."""
+ self._test_dict_attribute(LocalSubclass)
+
class ThreadLocalTest(unittest.TestCase, BaseLocalTest):
_local = _thread._local
@@ -142,7 +203,6 @@ class ThreadLocalTest(unittest.TestCase, BaseLocalTest):
gc.collect()
self.assertIs(wr(), None)
-
class PyThreadingLocalTest(unittest.TestCase, BaseLocalTest):
_local = _threading_local.local