Commit 5d4bdad6 authored by Jason Madden's avatar Jason Madden

Add a GreenletTree for more organized, clearer output of greenlets

It organizes things by the spawning greenlet, if possible.

Example output::

```
<greenlet.greenlet object at 0x10753a9b0>
 :    Running:
 :      File "/tmp/t.py", line 38, in <module>
 :        print("\n".join(format_run_info()))
 :      File "//src/gevent/util.py", line 99, in format_run_info
 :        _format_greenlet_info(lines)
 :      File "//src/gevent/util.py", line 132, in _format_greenlet_info
 :        lines.extend(tree.format_lines(details=True))
 :      File "//src/gevent/util.py", line 265, in format_lines
 :        for l in self._render(tree)]
 :      File "/-main/src/gevent/util.py", line 310, in _render
 :        self.__render_tb(tree, 'Running:', self.greenlet.gr_frame)
 :      File "//src/gevent/util.py", line 281, in __render_tb
 :        tb = ''.join(traceback.format_stack(frame))
 :    Greenlet Locals:
 :      Local <type 'gevent._local.local'> at 0x10759cec0
 :        {'foo': 42}
 +--- <Greenlet "Greenlet-0" at 0x107405cb0: _run>; finished with value <Greenlet "Greenlet-4" at 0x10
 :          Parent: <Hub at 0x10753a550 select default pending=0 ref=0>
 :          Spawned at:
 :            File "/tmp/t.py", line 1, in <module>
 :              from gevent.util import GreenletTree
 |    +--- <Greenlet "Greenlet-4" at 0x10780b260: _run>; finished
 :                Parent: <Hub at 0x10753a550 select default pending=0 ref=0>
 :                Spawned at:
 :                  File "/tmp/t.py", line 1, in <module>
 :                    from gevent.util import GreenletTree
 :                  File "/tmp/t.py", line 12, in t2
 :                    def t2():
 +--- <Hub at 0x10753a550 select default pending=0 ref=0>
 :          Parent: <greenlet.greenlet object at 0x10753a9b0>
 :          Running:
 :            File "/Users/jmadden/Projects/GithubSources/gevent-main/src/gevent/hub.py", line 673, in run
 :              loop.run()
 +--- <Greenlet "Greenlet-1" at 0x10780b368: _run>; finished with value <Greenlet "Greenlet-5" at 0x10
 :          Parent: <Hub at 0x10753a550 select default pending=0 ref=0>
 :          Spawned at:
 :            File "/tmp/t.py", line 1, in <module>
 :              from gevent.util import GreenletTree
 |    +--- <Greenlet "Greenlet-5" at 0x10780b578: _run>; finished
 :                Parent: <Hub at 0x10753a550 select default pending=0 ref=0>
 :                Spawned at:
 :                  File "/tmp/t.py", line 1, in <module>
 :                    from gevent.util import GreenletTree
 :                  File "/tmp/t.py", line 12, in t2
 :                    def t2():
 +--- <Greenlet "Greenlet-2" at 0x10780b470: _run>; finished with value <Greenlet "Greenlet-6" at 0x10
 :          Parent: <Hub at 0x10753a550 select default pending=0 ref=0>
 :          Spawned at:
 :            File "/tmp/t.py", line 1, in <module>
 :              from gevent.util import GreenletTree
 :          Spawn Tree Locals
 :          {'stl': 'STL'}
 |    +--- <Greenlet "Greenlet-6" at 0x10780b680: _run>; finished with value <Greenlet "Greenlet-7" at 0x10
 :                Parent: <Hub at 0x10753a550 select default pending=0 ref=0>
 :                Spawned at:
 :                  File "/tmp/t.py", line 1, in <module>
 :                    from gevent.util import GreenletTree
 :                  File "/tmp/t.py", line 21, in t3
 :                    def t3():
 |         +--- <Greenlet "Greenlet-7" at 0x10780b788: _run>; finished
 :                      Parent: <Hub at 0x10753a550 select default pending=0 ref=0>
 :                      Spawned at:
 :                        File "/tmp/t.py", line 1, in <module>
 :                          from gevent.util import GreenletTree
 :                        File "/tmp/t.py", line 21, in t3
 :                          def t3():
 :                        File "/tmp/t.py", line 12, in t2
 :                          def t2():
 +--- <Greenlet "Greenlet-3" at 0x10780b890: _run>; finished with value [<gevent.util.GreenletTree obj
            Parent: <Hub at 0x10753a550 select default pending=0 ref=0>
            Spawned at:
              File "/tmp/t.py", line 1, in <module>
                from gevent.util import GreenletTree
```
parent 6d3caff5
......@@ -123,6 +123,8 @@
- `gevent.Greenlet` objects now have a `gevent.Greenlet.name`
attribute that is included in the default repr.
- Add `gevent.util.GreenletTree` to visualize the greenlet tree. This
is used by `gevent.util.format_run_info`.
1.3a1 (2018-01-27)
==================
......
This diff is collapsed.
......@@ -6,6 +6,8 @@ from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import gc
import greentest
import gevent
......@@ -36,6 +38,7 @@ class TestFormat(greentest.TestCase):
rl.foo = 1
def root():
l = MyLocal(42)
assert l
gevent.getcurrent().spawn_tree_locals['a value'] = 42
g = gevent.spawn(util.format_run_info)
g.join()
......@@ -47,12 +50,100 @@ class TestFormat(greentest.TestCase):
value = '\n'.join(g.value)
self.assertIn("Spawned at", value)
self.assertIn("Parent greenlet", value)
self.assertIn("Parent:", value)
self.assertIn("Spawn Tree Locals", value)
self.assertIn("Greenlet Locals:", value)
self.assertIn('MyLocal', value)
self.assertIn("Printer", value) # The name is printed
@greentest.skipOnPyPy("See TestFormat")
class TestTree(greentest.TestCase):
@greentest.ignores_leakcheck
def test_tree(self):
# pylint:disable=too-many-locals
# Python 2.7 on Travis seems to show unexpected greenlet objects
# so perhaps we need a GC?
gc.collect()
gc.collect()
import re
glets = []
l = MyLocal(42)
assert l
def s(f):
g = gevent.spawn(f)
# Access this in spawning order for consistent sorting
# at print time in the test case.
getattr(g, 'minimal_ident')
return g
def t1():
raise greentest.ExpectedException()
def t2():
l = MyLocal(16)
assert l
return s(t1)
s1 = s(t2)
s1.join()
glets.append(s(t2))
def t3():
return s(t2)
s3 = s(t3)
s3.spawn_tree_locals['stl'] = 'STL'
s3.join()
s4 = s(util.GreenletTree.current_tree)
s4.join()
tree = s4.value
self.assertTrue(tree.root)
self.assertNotIn('Parent', str(tree)) # Simple output
value = tree.format(details={'stacks': False})
hexobj = re.compile('0x[0123456789abcdef]+L?', re.I)
value = hexobj.sub('X', value)
value = value.replace('epoll', 'select')
value = value.replace('select', 'default')
value = value.replace('test__util', '__main__')
value = re.compile(' fileno=.').sub('', value)
value = value.replace('ref=-1', 'ref=0')
self.maxDiff = None
expected = """\
<greenlet.greenlet object at X>
: Greenlet Locals:
: Local <class '__main__.MyLocal'> at X
: {'foo': 42}
+--- <QuietHub at X default default pending=0 ref=0>
: Parent: <greenlet.greenlet object at X>
+--- <Greenlet "Greenlet-1" at X: _run>; finished with value <Greenlet "Greenlet-0" at X
: Parent: <QuietHub at X default default pending=0 ref=0>
| +--- <Greenlet "Greenlet-0" at X: _run>; finished with exception ExpectedException()
: Parent: <QuietHub at X default default pending=0 ref=0>
+--- <Greenlet "Greenlet-2" at X: _run>; finished with value <Greenlet "Greenlet-4" at X
: Parent: <QuietHub at X default default pending=0 ref=0>
| +--- <Greenlet "Greenlet-4" at X: _run>; finished with exception ExpectedException()
: Parent: <QuietHub at X default default pending=0 ref=0>
+--- <Greenlet "Greenlet-3" at X: _run>; finished with value <Greenlet "Greenlet-5" at X
: Parent: <QuietHub at X default default pending=0 ref=0>
: Spawn Tree Locals
: {'stl': 'STL'}
| +--- <Greenlet "Greenlet-5" at X: _run>; finished with value <Greenlet "Greenlet-6" at X
: Parent: <QuietHub at X default default pending=0 ref=0>
| +--- <Greenlet "Greenlet-6" at X: _run>; finished with exception ExpectedException()
: Parent: <QuietHub at X default default pending=0 ref=0>
+--- <Greenlet "Greenlet-7" at X: _run>; finished with value <gevent.util.GreenletTree obje
Parent: <QuietHub at X default default pending=0 ref=0>
""".strip()
self.assertEqual(value, expected)
if __name__ == '__main__':
greentest.main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment