frompypy.module.thread.test.supportimportGenericTestThreadclassAppTestLocal(GenericTestThread):deftest_local_1(self):import_threadfrom_threadimport_localastlsobjectfreed=[]classX:def__del__(self):freed.append(1)ok=[]TLS1=tlsobject()TLS2=tlsobject()TLS1.aa="hello"deff(i):success=Falsetry:a=TLS1.aa=ib=TLS1.bbb=X()c=TLS2.cccc=i*3d=TLS2.ddddd=X()self.busywait(0.05)assertTLS1.aa==aassertTLS1.bbbisbassertTLS2.cccc==cassertTLS2.dddddisdsuccess=Truefinally:ok.append(success)foriinrange(20):_thread.start_new_thread(f,(i,))self.waitfor(lambda:len(ok)==20,delay=3)assertok==20*[True]# see stdout/stderr for failures in the threadsself.waitfor(lambda:len(freed)>=40)assertlen(freed)==40# in theory, all X objects should have been freed by now. Note that# Python's own thread._local objects suffer from the very same "bug" that# tls.py showed originally, and leaves len(freed)==38: the last thread's# __dict__ remains stored in the TLS1/TLS2 instances, although it is not# really accessible any more.assertTLS1.aa=="hello"deftest_local_init(self):import_threadtags=['???',1,2,3,4,5,54321]seen=[]raises(TypeError,_thread._local,a=1)raises(TypeError,_thread._local,1)classX(_thread._local):def__init__(self,n):assertn==42self.tag=tags.pop()x=X(42)assertx.tag==54321assertx.tag==54321deff():seen.append(x.tag)foriinrange(5):_thread.start_new_thread(f,())self.waitfor(lambda:len(seen)==5,delay=2)seen1=seen[:]seen1.sort()assertseen1==[1,2,3,4,5]asserttags==['???']deftest_local_setdict(self):import_threadx=_thread._local()raises(AttributeError,"x.__dict__ = 42")raises(AttributeError,"x.__dict__ = {}")done=[]deff(n):x.spam=nassertx.__dict__["spam"]==ndone.append(1)foriinrange(5):_thread.start_new_thread(f,(i,))self.waitfor(lambda:len(done)==5,delay=2)assertlen(done)==5deftest_local_is_not_immortal(self):import_thread,gc,timeclassLocal(_thread._local):def__del__(self):done.append('del')done=[]deff():assertnothasattr(l,'foo')l.bar=42done.append('ok')self.waitfor(lambda:len(done)==3,delay=8)l=Local()l.foo=42_thread.start_new_thread(f,())self.waitfor(lambda:len(done)==1,delay=2)l=Nonegc.collect()assertdone==['ok','del']done.append('shutdown')deftest_local_caching():frompypy.module.thread.os_localimportLocalclassFakeSpace:defgetexecutioncontext(self):returnself.ecdefgetattr(*args):passdefcall_obj_args(*args):passdefnewdict(*args,**kwargs):return{}defwrap(self,obj):returnobjdeftype(self,obj):returntype(obj)classconfig:classtranslation:rweakref=TrueclassFakeEC:def__init__(self,space):self.space=spaceself._thread_local_objs=Nonespace=FakeSpace()ec1=FakeEC(space)space.ec=ec1l=Local(space,None)assertl.last_dictisl.dicts[ec1]assertl.last_ecisec1d1=l.getdict(space)assertd1isl.last_dictec2=space.ec=FakeEC(space)d2=l.getdict(space)assertl.last_dictisd2assertd2isl.dicts[ec2]assertl.last_ecisec2dicts=l.dictsl.dicts="nope"assertl.getdict(space)isd2l.dicts=dictsspace.ec=ec1assertl.getdict(space)isd1l.dicts="nope"assertl.getdict(space)isd1l.dicts=dicts