I’m trying to write a dead-simple interface for an IRC library, like so:
import simpleirc
connection = simpleirc.Connect('irc.freenode.net', 6667)
channel = connection.join('foo')
find_command = re.compile(r'google ([a-z]+)').findall
for msg in channel:
for t in find_command(msg):
channel.say("http://google.com/search?q=%s" % t)
Working from their example, I’m running into trouble (code is a bit lengthy, so I pasted it here). Since the call to channel.__next__ needs to be returned when the callback <IRCClient instance>.privmsg is called, there doesn’t seem to be a clean option. Using exceptions or threads seems like the wrong thing here, is there a simpler (blocking?) way of using twisted that would make this possible?
In general, if you’re trying to use Twisted in a “blocking” way, you’re going to run into a lot of difficulties, because that’s neither the way it’s intended to be used, nor the way in which most people use it.
Going with the flow is generally a lot easier, and in this case, that means embracing callbacks. The callback-style solution to your question would look something like this:
This isn’t absolutely required (but I strongly suggest you consider trying it). One alternative is
inlineCallbacks:Notice no more
addCallbacks. It’s been replaced byyieldin a decorated generator function. This could get even closer to what you asked for if you had a version ofGooglerwith a different API (the one above should work withIRCClientfrom Twisted as it is written – though I didn’t test it). It would be entirely possible forGoogler.jointo return aChannelobject of some sort, and for thatChannelobject to be iterable like this:It’s only a matter of implementing this API on top of the ones already present. Of course, the
yieldexpressions are still there, and I don’t know how much this will upset you. 😉It’s possible to go still further away from callbacks and make the context switches necessary for asynchronous operation to work completely invisible. This is bad for the same reason it would be bad for sidewalks outside your house to be littered with invisible bear traps. However, it’s possible. Using something like corotwine, itself based on a third-party coroutine library for CPython, you can have the implementation of
Channeldo the context switching itself, rather than requiring the calling application code to do it. The result might look something like:with an implementation of
Channelthat might look something like:This in turn depends on a new
Googlermethod,getNextMessage, which is a straightforward feature addition based on existingIRCClientcallbacks:To run this, you create a new greenlet for the
runfunction and switch to it, and then start the reactor.When
rungets to its first asynchronous operation, it switches back to the reactor greenlet (which is the “main” greenlet in this case, but it doesn’t really matter) to let the asynchronous operation complete. When it completes, corotwine turns the callback into a greenlet switch back intorun. Sorunis granted the illusion of running straight through, like a “normal” synchronous program. Keep in mind that it is just an illusion, though.So, it’s possible to get as far away from the callback-oriented style that is most commonly used with Twisted as you want. It’s not necessarily a good idea, though.