*(Migrating this thread from the python-ideas list)*
Tulip does not appear to address a scenario of mine where I need different
parts of the same coroutine to run on specific threads or event loops.
Consider the following code which needs different parts executed on a UI
thread, database thread, and background thread:
def tree_node_clicked(tree_node):
print('Clicked: ' + repr(tree_node))
def db_task():
db_node = fetch_node_from_db(tree_node.url)
def process_node(db_node):
tree_node.set_contents(db_node.contents)
# (done)
if db_node is not None:
def bg_task():
db_node = fetch_node_from_internet(tree_node.url)
def db_task():
insert_into_db(db_node)
ui_call_soon(process_node, db_node)
db_call_soon(db_task)
bg_call_soon(bg_task)
else:
ui_call_soon(process_node, db_node)
db_call_soon(db_task)
Imagine if you could write this function like the following:
ui_loop = ...
db_loop = ...
bg_loop = ...
@asyncio.coroutine
def tree_node_clicked(tree_node):
print('Clicked: ' + repr(tree_node))
yield from switch_to_loop(db_loop)
db_node = fetch_node_from_db(tree_node)
if db_node is not None:
yield from switch_to_loop(bg_loop)
db_node = fetch_node_from_internet(tree_node.url)
yield from switch_to_loop(db_loop)
insert_into_db(db_node)
yield from switch_to_loop(ui_loop)
tree_node.set_contents(db_node.contents)
(The preceding relies on a hypothetical *switch_to_loop* primitive.)
Or even better: If you created a decorator like *@loop_affinity* that
automatically called switch_to_loop(...) if the current event loop wasn't
correct, you could even write the following even-more simplified version:
@asyncio.coroutine
def tree_node_clicked(tree_node):
print('Clicked: ' + repr(tree_node))
db_node = yield from fetch_node_from_db(tree_node)
if db_node is not None:
db_node = yield from fetch_node_from_internet(tree_node.url)
yield from insert_into_db(db_node)
yield from tree_node.set_contents(db_node.contents)
@loop_affinity(db_loop)
def fetch_node_from_db(...): ...
@loop_affinity(bg_loop)
def fetch_node_from_internet(...): ...
@loop_affinity(db_loop)
def insert_into_db(...): ...
@loop_affinity(ui_loop)
def set_contents(...): ...
Wouldn't that be just grand?
Is there a way to implement a primitive similar to switch_to_loop(...) on
top of existing primitives in Tulip? Or would that be considered a new
feature?
Jonathan Slenders and Guido suggested on python-ideas that I would be able
to implement this idea (although perhaps not switch_to_loop) using
run_in_executor. Regarding specifically the implementation
of switch_to_loop, it is not clear to me how I could get access to the next
part of the current coroutine which I would need to pass to run_in_executor
(or something similar).