Put on Your Monocle and Do Some Async Programming
Join the DZone community and get the full member experience.
Join For FreeGreg and Steven Hazel from Sauce Labs have recently built what they call, "An async programming framework with a blocking look-alike syntax". This framework, named Monocle, is focused on being portable between event-driven I/O frameworks. Currently, Monocle supports the Twisted and Tornado frameworks.


For those who haven't heard of Sauce Labs, they were co-founded by John Huggins, the creator of Selenium. Sauce Labs' free and commercial tools build on top of the core Selenium testing framework.
The emergence of Monocle could indicate that Sauce Labs is taking a more focused look at event-driven code and its role in concurrent web performance. Event-driven code is efficient and intuitive, but sometimes procedures are are split up and code is expanded in a not-so-good way.
Anytime you do I/O, you have to register a new handler and then return to the event loop so that you can let other things happen while the I/O finishes. Hazel and Hazel said, "It would be nice if we had some way to tell the event loop to call back into the middle of our function, so we could just continue where we left off." In Python, they found a mechanism that can do just that.
The mechanisms are called generators, and Monocle uses this Python syntax supported by Python 2.5 and up. The generators in Monocle straighten out event-based code such as this blocking code (expanded into four functions):
Here's a basic Monocle program running two concurrent processes called "o-routines" using Tornado's event loop. One process is an HTTP server, and the other makes an HTTP request:
Check out Monocle on GitHub.


For those who haven't heard of Sauce Labs, they were co-founded by John Huggins, the creator of Selenium. Sauce Labs' free and commercial tools build on top of the core Selenium testing framework.
The emergence of Monocle could indicate that Sauce Labs is taking a more focused look at event-driven code and its role in concurrent web performance. Event-driven code is efficient and intuitive, but sometimes procedures are are split up and code is expanded in a not-so-good way.
Anytime you do I/O, you have to register a new handler and then return to the event loop so that you can let other things happen while the I/O finishes. Hazel and Hazel said, "It would be nice if we had some way to tell the event loop to call back into the middle of our function, so we could just continue where we left off." In Python, they found a mechanism that can do just that.
The mechanisms are called generators, and Monocle uses this Python syntax supported by Python 2.5 and up. The generators in Monocle straighten out event-based code such as this blocking code (expanded into four functions):

def get_cmd(conn):Which is transformed into code like this, via Monocle:
conn.read_until("\n", callback=handle_cmd)
def handle_cmd(conn, cmd):
if cmd.type == "get-address":
# keep track of the conn so we can write the response back!
def callback(result):
handle_user_query_result(conn, result)
db.query(cmd.username, callback)
else:
conn.write("unknown command")
def handle_user_query_result(conn, user):
conn.write(user.address)
@_oSauce Labs believes Monocle will make code easier to work with than multi-threaded code, thanks to its "cooperative concurrency" approach.
def do_cmd(conn):
cmd = yield conn.read_until("\n")
if cmd.type == "get-address":
user = yield db.query(cmd.username)
yield conn.write(user.address)
else:
yield conn.write("unknown command")
Here's a basic Monocle program running two concurrent processes called "o-routines" using Tornado's event loop. One process is an HTTP server, and the other makes an HTTP request:
import monocleAnd instead of @monocle.o, you can use the don the monocle (@_o) as a shortcut:
monocle.init("tornado")
from monocle.stack import eventloop
from monocle.stack.network import add_service
from monocle.stack.network.http import HttpClient, HttpHeaders, HttpServer, http_respond
@monocle.o
def hello_http(req):
content = "Hello, World!"
headers = HttpHeaders()
headers['Content-Length'] = len(content)
headers['Content-Type'] = 'text/plain'
yield http_respond(req, 200, headers, content)
@monocle.o
def request():
client = HttpClient()
resp = yield client.request('http://127.0.0.1:8088/')
print resp.code, resp.body
add_service(HttpServer(hello_http, 8088))
monocle.launch(request())
eventloop.run()
from monocle import _oHazel and Hazel admit, "this violates Python's convention that underscores indicate variables for internal use. But rules are for breaking. Live a little."
@_o
def request():
client = HttpClient()
resp = yield client.request('http://127.0.0.1:8088/')
print resp.code, resp.body
Check out Monocle on GitHub.
Opinions expressed by DZone contributors are their own.
Comments