Skip to content

Commit b43c4ca

Browse files
authored
Restore dummy_threading and _dummy_thread, but deprecate them (bpo-31370) (#3648)
1 parent a8e7d90 commit b43c4ca

File tree

9 files changed

+575
-31
lines changed

9 files changed

+575
-31
lines changed

Doc/library/_dummy_thread.rst

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,15 @@
66

77
**Source code:** :source:`Lib/_dummy_thread.py`
88

9-
--------------
10-
11-
This module provides a duplicate interface to the :mod:`_thread` module. It is
12-
meant to be imported when the :mod:`_thread` module is not provided on a
13-
platform.
9+
.. deprecated:: 3.7
10+
Python now always has threading enabled. Please use :mod:`_thread`
11+
(or, better, :mod:`threading`) instead.
1412

15-
Suggested usage is::
13+
--------------
1614

17-
try:
18-
import _thread
19-
except ImportError:
20-
import _dummy_thread as _thread
15+
This module provides a duplicate interface to the :mod:`_thread` module.
16+
It was meant to be imported when the :mod:`_thread` module was not provided
17+
on a platform.
2118

2219
Be careful to not use this module where deadlock might occur from a thread being
2320
created that blocks waiting for another thread to be created. This often occurs

Doc/library/_thread.rst

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,10 @@ threading API built on top of this module.
2323
single: pthreads
2424
pair: threads; POSIX
2525

26-
The module is optional. It is supported on Windows, Linux, SGI IRIX, Solaris
27-
2.x, as well as on systems that have a POSIX thread (a.k.a. "pthread")
28-
implementation. For systems lacking the :mod:`_thread` module, the
29-
:mod:`_dummy_thread` module is available. It duplicates this module's interface
30-
and can be used as a drop-in replacement.
31-
32-
It defines the following constants and functions:
26+
.. versionchanged:: 3.7
27+
This module used to be optional, it is now always available.
3328

29+
This module defines the following constants and functions:
3430

3531
.. exception:: error
3632

Doc/library/concurrency.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@ The following are support modules for some of the above services:
2626

2727
.. toctree::
2828

29-
dummy_threading.rst
3029
_thread.rst
3130
_dummy_thread.rst
31+
dummy_threading.rst

Doc/library/dummy_threading.rst

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,15 @@
66

77
**Source code:** :source:`Lib/dummy_threading.py`
88

9-
--------------
10-
11-
This module provides a duplicate interface to the :mod:`threading` module. It
12-
is meant to be imported when the :mod:`_thread` module is not provided on a
13-
platform.
9+
.. deprecated:: 3.7
10+
Python now always has threading enabled. Please use :mod:`threading` instead.
1411

15-
Suggested usage is::
12+
--------------
1613

17-
try:
18-
import threading
19-
except ImportError:
20-
import dummy_threading as threading
14+
This module provides a duplicate interface to the :mod:`threading` module.
15+
It was meant to be imported when the :mod:`_thread` module was not provided
16+
on a platform.
2117

2218
Be careful to not use this module where deadlock might occur from a thread being
2319
created that blocks waiting for another thread to be created. This often occurs
2420
with blocking I/O.
25-

Doc/library/threading.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
This module constructs higher-level threading interfaces on top of the lower
1212
level :mod:`_thread` module. See also the :mod:`queue` module.
1313

14-
The :mod:`dummy_threading` module is provided for situations where
15-
:mod:`threading` cannot be used because :mod:`_thread` is missing.
14+
.. versionchanged:: 3.7
15+
This module used to be optional, it is now always available.
1616

1717
.. note::
1818

Lib/_dummy_thread.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
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+
# A dummy value
20+
TIMEOUT_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+
27+
error = RuntimeError
28+
29+
def 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+
61+
def exit():
62+
"""Dummy implementation of _thread.exit()."""
63+
raise SystemExit
64+
65+
def 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+
74+
def allocate_lock():
75+
"""Dummy implementation of _thread.allocate_lock()."""
76+
return LockType()
77+
78+
def stack_size(size=None):
79+
"""Dummy implementation of _thread.stack_size()."""
80+
if size is not None:
81+
raise error("setting thread stack size not supported")
82+
return 0
83+
84+
def _set_sentinel():
85+
"""Dummy implementation of _thread._set_sentinel()."""
86+
return LockType()
87+
88+
class LockType(object):
89+
"""Class implementing dummy implementation of _thread.LockType.
90+
91+
Compatibility is maintained by maintaining self.locked_status
92+
which is a boolean that stores the state of the lock. Pickling of
93+
the lock, though, should not be done since if the _thread module is
94+
then used with an unpickled ``lock()`` from here problems could
95+
occur from this class not having atomic methods.
96+
97+
"""
98+
99+
def __init__(self):
100+
self.locked_status = False
101+
102+
def acquire(self, waitflag=None, timeout=-1):
103+
"""Dummy implementation of acquire().
104+
105+
For blocking calls, self.locked_status is automatically set to
106+
True and returned appropriately based on value of
107+
``waitflag``. If it is non-blocking, then the value is
108+
actually checked and not set if it is already acquired. This
109+
is all done so that threading.Condition's assert statements
110+
aren't triggered and throw a little fit.
111+
112+
"""
113+
if waitflag is None or waitflag:
114+
self.locked_status = True
115+
return True
116+
else:
117+
if not self.locked_status:
118+
self.locked_status = True
119+
return True
120+
else:
121+
if timeout > 0:
122+
import time
123+
time.sleep(timeout)
124+
return False
125+
126+
__enter__ = acquire
127+
128+
def __exit__(self, typ, val, tb):
129+
self.release()
130+
131+
def release(self):
132+
"""Release the dummy lock."""
133+
# XXX Perhaps shouldn't actually bother to test? Could lead
134+
# to problems for complex, threaded code.
135+
if not self.locked_status:
136+
raise error
137+
self.locked_status = False
138+
return True
139+
140+
def locked(self):
141+
return self.locked_status
142+
143+
def __repr__(self):
144+
return "<%s %s.%s object at %s>" % (
145+
"locked" if self.locked_status else "unlocked",
146+
self.__class__.__module__,
147+
self.__class__.__qualname__,
148+
hex(id(self))
149+
)
150+
151+
# Used to signal that interrupt_main was called in a "thread"
152+
_interrupt = False
153+
# True when not executing in a "thread"
154+
_main = True
155+
156+
def interrupt_main():
157+
"""Set _interrupt flag to True to have start_new_thread raise
158+
KeyboardInterrupt upon exiting."""
159+
if _main:
160+
raise KeyboardInterrupt
161+
else:
162+
global _interrupt
163+
_interrupt = True

Lib/dummy_threading.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""Faux ``threading`` version using ``dummy_thread`` instead of ``thread``.
2+
3+
The module ``_dummy_threading`` is added to ``sys.modules`` in order
4+
to not have ``threading`` considered imported. Had ``threading`` been
5+
directly imported it would have made all subsequent imports succeed
6+
regardless of whether ``_thread`` was available which is not desired.
7+
8+
"""
9+
from sys import modules as sys_modules
10+
11+
import _dummy_thread
12+
13+
# Declaring now so as to not have to nest ``try``s to get proper clean-up.
14+
holding_thread = False
15+
holding_threading = False
16+
holding__threading_local = False
17+
18+
try:
19+
# Could have checked if ``_thread`` was not in sys.modules and gone
20+
# a different route, but decided to mirror technique used with
21+
# ``threading`` below.
22+
if '_thread' in sys_modules:
23+
held_thread = sys_modules['_thread']
24+
holding_thread = True
25+
# Must have some module named ``_thread`` that implements its API
26+
# in order to initially import ``threading``.
27+
sys_modules['_thread'] = sys_modules['_dummy_thread']
28+
29+
if 'threading' in sys_modules:
30+
# If ``threading`` is already imported, might as well prevent
31+
# trying to import it more than needed by saving it if it is
32+
# already imported before deleting it.
33+
held_threading = sys_modules['threading']
34+
holding_threading = True
35+
del sys_modules['threading']
36+
37+
if '_threading_local' in sys_modules:
38+
# If ``_threading_local`` is already imported, might as well prevent
39+
# trying to import it more than needed by saving it if it is
40+
# already imported before deleting it.
41+
held__threading_local = sys_modules['_threading_local']
42+
holding__threading_local = True
43+
del sys_modules['_threading_local']
44+
45+
import threading
46+
# Need a copy of the code kept somewhere...
47+
sys_modules['_dummy_threading'] = sys_modules['threading']
48+
del sys_modules['threading']
49+
sys_modules['_dummy__threading_local'] = sys_modules['_threading_local']
50+
del sys_modules['_threading_local']
51+
from _dummy_threading import *
52+
from _dummy_threading import __all__
53+
54+
finally:
55+
# Put back ``threading`` if we overwrote earlier
56+
57+
if holding_threading:
58+
sys_modules['threading'] = held_threading
59+
del held_threading
60+
del holding_threading
61+
62+
# Put back ``_threading_local`` if we overwrote earlier
63+
64+
if holding__threading_local:
65+
sys_modules['_threading_local'] = held__threading_local
66+
del held__threading_local
67+
del holding__threading_local
68+
69+
# Put back ``thread`` if we overwrote, else del the entry we made
70+
if holding_thread:
71+
sys_modules['_thread'] = held_thread
72+
del held_thread
73+
else:
74+
del sys_modules['_thread']
75+
del holding_thread
76+
77+
del _dummy_thread
78+
del sys_modules

0 commit comments

Comments
 (0)