Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 7 additions & 10 deletions Doc/library/_dummy_thread.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,15 @@

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

--------------

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

Suggested usage is::
--------------

try:
import _thread
except ImportError:
import _dummy_thread as _thread
This module provides a duplicate interface to the :mod:`_thread` module.
It was meant to be imported when the :mod:`_thread` module was not provided
on a platform.

Be careful to not use this module where deadlock might occur from a thread being
created that blocks waiting for another thread to be created. This often occurs
Expand Down
10 changes: 3 additions & 7 deletions Doc/library/_thread.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,10 @@ threading API built on top of this module.
single: pthreads
pair: threads; POSIX

The module is optional. It is supported on Windows, Linux, SGI IRIX, Solaris
2.x, as well as on systems that have a POSIX thread (a.k.a. "pthread")
implementation. For systems lacking the :mod:`_thread` module, the
:mod:`_dummy_thread` module is available. It duplicates this module's interface
and can be used as a drop-in replacement.

It defines the following constants and functions:
.. versionchanged:: 3.7
This module used to be optional, it is now always available.

This module defines the following constants and functions:

.. exception:: error

Expand Down
2 changes: 1 addition & 1 deletion Doc/library/concurrency.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ The following are support modules for some of the above services:

.. toctree::

dummy_threading.rst
_thread.rst
_dummy_thread.rst
dummy_threading.rst
17 changes: 6 additions & 11 deletions Doc/library/dummy_threading.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,15 @@

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

--------------

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

Suggested usage is::
--------------

try:
import threading
except ImportError:
import dummy_threading as threading
This module provides a duplicate interface to the :mod:`threading` module.
It was meant to be imported when the :mod:`_thread` module was not provided
on a platform.

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

4 changes: 2 additions & 2 deletions Doc/library/threading.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
This module constructs higher-level threading interfaces on top of the lower
level :mod:`_thread` module. See also the :mod:`queue` module.

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

.. note::

Expand Down
163 changes: 163 additions & 0 deletions Lib/_dummy_thread.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
"""Drop-in replacement for the thread module.

Meant to be used as a brain-dead substitute so that threaded code does
not need to be rewritten for when the thread module is not present.

Suggested usage is::

try:
import _thread
except ImportError:
import _dummy_thread as _thread

"""
# Exports only things specified by thread documentation;
# skipping obsolete synonyms allocate(), start_new(), exit_thread().
__all__ = ['error', 'start_new_thread', 'exit', 'get_ident', 'allocate_lock',
'interrupt_main', 'LockType']

# A dummy value
TIMEOUT_MAX = 2**31

# NOTE: this module can be imported early in the extension building process,
# and so top level imports of other modules should be avoided. Instead, all
# imports are done when needed on a function-by-function basis. Since threads
# are disabled, the import lock should not be an issue anyway (??).

error = RuntimeError

def start_new_thread(function, args, kwargs={}):
"""Dummy implementation of _thread.start_new_thread().

Compatibility is maintained by making sure that ``args`` is a
tuple and ``kwargs`` is a dictionary. If an exception is raised
and it is SystemExit (which can be done by _thread.exit()) it is
caught and nothing is done; all other exceptions are printed out
by using traceback.print_exc().

If the executed function calls interrupt_main the KeyboardInterrupt will be
raised when the function returns.

"""
if type(args) != type(tuple()):
raise TypeError("2nd arg must be a tuple")
if type(kwargs) != type(dict()):
raise TypeError("3rd arg must be a dict")
global _main
_main = False
try:
function(*args, **kwargs)
except SystemExit:
pass
except:
import traceback
traceback.print_exc()
_main = True
global _interrupt
if _interrupt:
_interrupt = False
raise KeyboardInterrupt

def exit():
"""Dummy implementation of _thread.exit()."""
raise SystemExit

def get_ident():
"""Dummy implementation of _thread.get_ident().

Since this module should only be used when _threadmodule is not
available, it is safe to assume that the current process is the
only thread. Thus a constant can be safely returned.
"""
return 1

def allocate_lock():
"""Dummy implementation of _thread.allocate_lock()."""
return LockType()

def stack_size(size=None):
"""Dummy implementation of _thread.stack_size()."""
if size is not None:
raise error("setting thread stack size not supported")
return 0

