Skip to content

Commit 6bdfc4b

Browse files
m4ns0urtrotterdylan
authored andcommitted
Add dummy_thread module (google#372)
1 parent 7f4857e commit 6bdfc4b

File tree

3 files changed

+328
-0
lines changed

3 files changed

+328
-0
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ STDLIB_TESTS := \
9797
test/test_datetime \
9898
test/test_dict \
9999
test/test_dircache \
100+
test/test_dummy_thread \
100101
test/test_fpformat \
101102
test/test_genericpath \
102103
test/test_list \

third_party/stdlib/dummy_thread.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
"""Drop-in replacement for the thread module.
2+
3+
Meant to be used as a brain-dead substitute so that threaded code does
4+
not need to be rewritten for when the thread module is not present.
5+
6+
Suggested 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+
import traceback as _traceback
20+
21+
class error(Exception):
22+
"""Dummy implementation of thread.error."""
23+
24+
def __init__(self, *args):
25+
self.args = args
26+
27+
def start_new_thread(function, args, kwargs={}):
28+
"""Dummy implementation of thread.start_new_thread().
29+
30+
Compatibility is maintained by making sure that ``args`` is a
31+
tuple and ``kwargs`` is a dictionary. If an exception is raised
32+
and it is SystemExit (which can be done by thread.exit()) it is
33+
caught and nothing is done; all other exceptions are printed out
34+
by using traceback.print_exc().
35+
36+
If the executed function calls interrupt_main the KeyboardInterrupt will be
37+
raised when the function returns.
38+
39+
"""
40+
if type(args) != type(tuple()):
41+
raise TypeError("2nd arg must be a tuple")
42+
if type(kwargs) != type(dict()):
43+
raise TypeError("3rd arg must be a dict")
44+
global _main
45+
_main = False
46+
try:
47+
function(*args, **kwargs)
48+
except SystemExit:
49+
pass
50+
except:
51+
_traceback.print_exc()
52+
_main = True
53+
global _interrupt
54+
if _interrupt:
55+
_interrupt = False
56+
raise KeyboardInterrupt
57+
58+
def exit():
59+
"""Dummy implementation of thread.exit()."""
60+
raise SystemExit
61+
62+
def get_ident():
63+
"""Dummy implementation of thread.get_ident().
64+
65+
Since this module should only be used when threadmodule is not
66+
available, it is safe to assume that the current process is the
67+
only thread. Thus a constant can be safely returned.
68+
"""
69+
return -1
70+
71+
def allocate_lock():
72+
"""Dummy implementation of thread.allocate_lock()."""
73+
return LockType()
74+
75+
def stack_size(size=None):
76+
"""Dummy implementation of thread.stack_size()."""
77+
if size is not None:
78+
raise error("setting thread stack size not supported")
79+
return 0
80+
81+
class LockType(object):
82+
"""Class implementing dummy implementation of thread.LockType.
83+
84+
Compatibility is maintained by maintaining self.locked_status
85+
which is a boolean that stores the state of the lock. Pickling of
86+
the lock, though, should not be done since if the thread module is
87+
then used with an unpickled ``lock()`` from here problems could
88+
occur from this class not having atomic methods.
89+
90+
"""
91+
92+
def __init__(self):
93+
self.locked_status = False
94+
95+
def acquire(self, waitflag=None):
96+
"""Dummy implementation of acquire().
97+
98+
For blocking calls, self.locked_status is automatically set to
99+
True and returned appropriately based on value of
100+
``waitflag``. If it is non-blocking, then the value is
101+
actually checked and not set if it is already acquired. This
102+
is all done so that threading.Condition's assert statements
103+
aren't triggered and throw a little fit.
104+
105+
"""
106+
if waitflag is None or waitflag:
107+
self.locked_status = True
108+
return True
109+
else:
110+
if not self.locked_status:
111+
self.locked_status = True
112+
return True
113+
else:
114+
return False
115+
116+
__enter__ = acquire
117+
118+
def __exit__(self, typ, val, tb):
119+
self.release()
120+
121+
def release(self):
122+
"""Release the dummy lock."""
123+
# XXX Perhaps shouldn't actually bother to test? Could lead
124+
# to problems for complex, threaded code.
125+
if not self.locked_status:
126+
raise error
127+
self.locked_status = False
128+
return True
129+
130+
def locked(self):
131+
return self.locked_status
132+
133+
# Used to signal that interrupt_main was called in a "thread"
134+
_interrupt = False
135+
# True when not executing in a "thread"
136+
_main = True
137+
138+
def interrupt_main():
139+
"""Set _interrupt flag to True to have start_new_thread raise
140+
KeyboardInterrupt upon exiting."""
141+
if _main:
142+
raise KeyboardInterrupt
143+
else:
144+
global _interrupt
145+
_interrupt = True
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
"""Generic thread tests.
2+
3+
Meant to be used by dummy_thread and thread. To allow for different modules
4+
to be used, test_main() can be called with the module to use as the thread
5+
implementation as its sole argument.
6+
7+
"""
8+
import dummy_thread as _thread
9+
import time
10+
import Queue
11+
import random
12+
import unittest
13+
from test import test_support
14+
15+
DELAY = 0 # Set > 0 when testing a module other than dummy_thread, such as
16+
# the 'thread' module.
17+
18+
class LockTests(unittest.TestCase):
19+
"""Test lock objects."""
20+
21+
def setUp(self):
22+
# Create a lock
23+
self.lock = _thread.allocate_lock()
24+
25+
def test_initlock(self):
26+
#Make sure locks start locked
27+
self.assertFalse(self.lock.locked(),
28+
"Lock object is not initialized unlocked.")
29+
30+
def test_release(self):
31+
# Test self.lock.release()
32+
self.lock.acquire()
33+
self.lock.release()
34+
self.assertFalse(self.lock.locked(),
35+
"Lock object did not release properly.")
36+
37+
def test_improper_release(self):
38+
#Make sure release of an unlocked thread raises _thread.error
39+
self.assertRaises(_thread.error, self.lock.release)
40+
41+
def test_cond_acquire_success(self):
42+
#Make sure the conditional acquiring of the lock works.
43+
self.assertTrue(self.lock.acquire(0),
44+
"Conditional acquiring of the lock failed.")
45+
46+
def test_cond_acquire_fail(self):
47+
#Test acquiring locked lock returns False
48+
self.lock.acquire(0)
49+
self.assertFalse(self.lock.acquire(0),
50+
"Conditional acquiring of a locked lock incorrectly "
51+
"succeeded.")
52+
53+
def test_uncond_acquire_success(self):
54+
#Make sure unconditional acquiring of a lock works.
55+
self.lock.acquire()
56+
self.assertTrue(self.lock.locked(),
57+
"Uncondional locking failed.")
58+
59+
def test_uncond_acquire_return_val(self):
60+
#Make sure that an unconditional locking returns True.
61+
self.assertIs(self.lock.acquire(1), True,
62+
"Unconditional locking did not return True.")
63+
self.assertIs(self.lock.acquire(), True)
64+
65+
def test_uncond_acquire_blocking(self):
66+
#Make sure that unconditional acquiring of a locked lock blocks.
67+
def delay_unlock(to_unlock, delay):
68+
"""Hold on to lock for a set amount of time before unlocking."""
69+
time.sleep(delay)
70+
to_unlock.release()
71+
72+
self.lock.acquire()
73+
start_time = int(time.time())
74+
_thread.start_new_thread(delay_unlock,(self.lock, DELAY))
75+
if test_support.verbose:
76+
print
77+
print "*** Waiting for thread to release the lock "\
78+
"(approx. %s sec.) ***" % DELAY
79+
self.lock.acquire()
80+
end_time = int(time.time())
81+
if test_support.verbose:
82+
print "done"
83+
self.assertGreaterEqual(end_time - start_time, DELAY,
84+
"Blocking by unconditional acquiring failed.")
85+
86+
class MiscTests(unittest.TestCase):
87+
"""Miscellaneous tests."""
88+
89+
def test_exit(self):
90+
#Make sure _thread.exit() raises SystemExit
91+
self.assertRaises(SystemExit, _thread.exit)
92+
93+
def test_ident(self):
94+
#Test sanity of _thread.get_ident()
95+
self.assertIsInstance(_thread.get_ident(), int,
96+
"_thread.get_ident() returned a non-integer")
97+
self.assertNotEqual(_thread.get_ident(), 0,
98+
"_thread.get_ident() returned 0")
99+
100+
def test_LockType(self):
101+
#Make sure _thread.LockType is the same type as _thread.allocate_locke()
102+
self.assertIsInstance(_thread.allocate_lock(), _thread.LockType,
103+
"_thread.LockType is not an instance of what "
104+
"is returned by _thread.allocate_lock()")
105+
106+
def test_interrupt_main(self):
107+
#Calling start_new_thread with a function that executes interrupt_main
108+
# should raise KeyboardInterrupt upon completion.
109+
def call_interrupt():
110+
_thread.interrupt_main()
111+
self.assertRaises(KeyboardInterrupt, _thread.start_new_thread,
112+
call_interrupt, tuple())
113+
114+
def test_interrupt_in_main(self):
115+
# Make sure that if interrupt_main is called in main threat that
116+
# KeyboardInterrupt is raised instantly.
117+
self.assertRaises(KeyboardInterrupt, _thread.interrupt_main)
118+
119+
class ThreadTests(unittest.TestCase):
120+
"""Test thread creation."""
121+
122+
def test_arg_passing(self):
123+
#Make sure that parameter passing works.
124+
def arg_tester(queue, arg1=False, arg2=False):
125+
"""Use to test _thread.start_new_thread() passes args properly."""
126+
queue.put((arg1, arg2))
127+
128+
testing_queue = Queue.Queue(1)
129+
_thread.start_new_thread(arg_tester, (testing_queue, True, True))
130+
result = testing_queue.get()
131+
self.assertTrue(result[0] and result[1],
132+
"Argument passing for thread creation using tuple failed")
133+
_thread.start_new_thread(arg_tester, tuple(), {'queue':testing_queue,
134+
'arg1':True, 'arg2':True})
135+
result = testing_queue.get()
136+
self.assertTrue(result[0] and result[1],
137+
"Argument passing for thread creation using kwargs failed")
138+
_thread.start_new_thread(arg_tester, (testing_queue, True), {'arg2':True})
139+
result = testing_queue.get()
140+
self.assertTrue(result[0] and result[1],
141+
"Argument passing for thread creation using both tuple"
142+
" and kwargs failed")
143+
144+
def test_multi_creation(self):
145+
#Make sure multiple threads can be created.
146+
def queue_mark(queue, delay):
147+
"""Wait for ``delay`` seconds and then put something into ``queue``"""
148+
time.sleep(delay)
149+
queue.put(_thread.get_ident())
150+
151+
thread_count = 5
152+
testing_queue = Queue.Queue(thread_count)
153+
if test_support.verbose:
154+
print
155+
print "*** Testing multiple thread creation "\
156+
"(will take approx. %s to %s sec.) ***" % (DELAY, thread_count)
157+
for count in xrange(thread_count):
158+
if DELAY:
159+
local_delay = round(random.random(), 1)
160+
else:
161+
local_delay = 0
162+
_thread.start_new_thread(queue_mark,
163+
(testing_queue, local_delay))
164+
time.sleep(DELAY)
165+
if test_support.verbose:
166+
print 'done'
167+
self.assertEqual(testing_queue.qsize(), thread_count,
168+
"Not all %s threads executed properly after %s sec." %
169+
(thread_count, DELAY))
170+
171+
def test_main(imported_module=None):
172+
global _thread, DELAY
173+
if imported_module:
174+
_thread = imported_module
175+
DELAY = 2
176+
if test_support.verbose:
177+
print
178+
print "*** Using %s as _thread module ***" % _thread
179+
test_support.run_unittest(LockTests, MiscTests, ThreadTests)
180+
181+
if __name__ == '__main__':
182+
test_main()

0 commit comments

Comments
 (0)