Загрузить файлы в «venv/Lib/site-packages/greenlet/tests»

This commit is contained in:
2026-07-02 18:27:02 +00:00
parent 150fd8c689
commit efdfc98c46
5 changed files with 231 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
"""
If we have a run callable passed to the constructor or set as an
attribute, but we don't actually use that (because ``__getattribute__``
or the like interferes), then when we clear callable before beginning
to run, there's an opportunity for Python code to run.
"""
import greenlet
g = None
main = greenlet.getcurrent()
results = []
class RunCallable:
def __del__(self):
results.append(('RunCallable', '__del__'))
main.switch('from RunCallable')
class G(greenlet.greenlet):
def __getattribute__(self, name):
if name == 'run':
results.append(('G.__getattribute__', 'run'))
return run_func
return object.__getattribute__(self, name)
def run_func():
results.append(('run_func', 'enter'))
g = G(RunCallable())
# Try to start G. It will get to the point where it deletes
# its run callable C++ variable in inner_bootstrap. That triggers
# the __del__ method, which switches back to main before g
# actually even starts running.
x = g.switch()
results.append(('main: g.switch()', x))
# In the C++ code, this results in g->g_switch() appearing to return, even though
# it has yet to run.
print('In main with', x, flush=True)
g.switch()
print('RESULTS', results)

View File

@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
"""
Helper for testing a C++ exception throw aborts the process.
Takes one argument, the name of the function in :mod:`_test_extension_cpp` to call.
"""
import sys
import greenlet
from greenlet.tests import _test_extension_cpp
print('fail_cpp_exception is running')
def run_unhandled_exception_in_greenlet_aborts():
def _():
_test_extension_cpp.test_exception_switch_and_do_in_g2(
_test_extension_cpp.test_exception_throw_nonstd
)
g1 = greenlet.greenlet(_)
g1.switch()
func_name = sys.argv[1]
try:
func = getattr(_test_extension_cpp, func_name)
except AttributeError:
if func_name == run_unhandled_exception_in_greenlet_aborts.__name__:
func = run_unhandled_exception_in_greenlet_aborts
elif func_name == 'run_as_greenlet_target':
g = greenlet.greenlet(_test_extension_cpp.test_exception_throw_std)
func = g.switch
else:
raise
print('raising', func, flush=True)
func()

View File

@@ -0,0 +1,78 @@
"""
Testing initialstub throwing an already started exception.
"""
import greenlet
a = None
b = None
c = None
main = greenlet.getcurrent()
# If we switch into a dead greenlet,
# we go looking for its parents.
# if a parent is not yet started, we start it.
results = []
def a_run(*args):
#results.append('A')
results.append(('Begin A', args))
def c_run():
results.append('Begin C')
b.switch('From C')
results.append('C done')
class A(greenlet.greenlet): pass
class B(greenlet.greenlet):
doing_it = False
def __getattribute__(self, name):
if name == 'run' and not self.doing_it:
assert greenlet.getcurrent() is c
self.doing_it = True
results.append('Switch to b from B.__getattribute__ in '
+ type(greenlet.getcurrent()).__name__)
b.switch()
results.append('B.__getattribute__ back from main in '
+ type(greenlet.getcurrent()).__name__)
if name == 'run':
name = '_B_run'
return object.__getattribute__(self, name)
def _B_run(self, *arg):
results.append(('Begin B', arg))
results.append('_B_run switching to main')
main.switch('From B')
class C(greenlet.greenlet):
pass
a = A(a_run)
b = B(parent=a)
c = C(c_run, b)
# Start a child; while running, it will start B,
# but starting B will ALSO start B.
result = c.switch()
results.append(('main from c', result))
# Switch back to C, which was in the middle of switching
# already. This will throw the ``GreenletStartedWhileInPython``
# exception, which results in parent A getting started (B is finished)
c.switch()
results.append(('A dead?', a.dead, 'B dead?', b.dead, 'C dead?', c.dead))
# A and B should both be dead now.
assert a.dead
assert b.dead
assert not c.dead
result = c.switch()
results.append(('main from c.2', result))
# Now C is dead
assert c.dead
print("RESULTS:", results)

View File

@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
"""
A test helper for seeing what happens when slp_switch()
fails.
"""
# pragma: no cover
import greenlet
print('fail_slp_switch is running', flush=True)
runs = []
def func():
runs.append(1)
greenlet.getcurrent().parent.switch()
runs.append(2)
greenlet.getcurrent().parent.switch()
runs.append(3)
g = greenlet._greenlet.UnswitchableGreenlet(func)
g.switch()
assert runs == [1]
g.switch()
assert runs == [1, 2]
g.force_slp_switch_error = True
# This should crash.
g.switch()

View File

@@ -0,0 +1,44 @@
"""
Uses a trace function to switch greenlets at unexpected times.
In the trace function, we switch from the current greenlet to another
greenlet, which switches
"""
import greenlet
g1 = None
g2 = None
switch_to_g2 = False
def tracefunc(*args):
print('TRACE', *args)
global switch_to_g2
if switch_to_g2:
switch_to_g2 = False
g2.switch()
print('\tLEAVE TRACE', *args)
def g1_run():
print('In g1_run')
global switch_to_g2
switch_to_g2 = True
from_parent = greenlet.getcurrent().parent.switch()
print('Return to g1_run')
print('From parent', from_parent)
def g2_run():
#g1.switch()
greenlet.getcurrent().parent.switch()
greenlet.settrace(tracefunc)
g1 = greenlet.greenlet(g1_run)
g2 = greenlet.greenlet(g2_run)
# This switch didn't actually finish!
# And if it did, it would raise TypeError
# because g1_run() doesn't take any arguments.
g1.switch(1)
print('Back in main')
g1.switch(2)