There has been much talk of the use of async libraries (such as Tulip) within the general programming community to avoid the "christmas tree" pattern of code indentation in code that uses a lot of callbacks. For example, consider the following callback-based code: def duplicate_file_async(filepath, done): def exists_async(does_exist): if not does_exist: done(False) return def entire_file_read(contents): if not contents: done(False) return def file_written(success): done(success) write_entire_file_async(filepath + ' copy', contents, file_written) read_entire_file_async(filepath, entire_file_read) exists_async(filepath) This code be rewritten in Tulip as: @async.coroutine def duplicate_file_async(filepath): if not yield from exists_async(filepath): return False contents = yield from read_entire_file_async(filepath) if not contents: return False return yield from write_entire_file_async(filepath + ' copy', contents) Much more clear, no? Tulip, however, does not appear to address a slightly different problem I run into where I need different parts of the same conceptual function to run on specific *different* threads. 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) 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 = ... @async.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) Or even better: If you created a decorators like @loop_affinity(*_loop) that automatically called switch_to_loop(...) if the current event loop wasn't correct, you could even write the following even-more simplified version: @async.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) @async.loop_affinity(db_loop) def fetch_node_from_db(...): ... @async.loop_affinity(bg_loop) def fetch_node_from_internet(...): ... @async.loop_affinity(db_loop) def insert_into_db(...): ... @async.loop_affinity(ui_loop) def set_contents(...): ... Wouldn't that be just grand? I have prototyped switch_to_loop(...) successfully in Tulip, albeit with a race condition I was unable to isolate. How about some equivalent to switch_to_loop(...) and loop_affinity(...) in a future version of Tulip? -- David Foster http://dafoster.net/
Do you know about run_in_executor? http://docs.python.org/3.4/library/asyncio-eventloop.html#asyncio.BaseEventL... 2014-02-12 10:24 GMT+01:00 David Foster <davidfstr@gmail.com>:
There has been much talk of the use of async libraries (such as Tulip) within the general programming community to avoid the "christmas tree" pattern of code indentation in code that uses a lot of callbacks. For example, consider the following callback-based code:
def duplicate_file_async(filepath, done): def exists_async(does_exist): if not does_exist: done(False) return def entire_file_read(contents): if not contents: done(False) return def file_written(success): done(success) write_entire_file_async(filepath + ' copy', contents, file_written) read_entire_file_async(filepath, entire_file_read) exists_async(filepath)
This code be rewritten in Tulip as:
@async.coroutine def duplicate_file_async(filepath): if not yield from exists_async(filepath): return False contents = yield from read_entire_file_async(filepath) if not contents: return False return yield from write_entire_file_async(filepath + ' copy', contents)
Much more clear, no?
Tulip, however, does not appear to address a slightly different problem I run into where I need different parts of the same conceptual function to run on specific *different* threads. 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)
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 = ...
@async.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)
Or even better: If you created a decorators like @loop_affinity(*_loop) that automatically called switch_to_loop(...) if the current event loop wasn't correct, you could even write the following even-more simplified version:
@async.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)
@async.loop_affinity(db_loop) def fetch_node_from_db(...): ...
@async.loop_affinity(bg_loop) def fetch_node_from_internet(...): ...
@async.loop_affinity(db_loop) def insert_into_db(...): ...
@async.loop_affinity(ui_loop) def set_contents(...): ...
Wouldn't that be just grand? I have prototyped switch_to_loop(...) successfully in Tulip, albeit with a race condition I was unable to isolate.
How about some equivalent to switch_to_loop(...) and loop_affinity(...) in a future version of Tulip?
-- David Foster http://dafoster.net/ _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On 2/12/2014 4:24 AM, David Foster wrote:
Tulip, however, does not appear to address a slightly different problem I run into where I need different parts of the same conceptual function to run on specific *different* threads. Consider the following code which needs different parts executed on a UI thread, database thread, and background thread:
... There is a tulip google group where tulip discussion takes place. I don't have the specific reference though. -- Terry Jan Reedy
On 12 Feb 2014 19:54, "Terry Reedy" <tjreedy@udel.edu> wrote:
On 2/12/2014 4:24 AM, David Foster wrote:
Tulip, however, does not appear to address a slightly different problem I run into where I need different parts of the same conceptual function to run on specific *different* threads. Consider the following code which needs different parts executed on a UI thread, database thread, and background thread:
... There is a tulip google group where tulip discussion takes place. I don't
have the specific reference though. With 3.4 nearing release, I suspect we're going to have to get used to the idea of gently redirecting pip and asyncio/tulip discussions :) An update to the "communications" section of the dev guide may be in order, too. Cheers, Nick.
-- Terry Jan Reedy
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
participants (4)
-
David Foster
-
Jonathan Slenders
-
Nick Coghlan
-
Terry Reedy