Windows: Use 32-bit distribution of python
This commit is contained in:
@@ -1,22 +1,34 @@
|
||||
# Copyright (c) 2009-2012 Denis Bilenko. See LICENSE for details.
|
||||
from __future__ import absolute_import
|
||||
import sys
|
||||
# cython: auto_pickle=False,embedsignature=True,always_allow_keywords=False
|
||||
|
||||
from __future__ import absolute_import, print_function, division
|
||||
|
||||
from sys import _getframe as sys_getframe
|
||||
from sys import exc_info as sys_exc_info
|
||||
from weakref import ref as wref
|
||||
|
||||
# XXX: How to get cython to let us rename this as RawGreenlet
|
||||
# like we prefer?
|
||||
from greenlet import greenlet
|
||||
from gevent._compat import PY3
|
||||
from gevent._compat import PYPY
|
||||
from greenlet import GreenletExit
|
||||
|
||||
from gevent._compat import reraise
|
||||
from gevent._util import Lazy
|
||||
from gevent._compat import PYPY as _PYPY
|
||||
from gevent._tblib import dump_traceback
|
||||
from gevent._tblib import load_traceback
|
||||
from gevent.hub import GreenletExit
|
||||
from gevent.hub import InvalidSwitchError
|
||||
from gevent.hub import Waiter
|
||||
from gevent.hub import get_hub
|
||||
from gevent.hub import getcurrent
|
||||
from gevent.hub import iwait
|
||||
from gevent.hub import wait
|
||||
|
||||
from gevent.exceptions import InvalidSwitchError
|
||||
|
||||
from gevent._hub_primitives import iwait_on_objects as iwait
|
||||
from gevent._hub_primitives import wait_on_objects as wait
|
||||
|
||||
from gevent.timeout import Timeout
|
||||
from collections import deque
|
||||
|
||||
from gevent._config import config as GEVENT_CONFIG
|
||||
from gevent._util import Lazy
|
||||
from gevent._util import readproperty
|
||||
from gevent._hub_local import get_hub_noargs as get_hub
|
||||
from gevent import _waiter
|
||||
|
||||
|
||||
__all__ = [
|
||||
@@ -26,13 +38,23 @@ __all__ = [
|
||||
]
|
||||
|
||||
|
||||
if PYPY:
|
||||
# In Cython, we define these as 'cdef inline' functions. The
|
||||
# compilation unit cannot have a direct assignment to them (import
|
||||
# is assignment) without generating a 'lvalue is not valid target'
|
||||
# error.
|
||||
locals()['getcurrent'] = __import__('greenlet').getcurrent
|
||||
locals()['greenlet_init'] = lambda: None
|
||||
locals()['Waiter'] = _waiter.Waiter
|
||||
|
||||
|
||||
if _PYPY:
|
||||
import _continuation # pylint:disable=import-error
|
||||
_continulet = _continuation.continulet
|
||||
|
||||
|
||||
class SpawnedLink(object):
|
||||
"""A wrapper around link that calls it in another greenlet.
|
||||
"""
|
||||
A wrapper around link that calls it in another greenlet.
|
||||
|
||||
Can be called only from main loop.
|
||||
"""
|
||||
@@ -87,39 +109,80 @@ class FailureSpawnedLink(SpawnedLink):
|
||||
if not source.successful():
|
||||
return SpawnedLink.__call__(self, source)
|
||||
|
||||
class _Frame(object):
|
||||
|
||||
__slots__ = ('f_code', 'f_lineno', 'f_back')
|
||||
|
||||
def __init__(self, f_code, f_lineno, f_back):
|
||||
self.f_code = f_code
|
||||
self.f_lineno = f_lineno
|
||||
self.f_back = f_back
|
||||
|
||||
@property
|
||||
def f_globals(self):
|
||||
return None
|
||||
|
||||
def _Frame_from_list(frames):
|
||||
previous = None
|
||||
for frame in reversed(frames):
|
||||
f = _Frame(frame[0], frame[1], previous)
|
||||
previous = f
|
||||
return previous
|
||||
|
||||
def _extract_stack(limit):
|
||||
try:
|
||||
frame = sys_getframe()
|
||||
except ValueError:
|
||||
# In certain embedded cases that directly use the Python C api
|
||||
# to call Greenlet.spawn (e.g., uwsgi) this can raise
|
||||
# `ValueError: call stack is not deep enough`. This is because
|
||||
# the Cython stack frames for Greenlet.spawn ->
|
||||
# Greenlet.__init__ -> _extract_stack are all on the C level,
|
||||
# not the Python level.
|
||||
# See https://github.com/gevent/gevent/issues/1212
|
||||
frame = None
|
||||
|
||||
frames = []
|
||||
|
||||
while limit and frame is not None:
|
||||
limit -= 1
|
||||
frames.append((frame.f_code, frame.f_lineno))
|
||||
frame = frame.f_back
|
||||
|
||||
return frames
|
||||
|
||||
|
||||
_greenlet__init__ = greenlet.__init__
|
||||
|
||||
class Greenlet(greenlet):
|
||||
"""A light-weight cooperatively-scheduled execution unit.
|
||||
"""
|
||||
A light-weight cooperatively-scheduled execution unit.
|
||||
"""
|
||||
# pylint:disable=too-many-public-methods,too-many-instance-attributes
|
||||
|
||||
value = None
|
||||
_exc_info = ()
|
||||
_notifier = None
|
||||
|
||||
#: An event, such as a timer or a callback that fires. It is established in
|
||||
#: start() and start_later() as those two objects, respectively.
|
||||
#: Once this becomes non-None, the Greenlet cannot be started again. Conversely,
|
||||
#: kill() and throw() check for non-None to determine if this object has ever been
|
||||
#: scheduled for starting. A placeholder _dummy_event is assigned by them to prevent
|
||||
#: the greenlet from being started in the future, if necessary.
|
||||
_start_event = None
|
||||
args = ()
|
||||
_kwargs = None
|
||||
spawning_stack_limit = 10
|
||||
|
||||
# pylint:disable=keyword-arg-before-vararg,super-init-not-called
|
||||
def __init__(self, run=None, *args, **kwargs):
|
||||
"""
|
||||
Greenlet constructor.
|
||||
|
||||
:param args: The arguments passed to the ``run`` function.
|
||||
:param kwargs: The keyword arguments passed to the ``run`` function.
|
||||
:keyword run: The callable object to run. If not given, this object's
|
||||
:keyword callable run: The callable object to run. If not given, this object's
|
||||
`_run` method will be invoked (typically defined by subclasses).
|
||||
|
||||
.. versionchanged:: 1.1b1
|
||||
The ``run`` argument to the constructor is now verified to be a callable
|
||||
object. Previously, passing a non-callable object would fail after the greenlet
|
||||
was spawned.
|
||||
|
||||
.. versionchanged:: 1.3b1
|
||||
The ``GEVENT_TRACK_GREENLET_TREE`` configuration value may be set to
|
||||
a false value to disable ``spawn_tree_locals``, ``spawning_greenlet``,
|
||||
and ``spawning_stack``. The first two will be None in that case, and the
|
||||
latter will be empty.
|
||||
"""
|
||||
# The attributes are documented in the .rst file
|
||||
|
||||
# greenlet.greenlet(run=None, parent=None)
|
||||
# Calling it with both positional arguments instead of a keyword
|
||||
# argument (parent=get_hub()) speeds up creation of this object ~30%:
|
||||
@@ -128,7 +191,26 @@ class Greenlet(greenlet):
|
||||
# Python 3.4: 2.32usec with keywords vs 1.74usec with positional
|
||||
# Python 3.3: 2.55usec with keywords vs 1.92usec with positional
|
||||
# Python 2.7: 1.73usec with keywords vs 1.40usec with positional
|
||||
greenlet.__init__(self, None, get_hub())
|
||||
|
||||
# Timings taken Feb 21 2018 prior to integration of #755
|
||||
# python -m perf timeit -s 'import gevent' 'gevent.Greenlet()'
|
||||
# 3.6.4 : Mean +- std dev: 1.08 us +- 0.05 us
|
||||
# 2.7.14 : Mean +- std dev: 1.44 us +- 0.06 us
|
||||
# PyPy2 5.10.0: Mean +- std dev: 2.14 ns +- 0.08 ns
|
||||
|
||||
# After the integration of spawning_stack, spawning_greenlet,
|
||||
# and spawn_tree_locals on that same date:
|
||||
# 3.6.4 : Mean +- std dev: 8.92 us +- 0.36 us -> 8.2x
|
||||
# 2.7.14 : Mean +- std dev: 14.8 us +- 0.5 us -> 10.2x
|
||||
# PyPy2 5.10.0: Mean +- std dev: 3.24 us +- 0.17 us -> 1.5x
|
||||
|
||||
# Compiling with Cython gets us to these numbers:
|
||||
# 3.6.4 : Mean +- std dev: 3.63 us +- 0.14 us
|
||||
# 2.7.14 : Mean +- std dev: 3.37 us +- 0.20 us
|
||||
# PyPy2 5.10.0 : Mean +- std dev: 4.44 us +- 0.28 us
|
||||
|
||||
|
||||
_greenlet__init__(self, None, get_hub())
|
||||
|
||||
if run is not None:
|
||||
self._run = run
|
||||
@@ -139,21 +221,92 @@ class Greenlet(greenlet):
|
||||
if not callable(self._run):
|
||||
raise TypeError("The run argument or self._run must be callable")
|
||||
|
||||
if args:
|
||||
self.args = args
|
||||
if kwargs:
|
||||
self._kwargs = kwargs
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.value = None
|
||||
|
||||
@property
|
||||
def kwargs(self):
|
||||
return self._kwargs or {}
|
||||
#: An event, such as a timer or a callback that fires. It is established in
|
||||
#: start() and start_later() as those two objects, respectively.
|
||||
#: Once this becomes non-None, the Greenlet cannot be started again. Conversely,
|
||||
#: kill() and throw() check for non-None to determine if this object has ever been
|
||||
#: scheduled for starting. A placeholder _dummy_event is assigned by them to prevent
|
||||
#: the greenlet from being started in the future, if necessary.
|
||||
self._start_event = None
|
||||
|
||||
self._notifier = None
|
||||
self._formatted_info = None
|
||||
self._links = []
|
||||
self._ident = None
|
||||
|
||||
# Initial state: None.
|
||||
# Completed successfully: (None, None, None)
|
||||
# Failed with exception: (t, v, dump_traceback(tb)))
|
||||
self._exc_info = None
|
||||
|
||||
if GEVENT_CONFIG.track_greenlet_tree:
|
||||
spawner = getcurrent() # pylint:disable=undefined-variable
|
||||
self.spawning_greenlet = wref(spawner)
|
||||
try:
|
||||
self.spawn_tree_locals = spawner.spawn_tree_locals
|
||||
except AttributeError:
|
||||
self.spawn_tree_locals = {}
|
||||
if spawner.parent is not None:
|
||||
# The main greenlet has no parent.
|
||||
# Its children get separate locals.
|
||||
spawner.spawn_tree_locals = self.spawn_tree_locals
|
||||
|
||||
self._spawning_stack_frames = _extract_stack(self.spawning_stack_limit)
|
||||
self._spawning_stack_frames.extend(getattr(spawner, '_spawning_stack_frames', []))
|
||||
else:
|
||||
# None is the default for all of these in Cython, but we
|
||||
# need to declare them for pure-Python mode.
|
||||
self.spawning_greenlet = None
|
||||
self.spawn_tree_locals = None
|
||||
self._spawning_stack_frames = None
|
||||
|
||||
@Lazy
|
||||
def _links(self):
|
||||
return deque()
|
||||
def spawning_stack(self):
|
||||
# Store this in the __dict__. We don't use it from the C
|
||||
# code. It's tempting to discard _spawning_stack_frames
|
||||
# after this, but child greenlets may still be created
|
||||
# that need it.
|
||||
return _Frame_from_list(self._spawning_stack_frames or [])
|
||||
|
||||
def _has_links(self):
|
||||
return '_links' in self.__dict__ and self._links
|
||||
def _get_minimal_ident(self):
|
||||
reg = self.parent.ident_registry
|
||||
return reg.get_ident(self)
|
||||
|
||||
@property
|
||||
def minimal_ident(self):
|
||||
"""
|
||||
A small, unique integer that identifies this object.
|
||||
|
||||
This is similar to :attr:`threading.Thread.ident` (and `id`)
|
||||
in that as long as this object is alive, no other greenlet *in
|
||||
this hub* will have the same id, but it makes a stronger
|
||||
guarantee that the assigned values will be small and
|
||||
sequential. Sometime after this object has died, the value
|
||||
will be available for reuse.
|
||||
|
||||
To get ids that are unique across all hubs, combine this with
|
||||
the hub's ``minimal_ident``.
|
||||
|
||||
.. versionadded:: 1.3a2
|
||||
"""
|
||||
if self._ident is None:
|
||||
self._ident = self._get_minimal_ident()
|
||||
return self._ident
|
||||
|
||||
@readproperty
|
||||
def name(self):
|
||||
"""
|
||||
The greenlet name. By default, a unique name is constructed using
|
||||
the :attr:`minimal_ident`. You can assign a string to this
|
||||
value to change it. It is shown in the `repr` of this object.
|
||||
|
||||
.. versionadded:: 1.3a2
|
||||
"""
|
||||
return 'Greenlet-%d' % (self.minimal_ident)
|
||||
|
||||
def _raise_exception(self):
|
||||
reraise(*self.exc_info)
|
||||
@@ -163,50 +316,52 @@ class Greenlet(greenlet):
|
||||
# needed by killall
|
||||
return self.parent.loop
|
||||
|
||||
def __bool__(self):
|
||||
return self._start_event is not None and self._exc_info is Greenlet._exc_info
|
||||
__nonzero__ = __bool__
|
||||
def __nonzero__(self):
|
||||
return self._start_event is not None and self._exc_info is None
|
||||
try:
|
||||
__bool__ = __nonzero__ # Python 3
|
||||
except NameError: # pragma: no cover
|
||||
# When we're compiled with Cython, the __nonzero__ function
|
||||
# goes directly into the slot and can't be accessed by name.
|
||||
pass
|
||||
|
||||
### Lifecycle
|
||||
|
||||
if PYPY:
|
||||
if _PYPY:
|
||||
# oops - pypy's .dead relies on __nonzero__ which we overriden above
|
||||
@property
|
||||
def dead(self):
|
||||
"Boolean indicating that the greenlet is dead and will not run again."
|
||||
if self._greenlet__main:
|
||||
return False
|
||||
if self.__start_cancelled_by_kill or self.__started_but_aborted:
|
||||
if self.__start_cancelled_by_kill() or self.__started_but_aborted():
|
||||
return True
|
||||
|
||||
return self._greenlet__started and not _continulet.is_pending(self)
|
||||
else:
|
||||
@property
|
||||
def dead(self):
|
||||
return self.__start_cancelled_by_kill or self.__started_but_aborted or greenlet.dead.__get__(self)
|
||||
"Boolean indicating that the greenlet is dead and will not run again."
|
||||
return self.__start_cancelled_by_kill() or self.__started_but_aborted() or greenlet.dead.__get__(self)
|
||||
|
||||
@property
|
||||
def __never_started_or_killed(self):
|
||||
return self._start_event is None
|
||||
|
||||
@property
|
||||
def __start_pending(self):
|
||||
return (self._start_event is not None
|
||||
and (self._start_event.pending or getattr(self._start_event, 'active', False)))
|
||||
|
||||
@property
|
||||
def __start_cancelled_by_kill(self):
|
||||
return self._start_event is _cancelled_start_event
|
||||
|
||||
@property
|
||||
def __start_completed(self):
|
||||
return self._start_event is _start_completed_event
|
||||
|
||||
@property
|
||||
def __started_but_aborted(self):
|
||||
return (not self.__never_started_or_killed # we have been started or killed
|
||||
and not self.__start_cancelled_by_kill # we weren't killed, so we must have been started
|
||||
and not self.__start_completed # the start never completed
|
||||
and not self.__start_pending) # and we're not pending, so we must have been aborted
|
||||
return (not self.__never_started_or_killed() # we have been started or killed
|
||||
and not self.__start_cancelled_by_kill() # we weren't killed, so we must have been started
|
||||
and not self.__start_completed() # the start never completed
|
||||
and not self.__start_pending()) # and we're not pending, so we must have been aborted
|
||||
|
||||
def __cancel_start(self):
|
||||
if self._start_event is None:
|
||||
@@ -219,10 +374,11 @@ class Greenlet(greenlet):
|
||||
# variable copy of that list (in _run_callbacks). This isn't a problem,
|
||||
# except for the leak-tests.
|
||||
self._start_event.stop()
|
||||
self._start_event.close()
|
||||
|
||||
def __handle_death_before_start(self, *args):
|
||||
def __handle_death_before_start(self, args):
|
||||
# args is (t, v, tb) or simply t or v
|
||||
if self._exc_info is Greenlet._exc_info and self.dead:
|
||||
if self._exc_info is None and self.dead:
|
||||
# the greenlet was never switched to before and it will never be, _report_error was not called
|
||||
# the result was not set and the links weren't notified. let's do it here.
|
||||
# checking that self.dead is true is essential, because throw() does not necessarily kill the greenlet
|
||||
@@ -252,7 +408,7 @@ class Greenlet(greenlet):
|
||||
This function is only guaranteed to return true or false *values*, not
|
||||
necessarily the literal constants ``True`` or ``False``.
|
||||
"""
|
||||
return self.dead or self._exc_info
|
||||
return self.dead or self._exc_info is not None
|
||||
|
||||
def successful(self):
|
||||
"""
|
||||
@@ -266,34 +422,39 @@ class Greenlet(greenlet):
|
||||
.. note:: This function is only guaranteed to return true or false *values*,
|
||||
not necessarily the literal constants ``True`` or ``False``.
|
||||
"""
|
||||
return self._exc_info and self._exc_info[1] is None
|
||||
return self._exc_info is not None and self._exc_info[1] is None
|
||||
|
||||
def __repr__(self):
|
||||
classname = self.__class__.__name__
|
||||
result = '<%s at %s' % (classname, hex(id(self)))
|
||||
result = '<%s "%s" at %s' % (classname, self.name, hex(id(self)))
|
||||
formatted = self._formatinfo()
|
||||
if formatted:
|
||||
result += ': ' + formatted
|
||||
return result + '>'
|
||||
|
||||
_formatted_info = None
|
||||
|
||||
def _formatinfo(self):
|
||||
info = self._formatted_info
|
||||
if info is not None:
|
||||
return info
|
||||
|
||||
try:
|
||||
result = getfuncname(self.__dict__['_run'])
|
||||
except Exception: # pylint:disable=broad-except
|
||||
# Don't cache
|
||||
return ''
|
||||
# Are we running an arbitrary function provided to the constructor,
|
||||
# or did a subclass override _run?
|
||||
func = self._run
|
||||
im_self = getattr(func, '__self__', None)
|
||||
if im_self is self:
|
||||
funcname = '_run'
|
||||
elif im_self is not None:
|
||||
funcname = repr(func)
|
||||
else:
|
||||
funcname = getattr(func, '__name__', '') or repr(func)
|
||||
|
||||
result = funcname
|
||||
args = []
|
||||
if self.args:
|
||||
args = [repr(x)[:50] for x in self.args]
|
||||
if self._kwargs:
|
||||
args.extend(['%s=%s' % (key, repr(value)[:50]) for (key, value) in self._kwargs.items()])
|
||||
if self.kwargs:
|
||||
args.extend(['%s=%s' % (key, repr(value)[:50]) for (key, value) in self.kwargs.items()])
|
||||
if args:
|
||||
result += '(' + ', '.join(args) + ')'
|
||||
# it is important to save the result here, because once the greenlet exits '_run' attribute will be removed
|
||||
@@ -302,10 +463,11 @@ class Greenlet(greenlet):
|
||||
|
||||
@property
|
||||
def exception(self):
|
||||
"""Holds the exception instance raised by the function if the greenlet has finished with an error.
|
||||
Otherwise ``None``.
|
||||
"""
|
||||
return self._exc_info[1] if self._exc_info else None
|
||||
Holds the exception instance raised by the function if the
|
||||
greenlet has finished with an error. Otherwise ``None``.
|
||||
"""
|
||||
return self._exc_info[1] if self._exc_info is not None else None
|
||||
|
||||
@property
|
||||
def exc_info(self):
|
||||
@@ -317,12 +479,12 @@ class Greenlet(greenlet):
|
||||
|
||||
.. versionadded:: 1.1
|
||||
"""
|
||||
e = self._exc_info
|
||||
if e and e[0] is not None:
|
||||
return (e[0], e[1], load_traceback(e[2]))
|
||||
ei = self._exc_info
|
||||
if ei is not None and ei[0] is not None:
|
||||
return (ei[0], ei[1], load_traceback(ei[2]))
|
||||
|
||||
def throw(self, *args):
|
||||
"""Immediatelly switch into the greenlet and raise an exception in it.
|
||||
"""Immediately switch into the greenlet and raise an exception in it.
|
||||
|
||||
Should only be called from the HUB, otherwise the current greenlet is left unscheduled forever.
|
||||
To raise an exception in a safe manner from any greenlet, use :meth:`kill`.
|
||||
@@ -341,7 +503,7 @@ class Greenlet(greenlet):
|
||||
# LoopExit.
|
||||
greenlet.throw(self, *args)
|
||||
finally:
|
||||
self.__handle_death_before_start(*args)
|
||||
self.__handle_death_before_start(args)
|
||||
|
||||
def start(self):
|
||||
"""Schedule the greenlet to run in this loop iteration"""
|
||||
@@ -349,7 +511,12 @@ class Greenlet(greenlet):
|
||||
self._start_event = self.parent.loop.run_callback(self.switch)
|
||||
|
||||
def start_later(self, seconds):
|
||||
"""Schedule the greenlet to run in the future loop iteration *seconds* later"""
|
||||
"""
|
||||
start_later(seconds) -> None
|
||||
|
||||
Schedule the greenlet to run in the future loop iteration
|
||||
*seconds* later
|
||||
"""
|
||||
if self._start_event is None:
|
||||
self._start_event = self.parent.loop.timer(seconds)
|
||||
self._start_event.start(self.switch)
|
||||
@@ -357,6 +524,8 @@ class Greenlet(greenlet):
|
||||
@classmethod
|
||||
def spawn(cls, *args, **kwargs):
|
||||
"""
|
||||
spawn(function, *args, **kwargs) -> Greenlet
|
||||
|
||||
Create a new :class:`Greenlet` object and schedule it to run ``function(*args, **kwargs)``.
|
||||
This can be used as ``gevent.spawn`` or ``Greenlet.spawn``.
|
||||
|
||||
@@ -373,8 +542,10 @@ class Greenlet(greenlet):
|
||||
@classmethod
|
||||
def spawn_later(cls, seconds, *args, **kwargs):
|
||||
"""
|
||||
Create and return a new Greenlet object scheduled to run ``function(*args, **kwargs)``
|
||||
in the future loop iteration *seconds* later. This can be used as ``Greenlet.spawn_later``
|
||||
spawn_later(seconds, function, *args, **kwargs) -> Greenlet
|
||||
|
||||
Create and return a new `Greenlet` object scheduled to run ``function(*args, **kwargs)``
|
||||
in a future loop iteration *seconds* later. This can be used as ``Greenlet.spawn_later``
|
||||
or ``gevent.spawn_later``.
|
||||
|
||||
The arguments are passed to :meth:`Greenlet.__init__`.
|
||||
@@ -433,9 +604,9 @@ class Greenlet(greenlet):
|
||||
self.__cancel_start()
|
||||
|
||||
if self.dead:
|
||||
self.__handle_death_before_start(exception)
|
||||
self.__handle_death_before_start((exception,))
|
||||
else:
|
||||
waiter = Waiter() if block else None
|
||||
waiter = Waiter() if block else None # pylint:disable=undefined-variable
|
||||
self.parent.loop.run_callback(_kill, self, exception, waiter)
|
||||
if block:
|
||||
waiter.get()
|
||||
@@ -444,11 +615,17 @@ class Greenlet(greenlet):
|
||||
# thus it should not raise when the greenlet is already killed (= not started)
|
||||
|
||||
def get(self, block=True, timeout=None):
|
||||
"""Return the result the greenlet has returned or re-raise the exception it has raised.
|
||||
"""
|
||||
get(block=True, timeout=None) -> object
|
||||
|
||||
If block is ``False``, raise :class:`gevent.Timeout` if the greenlet is still alive.
|
||||
If block is ``True``, unschedule the current greenlet until the result is available
|
||||
or the timeout expires. In the latter case, :class:`gevent.Timeout` is raised.
|
||||
Return the result the greenlet has returned or re-raise the
|
||||
exception it has raised.
|
||||
|
||||
If block is ``False``, raise :class:`gevent.Timeout` if the
|
||||
greenlet is still alive. If block is ``True``, unschedule the
|
||||
current greenlet until the result is available or the timeout
|
||||
expires. In the latter case, :class:`gevent.Timeout` is
|
||||
raised.
|
||||
"""
|
||||
if self.ready():
|
||||
if self.successful():
|
||||
@@ -457,7 +634,7 @@ class Greenlet(greenlet):
|
||||
if not block:
|
||||
raise Timeout()
|
||||
|
||||
switch = getcurrent().switch
|
||||
switch = getcurrent().switch # pylint:disable=undefined-variable
|
||||
self.rawlink(switch)
|
||||
try:
|
||||
t = Timeout._start_new_or_dummy(timeout)
|
||||
@@ -482,13 +659,16 @@ class Greenlet(greenlet):
|
||||
self._raise_exception()
|
||||
|
||||
def join(self, timeout=None):
|
||||
"""Wait until the greenlet finishes or *timeout* expires.
|
||||
Return ``None`` regardless.
|
||||
"""
|
||||
join(timeout=None) -> None
|
||||
|
||||
Wait until the greenlet finishes or *timeout* expires. Return
|
||||
``None`` regardless.
|
||||
"""
|
||||
if self.ready():
|
||||
return
|
||||
|
||||
switch = getcurrent().switch
|
||||
switch = getcurrent().switch # pylint:disable=undefined-variable
|
||||
self.rawlink(switch)
|
||||
try:
|
||||
t = Timeout._start_new_or_dummy(timeout)
|
||||
@@ -509,7 +689,7 @@ class Greenlet(greenlet):
|
||||
def _report_result(self, result):
|
||||
self._exc_info = (None, None, None)
|
||||
self.value = result
|
||||
if self._has_links() and not self._notifier:
|
||||
if self._links and not self._notifier:
|
||||
self._notifier = self.parent.loop.run_callback(self._notify_links)
|
||||
|
||||
def _report_error(self, exc_info):
|
||||
@@ -519,7 +699,7 @@ class Greenlet(greenlet):
|
||||
|
||||
self._exc_info = exc_info[0], exc_info[1], dump_traceback(exc_info[2])
|
||||
|
||||
if self._has_links() and not self._notifier:
|
||||
if self._links and not self._notifier:
|
||||
self._notifier = self.parent.loop.run_callback(self._notify_links)
|
||||
|
||||
try:
|
||||
@@ -535,29 +715,38 @@ class Greenlet(greenlet):
|
||||
try:
|
||||
result = self._run(*self.args, **self.kwargs)
|
||||
except: # pylint:disable=bare-except
|
||||
self._report_error(sys.exc_info())
|
||||
self._report_error(sys_exc_info())
|
||||
return
|
||||
self._report_result(result)
|
||||
finally:
|
||||
self.__dict__.pop('_run', None)
|
||||
self.__dict__.pop('args', None)
|
||||
self.__dict__.pop('kwargs', None)
|
||||
self.args = ()
|
||||
self.kwargs.clear()
|
||||
|
||||
def _run(self):
|
||||
"""Subclasses may override this method to take any number of arguments and keyword arguments.
|
||||
"""
|
||||
Subclasses may override this method to take any number of
|
||||
arguments and keyword arguments.
|
||||
|
||||
.. versionadded:: 1.1a3
|
||||
Previously, if no callable object was passed to the constructor, the spawned greenlet would
|
||||
later fail with an AttributeError.
|
||||
Previously, if no callable object was
|
||||
passed to the constructor, the spawned greenlet would later
|
||||
fail with an AttributeError.
|
||||
"""
|
||||
# We usually override this in __init__
|
||||
# pylint: disable=method-hidden
|
||||
return
|
||||
|
||||
def rawlink(self, callback):
|
||||
"""Register a callable to be executed when the greenlet finishes execution.
|
||||
def has_links(self):
|
||||
return len(self._links)
|
||||
|
||||
The *callback* will be called with this instance as an argument.
|
||||
def rawlink(self, callback):
|
||||
"""
|
||||
Register a callable to be executed when the greenlet finishes
|
||||
execution.
|
||||
|
||||
The *callback* will be called with this instance as an
|
||||
argument.
|
||||
|
||||
.. caution:: The callable will be called in the HUB greenlet.
|
||||
"""
|
||||
@@ -588,6 +777,14 @@ class Greenlet(greenlet):
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def unlink_all(self):
|
||||
"""
|
||||
Remove all the callbacks.
|
||||
|
||||
.. versionadded:: 1.3a2
|
||||
"""
|
||||
del self._links[:]
|
||||
|
||||
def link_value(self, callback, SpawnedLink=SuccessSpawnedLink):
|
||||
"""
|
||||
Like :meth:`link` but *callback* is only notified when the greenlet
|
||||
@@ -597,22 +794,34 @@ class Greenlet(greenlet):
|
||||
self.link(callback, SpawnedLink=SpawnedLink)
|
||||
|
||||
def link_exception(self, callback, SpawnedLink=FailureSpawnedLink):
|
||||
"""Like :meth:`link` but *callback* is only notified when the greenlet dies because of an unhandled exception."""
|
||||
"""
|
||||
Like :meth:`link` but *callback* is only notified when the
|
||||
greenlet dies because of an unhandled exception.
|
||||
"""
|
||||
# pylint:disable=redefined-outer-name
|
||||
self.link(callback, SpawnedLink=SpawnedLink)
|
||||
|
||||
def _notify_links(self):
|
||||
while self._links:
|
||||
link = self._links.popleft() # pylint:disable=no-member
|
||||
# Early links are allowed to remove later links
|
||||
# before we get to them, and they're also allowed to
|
||||
# add new links, so we have to be careful about iterating.
|
||||
|
||||
# We don't expect this list to be very large, so the time spent
|
||||
# manipulating it should be small. a deque is probably not justified.
|
||||
# Cython has optimizations to transform this into a memmove anyway.
|
||||
link = self._links.pop(0)
|
||||
try:
|
||||
link(self)
|
||||
except: # pylint:disable=bare-except
|
||||
self.parent.handle_error((link, self), *sys.exc_info())
|
||||
self.parent.handle_error((link, self), *sys_exc_info())
|
||||
|
||||
|
||||
class _dummy_event(object):
|
||||
pending = False
|
||||
active = False
|
||||
__slots__ = ('pending', 'active')
|
||||
|
||||
def __init__(self):
|
||||
self.pending = self.active = False
|
||||
|
||||
def stop(self):
|
||||
pass
|
||||
@@ -620,10 +829,11 @@ class _dummy_event(object):
|
||||
def start(self, cb): # pylint:disable=unused-argument
|
||||
raise AssertionError("Cannot start the dummy event")
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
_cancelled_start_event = _dummy_event()
|
||||
_start_completed_event = _dummy_event()
|
||||
del _dummy_event
|
||||
|
||||
|
||||
def _kill(glet, exception, waiter):
|
||||
@@ -631,9 +841,9 @@ def _kill(glet, exception, waiter):
|
||||
glet.throw(exception)
|
||||
except: # pylint:disable=bare-except
|
||||
# XXX do we need this here?
|
||||
glet.parent.handle_error(glet, *sys.exc_info())
|
||||
glet.parent.handle_error(glet, *sys_exc_info())
|
||||
if waiter is not None:
|
||||
waiter.switch()
|
||||
waiter.switch(None)
|
||||
|
||||
|
||||
def joinall(greenlets, timeout=None, raise_error=False, count=None):
|
||||
@@ -666,7 +876,7 @@ def _killall3(greenlets, exception, waiter):
|
||||
try:
|
||||
g.throw(exception)
|
||||
except: # pylint:disable=bare-except
|
||||
g.parent.handle_error(g, *sys.exc_info())
|
||||
g.parent.handle_error(g, *sys_exc_info())
|
||||
if not g.dead:
|
||||
diehards.append(g)
|
||||
waiter.switch(diehards)
|
||||
@@ -678,7 +888,7 @@ def _killall(greenlets, exception):
|
||||
try:
|
||||
g.throw(exception)
|
||||
except: # pylint:disable=bare-except
|
||||
g.parent.handle_error(g, *sys.exc_info())
|
||||
g.parent.handle_error(g, *sys_exc_info())
|
||||
|
||||
|
||||
def killall(greenlets, exception=GreenletExit, block=True, timeout=None):
|
||||
@@ -713,7 +923,7 @@ def killall(greenlets, exception=GreenletExit, block=True, timeout=None):
|
||||
return
|
||||
loop = greenlets[0].loop
|
||||
if block:
|
||||
waiter = Waiter()
|
||||
waiter = Waiter() # pylint:disable=undefined-variable
|
||||
loop.run_callback(_killall3, greenlets, exception, waiter)
|
||||
t = Timeout._start_new_or_dummy(timeout)
|
||||
try:
|
||||
@@ -725,20 +935,10 @@ def killall(greenlets, exception=GreenletExit, block=True, timeout=None):
|
||||
else:
|
||||
loop.run_callback(_killall, greenlets, exception)
|
||||
|
||||
def _init():
|
||||
greenlet_init() # pylint:disable=undefined-variable
|
||||
|
||||
if PY3:
|
||||
_meth_self = "__self__"
|
||||
else:
|
||||
_meth_self = "im_self"
|
||||
_init()
|
||||
|
||||
|
||||
def getfuncname(func):
|
||||
if not hasattr(func, _meth_self):
|
||||
try:
|
||||
funcname = func.__name__
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
if funcname != '<lambda>':
|
||||
return funcname
|
||||
return repr(func)
|
||||
from gevent._util import import_c_accel
|
||||
import_c_accel(globals(), 'gevent._greenlet')
|
||||
|
||||
Reference in New Issue
Block a user