1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
|
==================================
Creating And Executing Greenlets
==================================
.. This document is a mess. It's a cross between how-to and API
reference.
.. currentmodule:: greenlet
To create a new greenlet, simply instantiate a new object of class
:class:`greenlet.greenlet`, passing it the initial function to run.
.. tip::
If you're using a framework built on greenlets, such as
:mod:`gevent`, consult its documentation. Some frameworks have
other ways of creating new greenlets (for example,
:func:`gevent.spawn`) or prefer a different greenlet class (for
example, :class:`gevent.Greenlet`).
.. doctest::
>>> import greenlet
>>> def run():
... print("Running in the greenlet function.")
>>> glet = greenlet.greenlet(run)
The greenlet will have its ``run`` attribute set to the function you
passed, and its :ref:`parent <greenlet_parents>` will be the
:func:`current greenlet <getcurrent>`.
.. doctest::
>>> glet.run is run
True
>>> glet.parent is greenlet.getcurrent()
True
Execution of the greenlet begins when :meth:`greenlet.switch` is
called on it.
.. doctest::
>>> glet.switch()
Running in the greenlet function.
The ``run`` attribute is deleted at that time.
.. doctest::
>>> glet.run
Traceback (most recent call last):
...
AttributeError: run
.. _subclassing_greenlet:
Subclassing greenlet
====================
You can also subclass :class:`greenlet.greenlet` and define ``run`` as
a method. This is useful to store additional state with the greenlet.
.. doctest::
>>> import time
>>> class MyGreenlet(greenlet.greenlet):
... created_at = None
... finished_at = None
... def run(self):
... self.created_at = time.time()
... print("Running in the greenlet subclass.")
... self.finished_at = time.time()
>>> glet = MyGreenlet()
>>> glet.switch()
Running in the greenlet subclass.
>>> glet.finished_at >= glet.created_at
True
See :ref:`switching` for more information about switching into greenlets.
.. _changing_the_parent:
Changing The Parent
===================
When a greenlet finishes, :ref:`execution resumes with its parent
<greenlet_parents>`. This defaults to the current greenlet when the
object was instantiated, but can be changed either at that time or any
time later. To set it at creation time, pass the desired parent as the
second argument:
.. doctest::
>>> def parent(child_result):
... print("In the parent.")
>>> parent_glet = greenlet.greenlet(parent)
>>> def child():
... print("In the child.")
>>> child_glet = greenlet.greenlet(child, parent_glet)
>>> child_glet.switch()
In the child.
In the parent.
To change it later, assign to the ``greenlet.parent`` attribute.
.. doctest::
>>> parent_glet = greenlet.greenlet(parent)
>>> child_glet = greenlet.greenlet(child)
>>> child_glet.parent = parent_glet
>>> child_glet.switch()
In the child.
In the parent.
Of course, cycles are not permitted.
.. doctest::
>>> parent_glet = greenlet.greenlet(parent)
>>> child_glet = greenlet.greenlet(child)
>>> child_glet.parent = parent_glet
>>> parent_glet.parent = child_glet
Traceback (most recent call last):
...
ValueError: cyclic parent chain
The parent must be a greenlet.
.. doctest::
>>> parent_glet.parent = 42
Traceback (most recent call last):
...
TypeError: parent must be a greenlet
Interrupting Greenlets by Throwing Exceptions
=============================================
Besides simply :meth:`switching <greenlet.switch>` into a greenlet,
you can also have it resume execution by throwing an exception into
it. This is useful to interrupt a loop in the greenlet, for instance.
.. doctest::
>>> main = greenlet.getcurrent()
>>> class MyException(Exception):
... pass
>>> def run():
... try:
... main.switch()
... except MyException:
... print("Caught exception in greenlet.")
>>> glet = greenlet.greenlet(run)
>>> _ = glet.switch()
>>> glet.throw(MyException)
Caught exception in greenlet.
Uncaught exceptions thrown into the greenlet will propagate into the
parent greenlet.
.. doctest::
>>> glet = greenlet.greenlet(run)
>>> _ = glet.switch()
>>> glet.throw(ValueError)
Traceback (most recent call last):
...
ValueError
As a special case, if the uncaught exception is
:exc:`greenlet.GreenletExit`, it will *not* propagate but instead be
returned. This is commonly used to signal an "expected exit".
.. doctest::
>>> glet = greenlet.greenlet(run)
>>> _ = glet.switch()
>>> glet.throw(greenlet.GreenletExit)
GreenletExit()
|