A python library providing gevent tools:
- a gevent friendly pdb
- tree representation of running greenlets
The standard python pdb
module blocks all greenlets at the pdb prompt
(unlike a threaded app). If you want your greenlets to run in the background
you can use gtools.pdb
instead.
It can be used like the standard pdb
.
So, imagine you have a gevent app that you want to debug:
# ======
# app.py
# ======
import gevent
def produce(p):
for i in range(60):
p.append(i)
gevent.sleep(1)
products = []
gevent.spawn(produce, products)
to debug it just type on the console:
$ python -m gtools.pdb app.py
Then hit 'n' until you reach the task.join()
line. At this point the
greenlet is already doing its work on the background. To make sure just type
products several times on the pdb console and you will see the products list
being filled by the running greenlet:
> /app.py(9)<module>()
-> gevent.spawn(produce, products)
(Pdb) products
[0, 1, 2]
(Pdb) products
[0, 1, 2, 3, 4, 5, 6, 7]
Use gtools.pdb.set_trace()
just as you would with the standard
pdb.set_trace()
gtools.tree.Tree()
allows you to trace the current greenlets and display them
in a tree like structure:
>>> import gevent
>>> import gtools.tree
>>> def iloop():
... gevent.sleep(1)
>>> def oloop():
... gtools.spawn(iloop)
... gevent.sleep(0.5)
>>> task = gtools.spawn(oloop)
>>> # sleep just to trigger spawn of inner greenlets
>>> gevent.sleep()
>>> tree = gtools.tree.Tree()
>>> # initial status
>>> print(tree)
Root
└─ <greenlet.greenlet A> status=running
└─ <Greenlet B: oloop> status=running
└─ <Greenlet C: iloop> status=running
>>> # after outer loop finishes
>>> gevent.sleep(0.6)
>>> print(tree)
Root
└─ <greenlet.greenlet A> status=running
└─ <Greenlet B: oloop> status=finished:success
└─ <Greenlet C: iloop> status=running
>>> # after inner loop finishes
>>> gevent.sleep(0.6)
>>> print(tree)
Root
└─ <greenlet.greenlet A> status=running
└─ <Greenlet B: oloop> status=finished:success
└─ <Greenlet C: iloop> status=dead:garbage collected
>>> del task
>>> # when there are no more references to the greenlets
>>> print(tree)
Root
└─ <greenlet.greenlet A> status=running
└─ <Greenlet B: oloop> status=dead:garbage collected
└─ <Greenlet C: iloop> status=dead:garbage collected
>>>
>>> # new tree
>>> print(gtools.tree.Tree())
Root
The above example requires the usage of gtools.Greenlet
.
To trace greenlets from an existing gevent application you simply need to monkey-patch gevent itself before importing your app:
# ======
# app.py
# ======
import gevent
def iloop():
gevent.sleep(1)
def oloop():
gevent.spawn(iloop)
gevent.sleep(0.5)
def run():
return gevent.spawn(oloop)
>>> from gtools.monkey import patch_gevent
>>> patch_gevent()
>>> import app
>>> import gtools.tree
>>> the_app = app.run()
>>> # sleep just to trigger spawn of inner greenlets
>>> gevent.sleep()
>>> tree = gtools.tree.Tree()
>>> # initial status
>>> print(tree)
Root
└─ <greenlet.greenlet A> status=running
└─ <Greenlet B: oloop> status=running
└─ <Greenlet C: iloop> status=running
If you don't monkey patch, you can still have limited information about the running greenlets (notice that the tree hierarchy is lost):
>>> import app
>>> import gtools.tree
>>> task = gevent.spawn(oloop)
>>> the_app = app.run()
>>> # sleep just to trigger spawn of inner greenlets
>>> gevent.sleep()
>>> tree = gtools.tree.Tree(all=True)
>>> # initial status
>>> print(tree)
Root
└─ <greenlet.greenlet A> status=running
└─ <Hub [...]> status=running
├─ <Greenlet B: oloop> status=running
└─ <Greenlet C: iloop> status=running
It can even trace greenlets across multiple threads (see
examples/tree_threads.py
)