def acquire(self, blocking=True): """acquire ownership of the lock if the lock is already owned by the calling greenlet, a counter simply gets incremented. if it is owned by a different greenlet then it will block until the lock becomes available. :param blocking: whether to block if the lock is owned by a different greenlet (default ``True``) :type blocking: bool :returns: a `bool` indicating whether the lock was acquired. In the default case of ``blocking = True`` this will always be the case, but may not be otherwise. """ current = compat.getcurrent() if self._owner is current: self._count += 1 return True if self._locked and not blocking: return False if self._locked: self._waiters.append(compat.getcurrent()) scheduler.state.mainloop.switch() else: self._locked = True self._owner = current self._count = 1 return True
def local_exception_handler(handler=None, coro=None): """add a callback for when an exception occurs in a particular greenlet :param handler: the callback function, must be a function taking 3 arguments: - ``klass`` the exception class - ``exc`` the exception instance - ``tb`` the traceback object :type handler: function :param coro: the coroutine for which to apply the exception handler (defaults to the current coroutine) :type coro: greenlet """ if handler is None: return lambda h: local_exception_handler(h, coro) if not hasattr(handler, "__call__"): raise TypeError("exception handlers must be callable") if coro is None: coro = compat.getcurrent() log.info("setting a new coroutine local exception handler") state.local_exception_handlers.setdefault(coro, []).append( weakref.ref(handler)) return handler
def wait(self, timeout=None): """wait to be woken up by the condition .. note:: this method will block the current coroutine until a :meth:`notify` wakes it back up. :raises: `RuntimeError` if the underlying lock hasn't been :meth:`acquired <Lock.acquire>` """ if not self._is_owned(): raise RuntimeError("cannot wait on un-acquired lock") current = compat.getcurrent() waketime = None if timeout is None else time.time() + timeout if timeout is not None: scheduler.schedule_at(waketime, current) self._waiters.append((current, waketime)) self._lock.release() scheduler.state.mainloop.switch() self._lock.acquire() if timeout is not None: timedout = not scheduler._remove_timer(waketime, current) if timedout: self._waiters.remove((current, waketime)) return timedout return False
def local_outgoing_hook(handler=None, coro=None): """add a callback to run every time a greenlet is switched away from :param handler: the callback function, must be a function taking 2 arguments: - an integer indicating whether it is being called as an incoming (1) hook or as an outgoing (2) hook (in this case it will always be 2). - the coroutine being switched from (in this case it is the one indicated by the ``coro`` argument to ``local_outgoing_hook``. Be aware that only a weak reference to this function will be held. :type handler: function :param coro: the coroutine for which to apply the trace hook (defaults to current) :type coro: greenlet """ if handler is None: return lambda h: local_outgoing_hook(h, coro) if not hasattr(handler, "__call__"): raise TypeError("trace hooks must be callable") if coro is None: coro = compat.getcurrent() log.info("setting a coroutine local outgoing hook callback") state.local_from_hooks.setdefault(coro, []).append( weakref.ref(handler)) return handler
def wait(self, timeout=None): """pause the current coroutine until this event is set .. note:: this method will block the current coroutine if :meth:`set` has not been called. :param timeout: the maximum amount of time to block in seconds. the default of ``None`` allows indefinite blocking. :type timeout: number or None :returns: ``True`` if a timeout was provided and was hit, otherwise ``False`` """ if self._is_set: return False current = compat.getcurrent() # the waiting greenlet waketime = None if timeout is None else time.time() + timeout if timeout is not None: scheduler.schedule_at(waketime, current) self._waiters.append(current) scheduler.state.mainloop.switch() if timeout is not None: if not scheduler._remove_timer(waketime, current): self._waiters.remove(current) return True return False
def local_outgoing_hook(handler=None, coro=None): """add a callback to run every time a greenlet is switched away from :param handler: the callback function, must be a function taking 2 arguments: - an integer indicating whether it is being called as an incoming (1) hook or as an outgoing (2) hook (in this case it will always be 2). - the coroutine being switched from (in this case it is the one indicated by the ``coro`` argument to ``local_outgoing_hook``. Be aware that only a weak reference to this function will be held. :type handler: function :param coro: the coroutine for which to apply the trace hook (defaults to current) :type coro: greenlet """ if handler is None: return lambda h: local_outgoing_hook(h, coro) if not hasattr(handler, "__call__"): raise TypeError("trace hooks must be callable") if coro is None: coro = compat.getcurrent() log.info("setting a coroutine local outgoing hook callback") state.local_from_hooks.setdefault(coro, []).append(weakref.ref(handler)) return handler
def acquire(self, blocking=True): """decrement the counter, waiting if it is already at 0 .. note:: if the counter is already at 0, this method will block the current coroutine until a :meth:`release` increments it again. :param blocking: whether or not to block if the counter is already at 0 (default ``True``) :type blocking: bool :returns: a bool, indicating whether the count was decremented (this can only be ``False`` if ``blocking`` was ``False`` -- otherwise it would have blocked until it could decrement the counter) """ if self._value: self._value -= 1 return True if not blocking: return False self._waiters.append(compat.getcurrent()) scheduler.state.mainloop.switch() return True
def local_exception_handler(handler=None, coro=None): """add a callback for when an exception occurs in a particular greenlet :param handler: the callback function, must be a function taking 3 arguments: - ``klass`` the exception class - ``exc`` the exception instance - ``tb`` the traceback object :type handler: function :param coro: the coroutine for which to apply the exception handler (defaults to the current coroutine) :type coro: greenlet """ if handler is None: return lambda h: local_exception_handler(h, coro) if not hasattr(handler, "__call__"): raise TypeError("exception handlers must be callable") if coro is None: coro = compat.getcurrent() log.info("setting a new coroutine local exception handler") state.local_exception_handlers.setdefault(coro, []).append(weakref.ref(handler)) return handler
def pause(): """pause and reschedule the current greenlet and switch to the next .. note:: this method blocks for a short period of time """ schedule(compat.getcurrent()) state.mainloop.switch()
def local_incoming_hook(handler=None, coro=None): """add a callback to run every time a greenlet is about to be switched to :param handler: the callback function, must be a function taking 2 arguments: an integer indicating whether it is being called as an incoming (1) hook or as an outgoing (2) hook. in this case it will always be 1. be aware that only a weak reference to this function will be held. :type handler: function :param coro: the coroutine for which to apply the trace hook (defaults to current) :type coro: greenlet """ if handler is None: return lambda h: local_incoming_hook(h, coro) if not hasattr(handler, "__call__"): raise TypeError("trace hooks must be callable") if coro is None: coro = compat.getcurrent() state.local_to_hooks.setdefault(coro, []).append( weakref.ref(handler)) return handler
def acquire(self, blocking=True): """lock the lock, blocking until it becomes available .. note:: this method will block the current coroutine if the lock is not already owned. :param blocking: whether to block if the lock is already owned (default ``True``) :type blocking: bool :returns: a `bool` indicating whether the lock was acquired. In the default case of ``blocking = True`` this will always be the case, but may not be otherwise. """ current = compat.getcurrent() if not blocking: locked_already = self._locked if not locked_already: self._locked = True self._owner = current return not locked_already if self._locked: self._waiters.append(current) scheduler.state.mainloop.switch() else: self._locked = True self._owner = current return True
def __getattr__(self, name): current = compat.getcurrent() if current is compat.main_greenlet: current = self._main_standin local = self._local_data.setdefault(current, {}) if name not in local: raise AttributeError("Local object has no attribute %s" % name) return local[name]
def pause_until(unixtime): """pause and reschedule the current greenlet until a set time :param unixtime: the unix timestamp of when to bring this greenlet back :type unixtime: int or float """ schedule_at(unixtime, compat.getcurrent()) state.mainloop.switch()
def __getattr__(self, name): current = compat.getcurrent() if current is compat.main_greenlet: current = self._main_standin local = self._local_data.setdefault(current, {}) if name not in local: raise AttributeError, "Local object has no attribute %s" % name return local[name]
def pause_until(unixtime): """pause and reschedule the current greenlet until a set time .. note:: this method will block the current greenlet :param unixtime: the unix timestamp of when to bring this greenlet back :type unixtime: int or float """ schedule_at(unixtime, compat.getcurrent()) state.mainloop.switch()
def handle_exception(klass, exc, tb, coro=None): """run all the registered exception handlers the first 3 arguments to this function match the output of ``sys.exc_info()`` :param klass: the exception klass :type klass: type :param exc: the exception instance :type exc: Exception :param tb: the traceback object :type tb: Traceback :param coro: behave as though the exception occurred in this coroutine (defaults to the current coroutine) :type coro: greenlet exception handlers run would be all those added with :func:`global_exception_handler`, and any added for the relevant coroutine with :func:`local_exception_handler`. """ if coro is None: coro = compat.getcurrent() replacement = [] for weak in state.local_exception_handlers.get(coro, ()): func = weak() if func is None: continue try: func(klass, exc, tb) except Exception: continue replacement.append(weak) if replacement: state.local_exception_handlers[coro][:] = replacement replacement = [] for weak in state.global_exception_handlers: func = weak() if func is None: continue try: func(klass, exc, tb) except Exception: continue replacement.append(weak) state.global_exception_handlers[:] = replacement
def wait(self, until=0): """wait until the count has reached a particular number .. note:: this method can block the current greenlet :param until: the number to wait for the count to get down (or up) to. default 0 :type until: int """ if self._count != until: self._waiters.setdefault(until, []).append(compat.getcurrent()) scheduler.state.mainloop.switch()
def put(self, item, block=True, timeout=None): """put an item into the queue .. note:: if the queue was created with a `maxsize` and it is currently :meth:`full`, this method will block the calling coroutine until another coroutine :meth:`get`\ s an item. :param item: the object to put into the queue, can be any type :param block: whether to block if the queue is already :meth:`full` (default ``True``) :type block: bool :param timeout: the maximum time in seconds to block waiting. with the default of ``None``, it can wait indefinitely. this is unused if `block` is ``False``. :type timeout: int, float or None :raises: :class:`Full` if the queue is :meth:`full` and `block` is ``False``, or if `timeout` expires. """ if self.full(): if not block: raise Full() current = compat.getcurrent() waketime = None if timeout is None else time.time() + timeout if timeout is not None: scheduler.schedule_at(waketime, current) self._waiters.append((current, waketime)) scheduler.state.mainloop.switch() if timeout is not None: if not scheduler._remove_timer(waketime, current): self._waiters.remove((current, waketime)) raise Full() if self._waiters and not self.full(): scheduler.schedule(self._waiters.popleft()[0]) if not self._open_tasks: self._jobs_done.clear() self._open_tasks += 1 self._put(item)
def put(self, item, blocking=True, timeout=None): """put an item into the queue .. note:: if the queue was created with a `maxsize` and it is currently :meth:`full`, this method will block the calling coroutine until another coroutine :meth:`get`\ s an item. :param item: the object to put into the queue, can be any type :param blocking: whether to block if the queue is already :meth:`full` (default ``True``) :type blocking: bool :param timeout: the maximum time in seconds to block waiting. with the default of ``None``, it can wait indefinitely. this is unused if `blocking` is ``False``. :type timeout: int, float or None :raises: :class:`Full` if the queue is :meth:`full` and `blocking` is ``False``, or if `timeout` expires. """ if self.full(): if not blocking: raise Full() current = compat.getcurrent() waketime = None if timeout is None else time.time() + timeout if timeout is not None: scheduler.schedule_at(waketime, current) self._waiters.append((current, waketime)) scheduler.state.mainloop.switch() if timeout is not None: if not scheduler._remove_timer(waketime, current): self._waiters.remove((current, waketime)) raise Full() if self._waiters and not self.full(): scheduler.schedule(self._waiters.popleft()[0]) if not self._open_tasks: self._jobs_done.clear() self._open_tasks += 1 self._put(item)
def acquire(self, blocking=True): """acquire ownership of the lock if the lock is already owned by the calling greenlet, a counter simply gets incremented. if it is owned by a different greenlet then it will block until the lock becomes available. .. note:: this method will block the current coroutine if the lock is not already owned by another coroutine. :param blocking: whether to block if the lock is owned by a different greenlet (default ``True``) :type blocking: bool :returns: a `bool` indicating whether the lock was acquired. In the default case of ``blocking = True`` this will always be the case, but may not be otherwise. """ current = compat.getcurrent() if self._owner is current: self._count += 1 return True if self._locked and not blocking: return False if self._locked: self._waiters.append(compat.getcurrent()) scheduler.state.mainloop.switch() else: self._locked = True self._owner = current self._count = 1 return True
def get(self, block=True, timeout=None): """get an item out of the queue .. note:: if `block` is ``True`` (the default) and the queue is :meth`empty`, this method will block the current coroutine until something has been :meth:`put`. :param block: whether to block if there is no data yet available (default ``True``) :type block: bool :param timeout: the maximum time in seconds to block waiting for data. with the default of ``None``, can wait indefinitely. this is unused if `block` is ``False``. :type timeout: int, float or None :raises: :class:`Empty` if there is no data in the queue and block is ``False``, or `timeout` expires :returns: something that was previously :meth:`put` in the queue """ if not self._data: if not block: raise Empty() current = compat.getcurrent() waketime = None if timeout is None else time.time() + timeout if timeout is not None: scheduler.schedule_at(waketime, current) self._waiters.append((current, waketime)) scheduler.state.mainloop.switch() if timeout is not None: if not scheduler._remove_timer(waketime, current): self._waiters.remove((current, waketime)) raise Empty() if self.full() and self._waiters: scheduler.schedule(self._waiters.popleft()[0]) return self._get()
def get(self, blocking=True, timeout=None): """get an item out of the queue .. note:: if `blocking` is ``True`` (the default) and the queue is :meth`empty`, this method will block the current coroutine until something has been :meth:`put`. :param blocking: whether to block if there is no data yet available (default ``True``) :type blocking: bool :param timeout: the maximum time in seconds to block waiting for data. with the default of ``None``, can wait indefinitely. this is unused if `blocking` is ``False``. :type timeout: int, float or None :raises: :class:`Empty` if there is no data in the queue and blocking is ``False``, or `timeout` expires :returns: something that was previously :meth:`put` in the queue """ if not self._data: if not blocking: raise Empty() current = compat.getcurrent() waketime = None if timeout is None else time.time() + timeout if timeout is not None: scheduler.schedule_at(waketime, current) self._waiters.append((current, waketime)) scheduler.state.mainloop.switch() if timeout is not None: if not scheduler._remove_timer(waketime, current): self._waiters.remove((current, waketime)) raise Empty() if self.full() and self._waiters: scheduler.schedule(self._waiters.popleft()[0]) return self._get()
def join(self, timeout=None): """block until this thread terminates :param timeout: the maximum time to wait. with the default of ``None``, waits indefinitely :type timeout: int, float or None :raises: `RuntimeError` if called inside the thread, or it has not yet been started """ if not self._started: raise RuntimeError("cannot join thread before it is started") if compat.getcurrent() is self._glet: raise RuntimeError("cannot join current thread") self._finished.wait(timeout)
def remove_local_exception_handler(handler, coro=None): """remove a callback from the list of exception handlers for a coroutine :param handler: the callback to remove :type handler: function :param coro: the coroutine for which to remove the local handler :type coro: greenlet :returns: bool, whether the handler was found (and therefore removed) """ if coro is None: coro = compat.getcurrent() for i, cb in enumerate(state.local_exception_handlers.get(coro, [])): cb = cb() if cb is not None and cb is handler: state.local_exception_handlers[coro].pop(i) return True return False
def remove_local_outgoing_hook(handler, coro=None): """remove a callback from the outgoing hooks for a particular coro :param handler: the callback previously added via local_outgoing_hook :type handler: function :param coro: the coroutine for which the hook should be removed :type coro: greenlet :returns: bool, whether the handler was found and removed """ if coro is None: coro = compat.getcurrent() for i, cb in enumerate(state.local_from_hooks.get(coro, [])): cb = cb() if cb is not None and cb is handler: state.local_from_hooks[coro].pop(i) return True return False
def remove_local_exception_handler(handler, coro=None): """remove a callback from the list of exception handlers for a coroutine :param handler: the callback to remove :type handler: function :param coro: the coroutine for which to remove the local handler :type coro: greenlet :returns: bool, whether the handler was found (and therefore removed) """ if coro is None: coro = compat.getcurrent() for i, cb in enumerate(state.local_exception_handlers.get(coro, [])): cb = cb() if cb is not None and cb is handler: state.local_exception_handlers[coro].pop(i) log.info("removing a coroutine local exception handler") return True return False
def remove_local_outgoing_hook(handler, coro=None): """remove a callback from the outgoing hooks for a particular coro :param handler: the callback previously added via local_outgoing_hook :type handler: function :param coro: the coroutine for which the hook should be removed :type coro: greenlet :returns: bool, whether the handler was found and removed """ if coro is None: coro = compat.getcurrent() for i, cb in enumerate(state.local_from_hooks.get(coro, [])): cb = cb() if cb is not None and cb is handler: log.info("removing a coroutine outgoing local hook callback") state.local_from_hooks[coro].pop(i) return True return False
def join(self, timeout=None): """block until this thread terminates .. note:: this method can block the calling coroutine if the thread has not yet completed. :param timeout: the maximum time to wait. with the default of ``None``, waits indefinitely :type timeout: int, float or None :raises: `RuntimeError` if called inside the thread, or it has not yet been started """ if not self._started: raise RuntimeError("cannot join thread before it is started") if compat.getcurrent() is self._glet: raise RuntimeError("cannot join current thread") self._finished.wait(timeout)
def release(self): """release one ownership of the lock if the calling greenlet has :meth:`acquired <acquire>` the lock more than once this will simply decrement the counter. if this is a final release then a waiting greenlet is awoken :raises: `RuntimeError` if the calling greenlet is not the lock's owner """ if not self._locked or self._owner is not compat.getcurrent(): raise RuntimeError("cannot release un-acquired lock") self._count -= 1 if self._count == 0: self._owner = None if self._waiters: waiter = self._waiters.popleft() self._locked = True self._owner = waiter scheduler.state.awoken_from_events.add(waiter) else: self._locked = False self._owner = None
def wait(self, timeout=None): """pause the current coroutine until this event is set .. note:: this method will block the current coroutine if :meth:`set` has not been called. :param timeout: the maximum amount of time to block in seconds. the default of ``None`` allows indefinite blocking. :type timeout: number or None :returns: ``True`` if a timeout was provided and was hit, otherwise ``False`` """ if self._is_set: return False current = compat.getcurrent() # the waiting greenlet waketime = None if timeout is None else time.time() + timeout if timeout is not None: scheduler.schedule_at(waketime, current) self._waiters.append(current) scheduler.state.mainloop.switch() if timeout is not None: if not scheduler._remove_timer(waketime, current): scheduler.state.awoken_from_events.discard(current) if current in self._waiters: self._waiters.remove(current) return True return False
def _current_thread(): if compat.getcurrent() is compat.main_greenlet: return _main_thread return Thread._active.get(compat.getcurrent(), _dummy_thread)
def __setattr__(self, name, value): current = compat.getcurrent() if current is compat.main_greenlet: current = self._main_standin self._local_data.setdefault(current, {})[name] = value
def _is_owned(self): return self._owner is compat.getcurrent()
def pause(): "pause and reschedule the current greenlet and switch to the next" schedule(compat.getcurrent()) state.mainloop.switch()