blob: 0a877e1fa1690551056a459af0084faa221ec589 [file] [log] [blame]
Antoine Pitroub43c4ca2017-09-18 22:04:20 +02001"""Drop-in replacement for the thread module.
2
3Meant to be used as a brain-dead substitute so that threaded code does
4not need to be rewritten for when the thread module is not present.
5
6Suggested usage is::
7
8 try:
9 import _thread
10 except ImportError:
11 import _dummy_thread as _thread
12
13"""
14# Exports only things specified by thread documentation;
15# skipping obsolete synonyms allocate(), start_new(), exit_thread().
16__all__ = ['error', 'start_new_thread', 'exit', 'get_ident', 'allocate_lock',
17 'interrupt_main', 'LockType']
18
19# A dummy value
20TIMEOUT_MAX = 2**31
21
22# NOTE: this module can be imported early in the extension building process,
23# and so top level imports of other modules should be avoided. Instead, all
24# imports are done when needed on a function-by-function basis. Since threads
25# are disabled, the import lock should not be an issue anyway (??).
26
27error = RuntimeError
28
29def start_new_thread(function, args, kwargs={}):
30 """Dummy implementation of _thread.start_new_thread().
31
32 Compatibility is maintained by making sure that ``args`` is a
33 tuple and ``kwargs`` is a dictionary. If an exception is raised
34 and it is SystemExit (which can be done by _thread.exit()) it is
35 caught and nothing is done; all other exceptions are printed out
36 by using traceback.print_exc().
37
38 If the executed function calls interrupt_main the KeyboardInterrupt will be
39 raised when the function returns.
40
41 """
42 if type(args) != type(tuple()):
43 raise TypeError("2nd arg must be a tuple")
44 if type(kwargs) != type(dict()):
45 raise TypeError("3rd arg must be a dict")
46 global _main
47 _main = False
48 try:
49 function(*args, **kwargs)
50 except SystemExit:
51 pass
52 except:
53 import traceback
54 traceback.print_exc()
55 _main = True
56 global _interrupt
57 if _interrupt:
58 _interrupt = False
59 raise KeyboardInterrupt
60
61def exit():
62 """Dummy implementation of _thread.exit()."""
63 raise SystemExit
64
65def get_ident():
66 """Dummy implementation of _thread.get_ident().
67
68 Since this module should only be used when _threadmodule is not
69 available, it is safe to assume that the current process is the
70 only thread. Thus a constant can be safely returned.
71 """
72 return 1
73
Jake Tesler4959c332019-05-12 10:08:24 -070074def get_native_id():
75 """Dummy implementation of _thread.get_native_id()."""
76 return 0
77
Antoine Pitroub43c4ca2017-09-18 22:04:20 +020078def allocate_lock():
79 """Dummy implementation of _thread.allocate_lock()."""
80 return LockType()
81
82def stack_size(size=None):
83 """Dummy implementation of _thread.stack_size()."""
84 if size is not None:
85 raise error("setting thread stack size not supported")
86 return 0
87
88def _set_sentinel():
89 """Dummy implementation of _thread._set_sentinel()."""
90 return LockType()
91
92class LockType(object):
93 """Class implementing dummy implementation of _thread.LockType.
94
95 Compatibility is maintained by maintaining self.locked_status
96 which is a boolean that stores the state of the lock. Pickling of
97 the lock, though, should not be done since if the _thread module is
98 then used with an unpickled ``lock()`` from here problems could
99 occur from this class not having atomic methods.
100
101 """
102
103 def __init__(self):
104 self.locked_status = False
105
106 def acquire(self, waitflag=None, timeout=-1):
107 """Dummy implementation of acquire().
108
109 For blocking calls, self.locked_status is automatically set to
110 True and returned appropriately based on value of
111 ``waitflag``. If it is non-blocking, then the value is
112 actually checked and not set if it is already acquired. This
113 is all done so that threading.Condition's assert statements
114 aren't triggered and throw a little fit.
115
116 """
117 if waitflag is None or waitflag:
118 self.locked_status = True
119 return True
120 else:
121 if not self.locked_status:
122 self.locked_status = True
123 return True
124 else:
125 if timeout > 0:
126 import time
127 time.sleep(timeout)
128 return False
129
130 __enter__ = acquire
131
132 def __exit__(self, typ, val, tb):
133 self.release()
134
135 def release(self):
136 """Release the dummy lock."""
137 # XXX Perhaps shouldn't actually bother to test? Could lead
138 # to problems for complex, threaded code.
139 if not self.locked_status:
140 raise error
141 self.locked_status = False
142 return True
143
144 def locked(self):
145 return self.locked_status
146
147 def __repr__(self):
148 return "<%s %s.%s object at %s>" % (
149 "locked" if self.locked_status else "unlocked",
150 self.__class__.__module__,
151 self.__class__.__qualname__,
152 hex(id(self))
153 )
154
155# Used to signal that interrupt_main was called in a "thread"
156_interrupt = False
157# True when not executing in a "thread"
158_main = True
159
160def interrupt_main():
161 """Set _interrupt flag to True to have start_new_thread raise
162 KeyboardInterrupt upon exiting."""
163 if _main:
164 raise KeyboardInterrupt
165 else:
166 global _interrupt
167 _interrupt = True