def _set_sentinel():
"""Dummy implementation of _thread._set_sentinel()."""
return LockType()

class LockType(object):
"""Class implementing dummy implementation of _thread.LockType.

Compatibility is maintained by maintaining self.locked_status
which is a boolean that stores the state of the lock. Pickling of
the lock, though, should not be done since if the _thread module is
then used with an unpickled ``lock()`` from here problems could
occur from this class not having atomic methods.

"""

def __init__(self):
self.locked_status = False

def acquire(self, waitflag=None, timeout=-1):
"""Dummy implementation of acquire().

For blocking calls, self.locked_status is automatically set to
True and returned appropriately based on value of
``waitflag``. If it is non-blocking, then the value is
actually checked and not set if it is already acquired. This
is all done so that threading.Condition's assert statements
aren't triggered and throw a little fit.

"""
if waitflag is None or waitflag:
self.locked_status = True
return True
else:
if not self.locked_status:
self.locked_status = True
return True
else:
if timeout > 0:
import time
time.sleep(timeout)
return False

__enter__ = acquire

def __exit__(self, typ, val, tb):
self.release()

def release(self):
"""Release the dummy lock."""
# XXX Perhaps shouldn't actually bother to test? Could lead
# to problems for complex, threaded code.
if not self.locked_status:
raise error
self.locked_status = False
return True

def locked(self):
return self.locked_status

def __repr__(self):
return "<%s %s.%s object at %s>" % (
"locked" if self.locked_status else "unlocked",
self.__class__.__module__,
self.__class__.__qualname__,
hex(id(self))
)

# Used to signal that interrupt_main was called in a "thread"
_interrupt = False
# True when not executing in a "thread"
_main = True

def interrupt_main():
"""Set _interrupt flag to True to have start_new_thread raise
KeyboardInterrupt upon exiting."""
if _main:
raise KeyboardInterrupt
else:
global _interrupt
_interrupt = True
78 changes: 78 additions & 0 deletions Lib/dummy_threading.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""Faux ``threading`` version using ``dummy_thread`` instead of ``thread``.

The module ``_dummy_threading`` is added to ``sys.modules`` in order
to not have ``threading`` considered imported. Had ``threading`` been
directly imported it would have made all subsequent imports succeed
regardless of whether ``_thread`` was available which is not desired.

"""
from sys import modules as sys_modules

import _dummy_thread

# Declaring now so as to not have to nest ``try``s to get proper clean-up.
holding_thread = False
holding_threading = False
holding__threading_local = False

try:
# Could have checked if ``_thread`` was not in sys.modules and gone
# a different route, but decided to mirror technique used with
# ``threading`` below.
if '_thread' in sys_modules:
held_thread = sys_modules['_thread']
holding_thread = True
# Must have some module named ``_thread`` that implements its API
# in order to initially import ``threading``.
sys_modules['_thread'] = sys_modules['_dummy_thread']

if 'threading' in sys_modules:
# If ``threading`` is already imported, might as well prevent
# trying to import it more than needed by saving it if it is
# already imported before deleting it.
held_threading = sys_modules['threading']
holding_threading = True
del sys_modules['threading']

if '_threading_local' in sys_modules:
# If ``_threading_local`` is already imported, might as well prevent
# trying to import it more than needed by saving it if it is
# already imported before deleting it.
held__threading_local = sys_modules['_threading_local']
holding__threading_local = True
del sys_modules['_threading_local']

import threading
# Need a copy of the code kept somewhere...
sys_modules['_dummy_threading'] = sys_modules['threading']
del sys_modules['threading']
sys_modules['_dummy__threading_local'] = sys_modules['_threading_local']
del sys_modules['_threading_local']
from _dummy_threading import *
from _dummy_threading import __all__

finally:
# Put back ``threading`` if we overwrote earlier

if holding_threading:
sys_modules['threading'] = held_threading
del held_threading
del holding_threading

# Put back ``_threading_local`` if we overwrote earlier

if holding__threading_local:
sys_modules['_threading_local'] = held__threading_local
del held__threading_local
del holding__threading_local

# Put back ``thread`` if we overwrote, else del the entry we made
if holding_thread:
sys_modules['_thread'] = held_thread
del held_thread
else:
del sys_modules['_thread']
del holding_thread

del _dummy_thread
del sys_modules
Loading