Skip to content

Instantly share code, notes, and snippets.

@zzzeek
Created July 8, 2020 04:13
Show Gist options
  • Select an option

  • Save zzzeek/f5ac058e30e192f11160ffb52f5224fa to your computer and use it in GitHub Desktop.

Select an option

Save zzzeek/f5ac058e30e192f11160ffb52f5224fa to your computer and use it in GitHub Desktop.
do we need to worry about nesting of greenlet_spawn / await_()? seems like not
import asyncio
import contextvars
import sys
import greenlet
current_greenlet_context = contextvars.ContextVar("current greenlet context")
def await_(coroutine):
current_greenlet = current_greenlet_context.get()
if current_greenlet is None:
raise Exception(
"not running inside a greenlet right now, "
"can't use await_() function"
)
return current_greenlet.switch(coroutine)
async def greenlet_spawn(fn, *args):
result_future = asyncio.Future()
def run_greenlet_target():
result_future.set_result(fn(*args))
return None
async def run_greenlet():
gl = greenlet.greenlet(run_greenlet_target)
greenlet_coroutine = gl.switch()
while greenlet_coroutine is not None:
task = asyncio.create_task(greenlet_coroutine)
try:
await task
except:
# this allows an exception to be raised within
# the moderated greenlet so that it can continue
# its expected flow.
greenlet_coroutine = gl.throw(*sys.exc_info())
else:
greenlet_coroutine = gl.switch(task.result())
current_greenlet = greenlet.greenlet(run_greenlet)
print("set greenlet context to: %s" % run_greenlet)
current_greenlet_context.set(current_greenlet)
try:
await current_greenlet.switch()
finally:
# make sure the context is what we set it to, testing
# that a nested call has no issue
assert current_greenlet_context.get() is current_greenlet
print("reset greenlet context None from: %s" % run_greenlet)
current_greenlet_context.set(None)
return result_future.result()
async def func1():
print("func1")
await greenlet_spawn(func2)
def func2():
print("func2")
await_(func3())
async def func3():
print("func3")
await greenlet_spawn(func4)
def func4():
print("func4")
await_(asyncio.sleep(1))
asyncio.run(func1())
@zzzeek
Copy link
Copy Markdown
Author

zzzeek commented Jul 8, 2020

output:


$ python test4.py 
func1
set greenlet context to: <function greenlet_spawn.<locals>.run_greenlet at 0x7f8573f94a60>
func2
func3
set greenlet context to: <function greenlet_spawn.<locals>.run_greenlet at 0x7f8573f94b80>
func4
reset greenlet context None from: <function greenlet_spawn.<locals>.run_greenlet at 0x7f8573f94b80>
reset greenlet context None from: <function greenlet_spawn.<locals>.run_greenlet at 0x7f8573f94a60>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment