Home | History | Annotate | Download | only in Lib
      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
    164