"""Implements the standard threading module, using greenthreads.""" import eventlet from eventlet.green import thread from eventlet.green import time from eventlet.support import greenlets as greenlet import six __patched__ = ['_start_new_thread', '_allocate_lock', '_sleep', 'local', 'stack_size', 'Lock', 'currentThread', 'current_thread', '_after_fork', '_shutdown'] if six.PY2: __patched__ += ['_get_ident'] else: __patched__ += ['get_ident', '_set_sentinel'] __orig_threading = eventlet.patcher.original('threading') __threadlocal = __orig_threading.local() __patched_enumerate = None eventlet.patcher.inject( 'threading', globals(), ('thread' if six.PY2 else '_thread', thread), ('time', time)) _count = 1 class _GreenThread(object): """Wrapper for GreenThread objects to provide Thread-like attributes and methods""" def __init__(self, g): global _count self._g = g self._name = 'GreenThread-%d' % _count _count += 1 def __repr__(self): return '<_GreenThread(%s, %r)>' % (self._name, self._g) def join(self, timeout=None): return self._g.wait() def getName(self): return self._name get_name = getName def setName(self, name): self._name = str(name) set_name = setName name = property(getName, setName) ident = property(lambda self: id(self._g)) def isAlive(self): return True is_alive = isAlive daemon = property(lambda self: True) def isDaemon(self): return self.daemon is_daemon = isDaemon __threading = None def _fixup_thread(t): # Some third-party packages (lockfile) will try to patch the # threading.Thread class with a get_name attribute if it doesn't # exist. Since we might return Thread objects from the original # threading package that won't get patched, let's make sure each # individual object gets patched too our patched threading.Thread # class has been patched. This is why monkey patching can be bad... global __threading if not __threading: __threading = __import__('threading') if (hasattr(__threading.Thread, 'get_name') and not hasattr(t, 'get_name')): t.get_name = t.getName return t def current_thread(): global __patched_enumerate g = greenlet.getcurrent() if not g: # Not currently in a greenthread, fall back to standard function return _fixup_thread(__orig_threading.current_thread()) try: active = __threadlocal.active except AttributeError: active = __threadlocal.active = {} g_id = id(g) t = active.get(g_id) if t is not None: return t # FIXME: move import from function body to top # (jaketesler@github) Furthermore, I was unable to have the current_thread() return correct results from # threading.enumerate() unless the enumerate() function was a) imported at runtime using the gross __import__() call # and b) was hot-patched using patch_function(). # https://github.com/eventlet/eventlet/issues/172#issuecomment-379421165 if __patched_enumerate is None: __patched_enumerate = eventlet.patcher.patch_function(__import__('threading').enumerate) found = [th for th in __patched_enumerate() if th.ident == g_id] if found: return found[0] # Add green thread to active if we can clean it up on exit def cleanup(g): del active[g_id] try: g.link(cleanup) except AttributeError: # Not a GreenThread type, so there's no way to hook into # the green thread exiting. Fall back to the standard # function then. t = _fixup_thread(__orig_threading.current_thread()) else: t = active[g_id] = _GreenThread(g) return t currentThread = current_thread