1111 import _dummy_thread as _thread
1212
1313"""
14+
1415# Exports only things specified by thread documentation;
1516# skipping obsolete synonyms allocate(), start_new(), exit_thread().
16- __all__ = ['error' , 'start_new_thread' , 'exit' , 'get_ident' , 'allocate_lock' ,
17- 'interrupt_main' , 'LockType' , 'RLock' ,
18- '_count' ]
17+ __all__ = [
18+ "error" ,
19+ "start_new_thread" ,
20+ "exit" ,
21+ "get_ident" ,
22+ "allocate_lock" ,
23+ "interrupt_main" ,
24+ "LockType" ,
25+ "RLock" ,
26+ "_count" ,
27+ "start_joinable_thread" ,
28+ "daemon_threads_allowed" ,
29+ "_shutdown" ,
30+ "_make_thread_handle" ,
31+ "_ThreadHandle" ,
32+ "_get_main_thread_ident" ,
33+ "_is_main_interpreter" ,
34+ "_local" ,
35+ ]
1936
2037# A dummy value
2138TIMEOUT_MAX = 2 ** 31
2239
40+ # Main thread ident for dummy implementation
41+ _MAIN_THREAD_IDENT = - 1
42+
2343# NOTE: this module can be imported early in the extension building process,
2444# and so top level imports of other modules should be avoided. Instead, all
2545# imports are done when needed on a function-by-function basis. Since threads
2646# are disabled, the import lock should not be an issue anyway (??).
2747
2848error = RuntimeError
2949
50+
3051def start_new_thread (function , args , kwargs = {}):
3152 """Dummy implementation of _thread.start_new_thread().
3253
@@ -52,44 +73,98 @@ def start_new_thread(function, args, kwargs={}):
5273 pass
5374 except :
5475 import traceback
76+
5577 traceback .print_exc ()
5678 _main = True
5779 global _interrupt
5880 if _interrupt :
5981 _interrupt = False
6082 raise KeyboardInterrupt
6183
84+
85+ def start_joinable_thread (function , handle = None , daemon = True ):
86+ """Dummy implementation of _thread.start_joinable_thread().
87+
88+ In dummy thread, we just run the function synchronously.
89+ """
90+ if handle is None :
91+ handle = _ThreadHandle ()
92+ try :
93+ function ()
94+ except SystemExit :
95+ pass
96+ except :
97+ import traceback
98+
99+ traceback .print_exc ()
100+ handle ._set_done ()
101+ return handle
102+
103+
104+ def daemon_threads_allowed ():
105+ """Dummy implementation of _thread.daemon_threads_allowed()."""
106+ return True
107+
108+
109+ def _shutdown ():
110+ """Dummy implementation of _thread._shutdown()."""
111+ pass
112+
113+
114+ def _make_thread_handle (ident ):
115+ """Dummy implementation of _thread._make_thread_handle()."""
116+ handle = _ThreadHandle ()
117+ handle ._ident = ident
118+ return handle
119+
120+
121+ def _get_main_thread_ident ():
122+ """Dummy implementation of _thread._get_main_thread_ident()."""
123+ return _MAIN_THREAD_IDENT
124+
125+
126+ def _is_main_interpreter ():
127+ """Dummy implementation of _thread._is_main_interpreter()."""
128+ return True
129+
130+
62131def exit ():
63132 """Dummy implementation of _thread.exit()."""
64133 raise SystemExit
65134
135+
66136def get_ident ():
67137 """Dummy implementation of _thread.get_ident().
68138
69139 Since this module should only be used when _threadmodule is not
70140 available, it is safe to assume that the current process is the
71141 only thread. Thus a constant can be safely returned.
72142 """
73- return - 1
143+ return _MAIN_THREAD_IDENT
144+
74145
75146def allocate_lock ():
76147 """Dummy implementation of _thread.allocate_lock()."""
77148 return LockType ()
78149
150+
79151def stack_size (size = None ):
80152 """Dummy implementation of _thread.stack_size()."""
81153 if size is not None :
82154 raise error ("setting thread stack size not supported" )
83155 return 0
84156
157+
85158def _set_sentinel ():
86159 """Dummy implementation of _thread._set_sentinel()."""
87160 return LockType ()
88161
162+
89163def _count ():
90164 """Dummy implementation of _thread._count()."""
91165 return 0
92166
167+
93168class LockType (object ):
94169 """Class implementing dummy implementation of _thread.LockType.
95170
@@ -125,6 +200,7 @@ def acquire(self, waitflag=None, timeout=-1):
125200 else :
126201 if timeout > 0 :
127202 import time
203+
128204 time .sleep (timeout )
129205 return False
130206
@@ -153,14 +229,41 @@ def __repr__(self):
153229 "locked" if self .locked_status else "unlocked" ,
154230 self .__class__ .__module__ ,
155231 self .__class__ .__qualname__ ,
156- hex (id (self ))
232+ hex (id (self )),
157233 )
158234
235+
236+ class _ThreadHandle :
237+ """Dummy implementation of _thread._ThreadHandle."""
238+
239+ def __init__ (self ):
240+ self ._ident = _MAIN_THREAD_IDENT
241+ self ._done = False
242+
243+ @property
244+ def ident (self ):
245+ return self ._ident
246+
247+ def _set_done (self ):
248+ self ._done = True
249+
250+ def is_done (self ):
251+ return self ._done
252+
253+ def join (self , timeout = None ):
254+ # In dummy thread, thread is always done
255+ return
256+
257+ def __repr__ (self ):
258+ return f"<_ThreadHandle ident={ self ._ident } >"
259+
260+
159261# Used to signal that interrupt_main was called in a "thread"
160262_interrupt = False
161263# True when not executing in a "thread"
162264_main = True
163265
266+
164267def interrupt_main ():
165268 """Set _interrupt flag to True to have start_new_thread raise
166269 KeyboardInterrupt upon exiting."""
@@ -170,6 +273,7 @@ def interrupt_main():
170273 global _interrupt
171274 _interrupt = True
172275
276+
173277class RLock :
174278 def __init__ (self ):
175279 self .locked_count = 0
@@ -190,7 +294,7 @@ def release(self):
190294 return True
191295
192296 def locked (self ):
193- return self .locked_status != 0
297+ return self .locked_count != 0
194298
195299 def __repr__ (self ):
196300 return "<%s %s.%s object owner=%s count=%s at %s>" % (
@@ -199,5 +303,36 @@ def __repr__(self):
199303 self .__class__ .__qualname__ ,
200304 get_ident () if self .locked_count else 0 ,
201305 self .locked_count ,
202- hex (id (self ))
306+ hex (id (self )),
203307 )
308+
309+
310+ class _local :
311+ """Dummy implementation of _thread._local (thread-local storage)."""
312+
313+ def __init__ (self ):
314+ object .__setattr__ (self , "_local__impl" , {})
315+
316+ def __getattribute__ (self , name ):
317+ if name .startswith ("_local__" ):
318+ return object .__getattribute__ (self , name )
319+ impl = object .__getattribute__ (self , "_local__impl" )
320+ try :
321+ return impl [name ]
322+ except KeyError :
323+ raise AttributeError (name )
324+
325+ def __setattr__ (self , name , value ):
326+ if name .startswith ("_local__" ):
327+ return object .__setattr__ (self , name , value )
328+ impl = object .__getattribute__ (self , "_local__impl" )
329+ impl [name ] = value
330+
331+ def __delattr__ (self , name ):
332+ if name .startswith ("_local__" ):
333+ return object .__delattr__ (self , name )
334+ impl = object .__getattribute__ (self , "_local__impl" )
335+ try :
336+ del impl [name ]
337+ except KeyError :
338+ raise AttributeError (name )
0 commit comments