Загрузить файлы в «venv/Lib/site-packages/greenlet/tests»
This commit is contained in:
@@ -0,0 +1,141 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
import greenlet
|
||||||
|
from . import _test_extension
|
||||||
|
from . import TestCase
|
||||||
|
from .leakcheck import ignores_leakcheck
|
||||||
|
|
||||||
|
# pylint:disable=c-extension-no-member
|
||||||
|
|
||||||
|
class CAPITests(TestCase):
|
||||||
|
def test_switch(self):
|
||||||
|
self.assertEqual(
|
||||||
|
50, _test_extension.test_switch(greenlet.greenlet(lambda: 50)))
|
||||||
|
|
||||||
|
def test_switch_kwargs(self):
|
||||||
|
def adder(x, y):
|
||||||
|
return x * y
|
||||||
|
g = greenlet.greenlet(adder)
|
||||||
|
self.assertEqual(6, _test_extension.test_switch_kwargs(g, x=3, y=2))
|
||||||
|
|
||||||
|
with self.assertRaisesRegex(TypeError, "argument 1 must be greenlet"):
|
||||||
|
_test_extension.test_switch_kwargs("not a greenlet")
|
||||||
|
|
||||||
|
def test_setparent(self):
|
||||||
|
# pylint:disable=disallowed-name
|
||||||
|
def foo():
|
||||||
|
def bar():
|
||||||
|
greenlet.getcurrent().parent.switch()
|
||||||
|
|
||||||
|
# This final switch should go back to the main greenlet, since
|
||||||
|
# the test_setparent() function in the C extension should have
|
||||||
|
# reparented this greenlet.
|
||||||
|
greenlet.getcurrent().parent.switch()
|
||||||
|
raise AssertionError("Should never have reached this code")
|
||||||
|
child = greenlet.greenlet(bar)
|
||||||
|
child.switch()
|
||||||
|
greenlet.getcurrent().parent.switch(child)
|
||||||
|
greenlet.getcurrent().parent.throw(
|
||||||
|
AssertionError("Should never reach this code"))
|
||||||
|
foo_child = greenlet.greenlet(foo).switch()
|
||||||
|
self.assertEqual(None, _test_extension.test_setparent(foo_child))
|
||||||
|
|
||||||
|
def test_getcurrent(self):
|
||||||
|
_test_extension.test_getcurrent()
|
||||||
|
|
||||||
|
def test_new_greenlet(self):
|
||||||
|
self.assertEqual(-15, _test_extension.test_new_greenlet(lambda: -15))
|
||||||
|
|
||||||
|
def test_raise_greenlet_dead(self):
|
||||||
|
self.assertRaises(
|
||||||
|
greenlet.GreenletExit, _test_extension.test_raise_dead_greenlet)
|
||||||
|
|
||||||
|
def test_raise_greenlet_error(self):
|
||||||
|
self.assertRaises(
|
||||||
|
greenlet.error, _test_extension.test_raise_greenlet_error)
|
||||||
|
|
||||||
|
def test_throw(self):
|
||||||
|
seen = []
|
||||||
|
|
||||||
|
def foo(): # pylint:disable=disallowed-name
|
||||||
|
try:
|
||||||
|
greenlet.getcurrent().parent.switch()
|
||||||
|
except ValueError:
|
||||||
|
seen.append(sys.exc_info()[1])
|
||||||
|
except greenlet.GreenletExit:
|
||||||
|
raise AssertionError
|
||||||
|
g = greenlet.greenlet(foo)
|
||||||
|
g.switch()
|
||||||
|
_test_extension.test_throw(g)
|
||||||
|
self.assertEqual(len(seen), 1)
|
||||||
|
self.assertTrue(
|
||||||
|
isinstance(seen[0], ValueError),
|
||||||
|
"ValueError was not raised in foo()")
|
||||||
|
self.assertEqual(
|
||||||
|
str(seen[0]),
|
||||||
|
'take that sucka!',
|
||||||
|
"message doesn't match")
|
||||||
|
|
||||||
|
def test_non_traceback_param(self):
|
||||||
|
with self.assertRaises(TypeError) as exc:
|
||||||
|
_test_extension.test_throw_exact(
|
||||||
|
greenlet.getcurrent(),
|
||||||
|
Exception,
|
||||||
|
Exception(),
|
||||||
|
self
|
||||||
|
)
|
||||||
|
self.assertEqual(str(exc.exception),
|
||||||
|
"throw() third argument must be a traceback object")
|
||||||
|
|
||||||
|
def test_instance_of_wrong_type(self):
|
||||||
|
with self.assertRaises(TypeError) as exc:
|
||||||
|
_test_extension.test_throw_exact(
|
||||||
|
greenlet.getcurrent(),
|
||||||
|
Exception(),
|
||||||
|
BaseException(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(str(exc.exception),
|
||||||
|
"instance exception may not have a separate value")
|
||||||
|
|
||||||
|
def test_not_throwable(self):
|
||||||
|
with self.assertRaises(TypeError) as exc:
|
||||||
|
_test_extension.test_throw_exact(
|
||||||
|
greenlet.getcurrent(),
|
||||||
|
"abc",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
self.assertEqual(str(exc.exception),
|
||||||
|
"exceptions must be classes, or instances, not str")
|
||||||
|
|
||||||
|
@ignores_leakcheck
|
||||||
|
def test_leaks(self):
|
||||||
|
from . import PY314
|
||||||
|
iters = 100
|
||||||
|
if PY314:
|
||||||
|
expected_refs = [1] * iters
|
||||||
|
else:
|
||||||
|
expected_refs = [2] * iters
|
||||||
|
for name, caller in (
|
||||||
|
("test_switch",
|
||||||
|
lambda: _test_extension.test_switch(greenlet.greenlet(object))),
|
||||||
|
("test_switch_kwargs",
|
||||||
|
lambda: _test_extension.test_switch_kwargs(greenlet.greenlet(object))),
|
||||||
|
("test_new_greenlet",
|
||||||
|
lambda: _test_extension.test_new_greenlet(object)),
|
||||||
|
):
|
||||||
|
with self.subTest(name):
|
||||||
|
results = [caller() for _ in range(iters)]
|
||||||
|
refs = [
|
||||||
|
sys.getrefcount(i) - 1 # ignore ref in ``i``
|
||||||
|
for i
|
||||||
|
in results
|
||||||
|
]
|
||||||
|
self.assertEqual(refs, expected_refs)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import unittest
|
||||||
|
unittest.main()
|
||||||
137
venv/Lib/site-packages/greenlet/tests/test_gc.py
Normal file
137
venv/Lib/site-packages/greenlet/tests/test_gc.py
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
import gc
|
||||||
|
|
||||||
|
import weakref
|
||||||
|
import sys
|
||||||
|
import greenlet
|
||||||
|
|
||||||
|
|
||||||
|
from . import TestCase
|
||||||
|
from .leakcheck import fails_leakcheck_on_py314_or_less
|
||||||
|
# These only work with greenlet gc support
|
||||||
|
# which is no longer optional.
|
||||||
|
assert greenlet.GREENLET_USE_GC
|
||||||
|
|
||||||
|
class TestGC(TestCase):
|
||||||
|
def test_dead_circular_ref(self):
|
||||||
|
o = weakref.ref(greenlet.greenlet(greenlet.getcurrent).switch())
|
||||||
|
gc.collect()
|
||||||
|
if o() is not None:
|
||||||
|
print("O IS NOT NONE.", sys.getrefcount(o()))
|
||||||
|
self.assertIsNone(o())
|
||||||
|
self.assertFalse(gc.garbage, gc.garbage)
|
||||||
|
|
||||||
|
def test_circular_greenlet(self):
|
||||||
|
class circular_greenlet(greenlet.greenlet):
|
||||||
|
self = None
|
||||||
|
o = circular_greenlet()
|
||||||
|
o.self = o
|
||||||
|
o = weakref.ref(o)
|
||||||
|
gc.collect()
|
||||||
|
self.assertIsNone(o())
|
||||||
|
self.assertFalse(gc.garbage, gc.garbage)
|
||||||
|
|
||||||
|
def test_inactive_ref(self):
|
||||||
|
class inactive_greenlet(greenlet.greenlet):
|
||||||
|
def __init__(self):
|
||||||
|
greenlet.greenlet.__init__(self, run=self.run)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
pass
|
||||||
|
o = inactive_greenlet()
|
||||||
|
o = weakref.ref(o)
|
||||||
|
gc.collect()
|
||||||
|
self.assertIsNone(o())
|
||||||
|
self.assertFalse(gc.garbage, gc.garbage)
|
||||||
|
|
||||||
|
@fails_leakcheck_on_py314_or_less
|
||||||
|
def test_finalizer_crash(self):
|
||||||
|
# This test is designed to crash when active greenlets
|
||||||
|
# are made garbage collectable, until the underlying
|
||||||
|
# problem is resolved. How does it work:
|
||||||
|
# - order of object creation is important
|
||||||
|
# - array is created first, so it is moved to unreachable first
|
||||||
|
# - we create a cycle between a greenlet and this array
|
||||||
|
# - we create an object that participates in gc, is only
|
||||||
|
# referenced by a greenlet, and would corrupt gc lists
|
||||||
|
# on destruction, the easiest is to use an object with
|
||||||
|
# a finalizer
|
||||||
|
# - because array is the first object in unreachable it is
|
||||||
|
# cleared first, which causes all references to greenlet
|
||||||
|
# to disappear and causes greenlet to be destroyed, but since
|
||||||
|
# it is still live it causes a switch during gc, which causes
|
||||||
|
# an object with finalizer to be destroyed, which causes stack
|
||||||
|
# corruption and then a crash
|
||||||
|
|
||||||
|
class object_with_finalizer(object):
|
||||||
|
def __del__(self):
|
||||||
|
pass
|
||||||
|
array = []
|
||||||
|
parent = greenlet.getcurrent()
|
||||||
|
def greenlet_body():
|
||||||
|
greenlet.getcurrent().object = object_with_finalizer()
|
||||||
|
try:
|
||||||
|
parent.switch()
|
||||||
|
except greenlet.GreenletExit:
|
||||||
|
print("Got greenlet exit!")
|
||||||
|
finally:
|
||||||
|
del greenlet.getcurrent().object
|
||||||
|
g = greenlet.greenlet(greenlet_body)
|
||||||
|
g.array = array
|
||||||
|
array.append(g)
|
||||||
|
g.switch()
|
||||||
|
del array
|
||||||
|
del g
|
||||||
|
greenlet.getcurrent()
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
def test_crashing_deferred_object(self):
|
||||||
|
if sys.version_info < (3, 15):
|
||||||
|
self.skipTest("Test is 3.15+ only")
|
||||||
|
import doctest
|
||||||
|
def with_doctest():
|
||||||
|
"""
|
||||||
|
>>> import gc
|
||||||
|
>>> from greenlet import getcurrent, greenlet, GreenletExit
|
||||||
|
>>> def outer():
|
||||||
|
... gc.collect()
|
||||||
|
>>> outer_glet = greenlet(outer)
|
||||||
|
>>> outer_glet.switch()
|
||||||
|
"""
|
||||||
|
doctest.run_docstring_examples(with_doctest, dict())
|
||||||
|
|
||||||
|
def test_cycle_in_suspended_frame(self):
|
||||||
|
if sys.version_info < (3, 15):
|
||||||
|
self.skipTest("Test is 3.15+ only")
|
||||||
|
import doctest
|
||||||
|
def with_doctest():
|
||||||
|
"""
|
||||||
|
>>> import gc
|
||||||
|
>>> from greenlet import getcurrent, greenlet
|
||||||
|
>>> class Cycle:
|
||||||
|
... def __del__(self):
|
||||||
|
... print("(Running finalizer)")
|
||||||
|
>>> def collect_it():
|
||||||
|
... print("Collecting garbage")
|
||||||
|
... gc.collect()
|
||||||
|
>>> def inner():
|
||||||
|
... cycle1 = Cycle()
|
||||||
|
... cycle2 = Cycle()
|
||||||
|
... cycle1.cycle = cycle2
|
||||||
|
... cycle2.cycle = cycle1
|
||||||
|
... getcurrent().parent.switch()
|
||||||
|
>>> def outer():
|
||||||
|
... glet = greenlet(inner)
|
||||||
|
... glet.switch()
|
||||||
|
... collect_it()
|
||||||
|
|
||||||
|
>>> outer_glet = greenlet(outer)
|
||||||
|
>>> outer_glet.switch()
|
||||||
|
Collecting garbage
|
||||||
|
>>> outer_glet.dead
|
||||||
|
True
|
||||||
|
>>> collect_it()
|
||||||
|
Collecting garbage
|
||||||
|
(Running finalizer)
|
||||||
|
(Running finalizer)
|
||||||
|
"""
|
||||||
|
doctest.run_docstring_examples(with_doctest, dict())
|
||||||
59
venv/Lib/site-packages/greenlet/tests/test_generator.py
Normal file
59
venv/Lib/site-packages/greenlet/tests/test_generator.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
|
||||||
|
from greenlet import greenlet
|
||||||
|
|
||||||
|
from . import TestCase
|
||||||
|
|
||||||
|
class genlet(greenlet):
|
||||||
|
parent = None
|
||||||
|
def __init__(self, *args, **kwds):
|
||||||
|
self.args = args
|
||||||
|
self.kwds = kwds
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
fn, = self.fn
|
||||||
|
fn(*self.args, **self.kwds)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
self.parent = greenlet.getcurrent()
|
||||||
|
result = self.switch()
|
||||||
|
if self:
|
||||||
|
return result
|
||||||
|
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
|
next = __next__
|
||||||
|
|
||||||
|
|
||||||
|
def Yield(value):
|
||||||
|
g = greenlet.getcurrent()
|
||||||
|
while not isinstance(g, genlet):
|
||||||
|
if g is None:
|
||||||
|
raise RuntimeError('yield outside a genlet')
|
||||||
|
g = g.parent
|
||||||
|
g.parent.switch(value)
|
||||||
|
|
||||||
|
|
||||||
|
def generator(func):
|
||||||
|
class Generator(genlet):
|
||||||
|
fn = (func,)
|
||||||
|
return Generator
|
||||||
|
|
||||||
|
# ____________________________________________________________
|
||||||
|
|
||||||
|
|
||||||
|
class GeneratorTests(TestCase):
|
||||||
|
def test_generator(self):
|
||||||
|
seen = []
|
||||||
|
|
||||||
|
def g(n):
|
||||||
|
for i in range(n):
|
||||||
|
seen.append(i)
|
||||||
|
Yield(i)
|
||||||
|
g = generator(g)
|
||||||
|
for _ in range(3):
|
||||||
|
for j in g(5):
|
||||||
|
seen.append(j)
|
||||||
|
self.assertEqual(seen, 3 * [0, 0, 1, 1, 2, 2, 3, 3, 4, 4])
|
||||||
168
venv/Lib/site-packages/greenlet/tests/test_generator_nested.py
Normal file
168
venv/Lib/site-packages/greenlet/tests/test_generator_nested.py
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
|
||||||
|
from greenlet import greenlet
|
||||||
|
from . import TestCase
|
||||||
|
from .leakcheck import fails_leakcheck
|
||||||
|
|
||||||
|
class genlet(greenlet):
|
||||||
|
parent = None
|
||||||
|
def __init__(self, *args, **kwds):
|
||||||
|
self.args = args
|
||||||
|
self.kwds = kwds
|
||||||
|
self.child = None
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
# Note the function is packed in a tuple
|
||||||
|
# to avoid creating a bound method for it.
|
||||||
|
fn, = self.fn
|
||||||
|
fn(*self.args, **self.kwds)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_child(self, child):
|
||||||
|
self.child = child
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
if self.child:
|
||||||
|
child = self.child
|
||||||
|
while child.child:
|
||||||
|
tmp = child
|
||||||
|
child = child.child
|
||||||
|
tmp.child = None
|
||||||
|
|
||||||
|
result = child.switch()
|
||||||
|
else:
|
||||||
|
self.parent = greenlet.getcurrent()
|
||||||
|
result = self.switch()
|
||||||
|
|
||||||
|
if self:
|
||||||
|
return result
|
||||||
|
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
|
next = __next__
|
||||||
|
|
||||||
|
def Yield(value, level=1):
|
||||||
|
g = greenlet.getcurrent()
|
||||||
|
|
||||||
|
while level != 0:
|
||||||
|
if not isinstance(g, genlet):
|
||||||
|
raise RuntimeError('yield outside a genlet')
|
||||||
|
if level > 1:
|
||||||
|
g.parent.set_child(g)
|
||||||
|
g = g.parent
|
||||||
|
level -= 1
|
||||||
|
|
||||||
|
g.switch(value)
|
||||||
|
|
||||||
|
|
||||||
|
def Genlet(func):
|
||||||
|
class TheGenlet(genlet):
|
||||||
|
fn = (func,)
|
||||||
|
return TheGenlet
|
||||||
|
|
||||||
|
# ____________________________________________________________
|
||||||
|
|
||||||
|
|
||||||
|
def g1(n, seen):
|
||||||
|
for i in range(n):
|
||||||
|
seen.append(i + 1)
|
||||||
|
yield i
|
||||||
|
|
||||||
|
|
||||||
|
def g2(n, seen):
|
||||||
|
for i in range(n):
|
||||||
|
seen.append(i + 1)
|
||||||
|
Yield(i)
|
||||||
|
|
||||||
|
g2 = Genlet(g2)
|
||||||
|
|
||||||
|
|
||||||
|
def nested(i):
|
||||||
|
Yield(i)
|
||||||
|
|
||||||
|
|
||||||
|
def g3(n, seen):
|
||||||
|
for i in range(n):
|
||||||
|
seen.append(i + 1)
|
||||||
|
nested(i)
|
||||||
|
g3 = Genlet(g3)
|
||||||
|
|
||||||
|
|
||||||
|
def a(n):
|
||||||
|
if n == 0:
|
||||||
|
return
|
||||||
|
for ii in ax(n - 1):
|
||||||
|
Yield(ii)
|
||||||
|
Yield(n)
|
||||||
|
ax = Genlet(a)
|
||||||
|
|
||||||
|
|
||||||
|
def perms(l):
|
||||||
|
if len(l) > 1:
|
||||||
|
for e in l:
|
||||||
|
# No syntactical sugar for generator expressions
|
||||||
|
x = [Yield([e] + p) for p in perms([x for x in l if x != e])]
|
||||||
|
assert x
|
||||||
|
else:
|
||||||
|
Yield(l)
|
||||||
|
perms = Genlet(perms)
|
||||||
|
|
||||||
|
|
||||||
|
def gr1(n):
|
||||||
|
for ii in range(1, n):
|
||||||
|
Yield(ii)
|
||||||
|
Yield(ii * ii, 2)
|
||||||
|
|
||||||
|
gr1 = Genlet(gr1)
|
||||||
|
|
||||||
|
|
||||||
|
def gr2(n, seen):
|
||||||
|
for ii in gr1(n):
|
||||||
|
seen.append(ii)
|
||||||
|
|
||||||
|
gr2 = Genlet(gr2)
|
||||||
|
|
||||||
|
|
||||||
|
class NestedGeneratorTests(TestCase):
|
||||||
|
def test_layered_genlets(self):
|
||||||
|
seen = []
|
||||||
|
for ii in gr2(5, seen):
|
||||||
|
seen.append(ii)
|
||||||
|
self.assertEqual(seen, [1, 1, 2, 4, 3, 9, 4, 16])
|
||||||
|
|
||||||
|
@fails_leakcheck
|
||||||
|
def test_permutations(self):
|
||||||
|
gen_perms = perms(list(range(4)))
|
||||||
|
permutations = list(gen_perms)
|
||||||
|
self.assertEqual(len(permutations), 4 * 3 * 2 * 1)
|
||||||
|
self.assertIn([0, 1, 2, 3], permutations)
|
||||||
|
self.assertIn([3, 2, 1, 0], permutations)
|
||||||
|
res = []
|
||||||
|
for ii in zip(perms(list(range(4))), perms(list(range(3)))):
|
||||||
|
res.append(ii)
|
||||||
|
self.assertEqual(
|
||||||
|
res,
|
||||||
|
[([0, 1, 2, 3], [0, 1, 2]), ([0, 1, 3, 2], [0, 2, 1]),
|
||||||
|
([0, 2, 1, 3], [1, 0, 2]), ([0, 2, 3, 1], [1, 2, 0]),
|
||||||
|
([0, 3, 1, 2], [2, 0, 1]), ([0, 3, 2, 1], [2, 1, 0])])
|
||||||
|
# XXX Test to make sure we are working as a generator expression
|
||||||
|
|
||||||
|
def test_genlet_simple(self):
|
||||||
|
for g in g1, g2, g3:
|
||||||
|
seen = []
|
||||||
|
for _ in range(3):
|
||||||
|
for j in g(5, seen):
|
||||||
|
seen.append(j)
|
||||||
|
self.assertEqual(seen, 3 * [1, 0, 2, 1, 3, 2, 4, 3, 5, 4])
|
||||||
|
|
||||||
|
def test_genlet_bad(self):
|
||||||
|
try:
|
||||||
|
Yield(10)
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_nested_genlets(self):
|
||||||
|
seen = []
|
||||||
|
for ii in ax(5):
|
||||||
|
seen.append(ii)
|
||||||
1482
venv/Lib/site-packages/greenlet/tests/test_greenlet.py
Normal file
1482
venv/Lib/site-packages/greenlet/tests/test_greenlet.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user