_dummy_thread.py 4.67 KB
Newer Older
1 2 3 4 5 6
"""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::
Tim Peters's avatar
Tim Peters committed
7

8
    try:
9
        import _thread
10
    except ModuleNotFoundError:
11
        import _dummy_thread as _thread
12 13

"""
14 15
# Exports only things specified by thread documentation;
# 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 20
# A dummy value
TIMEOUT_MAX = 2**31
21

22 23 24 25 26
# 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 (??).

27
error = RuntimeError
28 29

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

    Compatibility is maintained by making sure that ``args`` is a
    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 36 37
    caught and nothing is done; all other exceptions are printed out
    by using traceback.print_exc().

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

41 42 43 44 45
    """
    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")
46 47
    global _main
    _main = False
48 49 50 51 52
    try:
        function(*args, **kwargs)
    except SystemExit:
        pass
    except:
53 54
        import traceback
        traceback.print_exc()
55 56
    _main = True
    global _interrupt
57 58 59
    if _interrupt:
        _interrupt = False
        raise KeyboardInterrupt
60 61

def exit():
62
    """Dummy implementation of _thread.exit()."""
63 64 65
    raise SystemExit

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

68
    Since this module should only be used when _threadmodule is not
69 70 71 72 73 74
    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():
75
    """Dummy implementation of _thread.allocate_lock()."""
76 77
    return LockType()

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

84
class LockType(object):
85
    """Class implementing dummy implementation of _thread.LockType.
Tim Peters's avatar
Tim Peters committed
86

87 88
    Compatibility is maintained by maintaining self.locked_status
    which is a boolean that stores the state of the lock.  Pickling of
89
    the lock, though, should not be done since if the _thread module is
90 91 92 93 94 95 96
    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
Tim Peters's avatar
Tim Peters committed
97

98
    def acquire(self, waitflag=None, timeout=-1):
99 100 101 102 103 104 105 106 107 108
        """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.

        """
109
        if waitflag is None or waitflag:
110
            self.locked_status = True
111 112
            return True
        else:
113 114 115 116
            if not self.locked_status:
                self.locked_status = True
                return True
            else:
117
                if timeout > 0:
118
                    import time
119
                    time.sleep(timeout)
120 121
                return False

122 123 124 125 126
    __enter__ = acquire

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

127 128 129 130 131 132 133 134 135 136 137
    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
138

139
# Used to signal that interrupt_main was called in a "thread"
140
_interrupt = False
141 142
# True when not executing in a "thread"
_main = True
143 144 145 146

def interrupt_main():
    """Set _interrupt flag to True to have start_new_thread raise
    KeyboardInterrupt upon exiting."""
147 148 149 150 151
    if _main:
        raise KeyboardInterrupt
    else:
        global _interrupt
        _interrupt = True