You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
299 lines
9.7 KiB
Python
299 lines
9.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright 2018 gevent contributes
|
|
# See LICENSE for details.
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import print_function
|
|
|
|
import gc
|
|
import unittest
|
|
|
|
import gevent.testing as greentest
|
|
|
|
import gevent
|
|
from gevent import util
|
|
from gevent import local
|
|
from greenlet import getcurrent
|
|
|
|
from gevent._compat import NativeStrIO
|
|
|
|
class MyLocal(local.local):
|
|
def __init__(self, foo):
|
|
self.foo = foo
|
|
|
|
@greentest.skipOnPyPy("5.10.x is *very* slow formatting stacks")
|
|
class TestFormat(greentest.TestCase):
|
|
|
|
def test_basic(self):
|
|
lines = util.format_run_info()
|
|
|
|
value = '\n'.join(lines)
|
|
self.assertIn('Threads', value)
|
|
self.assertIn('Greenlets', value)
|
|
|
|
# because it's a raw greenlet, we have no data for it.
|
|
self.assertNotIn("Spawned at", value)
|
|
self.assertNotIn("Parent greenlet", value)
|
|
self.assertNotIn("Spawn Tree Locals", value)
|
|
|
|
def test_with_Greenlet(self):
|
|
rl = local.local()
|
|
rl.foo = 1
|
|
def root():
|
|
l = MyLocal(42)
|
|
assert l
|
|
# And an empty local.
|
|
l2 = local.local()
|
|
assert l2
|
|
gevent.getcurrent().spawn_tree_locals['a value'] = 42
|
|
io = NativeStrIO()
|
|
g = gevent.spawn(util.print_run_info, file=io)
|
|
g.join()
|
|
return io.getvalue()
|
|
|
|
g = gevent.spawn(root)
|
|
g.name = 'Printer'
|
|
g.join()
|
|
value = g.value
|
|
|
|
|
|
self.assertIn("Spawned at", 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
|
|
# Empty locals should not be printed
|
|
self.assertNotIn('{}', value)
|
|
|
|
@greentest.skipOnPyPy("See TestFormat")
|
|
class TestTree(greentest.TestCase):
|
|
|
|
def setUp(self):
|
|
super(TestTree, self).setUp()
|
|
self.track_greenlet_tree = gevent.config.track_greenlet_tree
|
|
gevent.config.track_greenlet_tree = True
|
|
self.maxDiff = None
|
|
|
|
def tearDown(self):
|
|
gevent.config.track_greenlet_tree = self.track_greenlet_tree
|
|
super(TestTree, self).tearDown()
|
|
|
|
def _build_tree(self):
|
|
# pylint:disable=too-many-locals
|
|
# Python 2.7 on Travis seems to show unexpected greenlet objects
|
|
# so perhaps we need a GC?
|
|
for _ in range(3):
|
|
gc.collect()
|
|
gevent.get_hub().resolver = None # Reset resolver, don't need to see it
|
|
gevent.get_hub().threadpool = None # ditto the pool
|
|
glets = []
|
|
l = MyLocal(42)
|
|
assert l
|
|
|
|
def s(f):
|
|
str(getcurrent())
|
|
g = gevent.spawn(f)
|
|
# Access this in spawning order for consistent sorting
|
|
# at print time in the test case.
|
|
getattr(g, 'minimal_ident')
|
|
str(g)
|
|
return g
|
|
|
|
def t1():
|
|
raise greentest.ExpectedException()
|
|
|
|
def t2():
|
|
l = MyLocal(16)
|
|
assert l
|
|
g = s(t1)
|
|
g.name = 'CustomName-' + str(g.minimal_ident)
|
|
return g
|
|
|
|
s1 = s(t2)
|
|
#self.assertEqual(0, s1.minimal_ident)
|
|
s1.join()
|
|
|
|
glets.append(s(t2))
|
|
|
|
def t3():
|
|
return s(t2)
|
|
|
|
s3 = s(t3)
|
|
if s3.spawn_tree_locals is not None:
|
|
# Can only do this if we're tracking spawn trees
|
|
s3.spawn_tree_locals['stl'] = 'STL'
|
|
s3.join()
|
|
|
|
s4 = s(util.GreenletTree.current_tree)
|
|
s4.join()
|
|
|
|
tree = s4.value
|
|
return tree, str(tree), tree.format(details={'running_stacks': False,
|
|
'spawning_stacks': False})
|
|
|
|
def _normalize_tree_format(self, value):
|
|
import re
|
|
hexobj = re.compile('0x[0123456789abcdef]+L?', re.I)
|
|
|
|
hub_repr = repr(gevent.get_hub())
|
|
value = value.replace(hub_repr, "<HUB>")
|
|
|
|
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')
|
|
value = value.replace("type.current_tree", 'GreenletTree.current_tree')
|
|
value = value.replace('gevent.tests.__main__.MyLocal', '__main__.MyLocal')
|
|
return value
|
|
|
|
@greentest.ignores_leakcheck
|
|
def test_tree(self):
|
|
tree, str_tree, tree_format = self._build_tree()
|
|
|
|
self.assertTrue(tree.root)
|
|
|
|
self.assertNotIn('Parent', str_tree) # Simple output
|
|
value = self._normalize_tree_format(tree_format)
|
|
|
|
expected = """\
|
|
<greenlet.greenlet object at X>
|
|
: Parent: None
|
|
: Greenlet Locals:
|
|
: Local <class '__main__.MyLocal'> at X
|
|
: {'foo': 42}
|
|
+--- <HUB>
|
|
: Parent: <greenlet.greenlet object at X>
|
|
+--- <Greenlet "Greenlet-1" at X: t2>; finished with value <Greenlet "CustomName-0" at 0x
|
|
: Parent: <HUB>
|
|
| +--- <Greenlet "CustomName-0" at X: t1>; finished with exception ExpectedException()
|
|
: Parent: <HUB>
|
|
+--- <Greenlet "Greenlet-2" at X: t2>; finished with value <Greenlet "CustomName-4" at 0x
|
|
: Parent: <HUB>
|
|
| +--- <Greenlet "CustomName-4" at X: t1>; finished with exception ExpectedException()
|
|
: Parent: <HUB>
|
|
+--- <Greenlet "Greenlet-3" at X: t3>; finished with value <Greenlet "Greenlet-5" at X
|
|
: Parent: <HUB>
|
|
: Spawn Tree Locals
|
|
: {'stl': 'STL'}
|
|
| +--- <Greenlet "Greenlet-5" at X: t2>; finished with value <Greenlet "CustomName-6" at 0x
|
|
: Parent: <HUB>
|
|
| +--- <Greenlet "CustomName-6" at X: t1>; finished with exception ExpectedException()
|
|
: Parent: <HUB>
|
|
+--- <Greenlet "Greenlet-7" at X: <bound method GreenletTree.current_tree of <class 'gevent.util.GreenletTree'>>>; finished with value <gevent.util.GreenletTree obje
|
|
Parent: <HUB>
|
|
""".strip()
|
|
self.assertEqual(expected, value)
|
|
|
|
@greentest.ignores_leakcheck
|
|
def test_tree_no_track(self):
|
|
gevent.config.track_greenlet_tree = False
|
|
self._build_tree()
|
|
|
|
|
|
@greentest.ignores_leakcheck
|
|
def test_forest_fake_parent(self):
|
|
from greenlet import greenlet as RawGreenlet
|
|
|
|
def t4():
|
|
# Ignore this one, make the child the parent,
|
|
# and don't be a child of the hub.
|
|
c = RawGreenlet(util.GreenletTree.current_tree)
|
|
c.parent.greenlet_tree_is_ignored = True
|
|
c.greenlet_tree_is_root = True
|
|
return c.switch()
|
|
|
|
|
|
g = RawGreenlet(t4)
|
|
tree = g.switch()
|
|
|
|
tree_format = tree.format(details={'running_stacks': False,
|
|
'spawning_stacks': False})
|
|
value = self._normalize_tree_format(tree_format)
|
|
|
|
expected = """\
|
|
<greenlet.greenlet object at X>; not running
|
|
: Parent: <greenlet.greenlet object at X>
|
|
""".strip()
|
|
|
|
self.assertEqual(expected, value)
|
|
|
|
|
|
class TestAssertSwitches(unittest.TestCase):
|
|
|
|
def test_time_sleep(self):
|
|
# A real blocking function
|
|
from time import sleep
|
|
|
|
# No time given, we detect the failure to switch immediately
|
|
with self.assertRaises(util._FailedToSwitch) as exc:
|
|
with util.assert_switches():
|
|
sleep(0.001)
|
|
|
|
message = str(exc.exception)
|
|
self.assertIn('To any greenlet in', message)
|
|
|
|
# Supply a max blocking allowed and exceed it
|
|
with self.assertRaises(util._FailedToSwitch):
|
|
with util.assert_switches(0.001):
|
|
sleep(0.1)
|
|
|
|
|
|
# Supply a max blocking allowed, and exit before that happens,
|
|
# but don't switch to the hub as requested
|
|
with self.assertRaises(util._FailedToSwitch) as exc:
|
|
with util.assert_switches(0.001, hub_only=True):
|
|
sleep(0)
|
|
|
|
message = str(exc.exception)
|
|
self.assertIn('To the hub in', message)
|
|
self.assertIn('(max allowed 0.0010 seconds)', message)
|
|
|
|
# Supply a max blocking allowed, and exit before that happens,
|
|
# and allow any switch (or no switch).
|
|
# Note that we need to use a relatively long duration;
|
|
# sleep(0) on Windows can actually take a substantial amount of time
|
|
# sometimes (more than 0.001s)
|
|
with util.assert_switches(1.0, hub_only=False):
|
|
sleep(0)
|
|
|
|
|
|
def test_no_switches_no_function(self):
|
|
# No blocking time given, no switch performed: exception
|
|
with self.assertRaises(util._FailedToSwitch):
|
|
with util.assert_switches():
|
|
pass
|
|
|
|
# blocking time given, for all greenlets, no switch performed: nothing
|
|
with util.assert_switches(max_blocking_time=1, hub_only=False):
|
|
pass
|
|
|
|
def test_exception_not_supressed(self):
|
|
|
|
with self.assertRaises(NameError):
|
|
with util.assert_switches():
|
|
raise NameError()
|
|
|
|
def test_nested(self):
|
|
from greenlet import gettrace
|
|
with util.assert_switches() as outer:
|
|
self.assertEqual(gettrace(), outer.tracer)
|
|
self.assertIsNotNone(outer.tracer.active_greenlet)
|
|
|
|
with util.assert_switches() as inner:
|
|
self.assertEqual(gettrace(), inner.tracer)
|
|
self.assertEqual(inner.tracer.previous_trace_function, outer.tracer)
|
|
|
|
inner.tracer('switch', (self, self))
|
|
|
|
self.assertIs(self, inner.tracer.active_greenlet)
|
|
self.assertIs(self, outer.tracer.active_greenlet)
|
|
|
|
self.assertEqual(gettrace(), outer.tracer)
|
|
|
|
if __name__ == '__main__':
|
|
greentest.main()
|