# Copyright (c) 2001-2004 Twisted Matrix Laboratories. # See LICENSE for details. # # Author: Clark Evans (cce@clarkevans.com) # """ flow.protocol This allows one to use flow module to create protocols, a protocol is actually a controller, but it is specialized enough to deserve its own module. """ import types from base import * from wrap import wrap from stage import Callback from twisted.internet import protocol from twisted.internet.error import ConnectionLost, ConnectionDone def makeProtocol(controller, baseClass = protocol.Protocol, *callbacks, **kwargs): """ Construct a flow based protocol This takes a base protocol class, and a set of callbacks and creates a connection flow based on the two. For example, the following would build a simple 'echo' protocol:: from __future__ import generators from twisted.internet import reactor, protocol from twisted.flow import flow PORT = 8392 def echoServer(conn): yield conn for data in conn: conn.write(data) yield conn def echoClient(conn): conn.write("hello, world!") yield conn print "server said: ", conn.next() reactor.callLater(0,reactor.stop) server = protocol.ServerFactory() server.protocol = flow.makeProtocol(echoServer) reactor.listenTCP(PORT,server) client = protocol.ClientFactory() client.protocol = flow.makeProtocol(echoClient) reactor.connectTCP("localhost", PORT, client) reactor.run() Of course, the best part about flow is that you can nest stages. Therefore it is quite easy to make a lineBreaker generator which takes an input connection and produces and output connection. Anyway, the code is almost identical as far as the client/server is concerned:: # this is a filter generator, it consumes from the # incoming connection, and yields results to # the next stage, the echoServer below def lineBreaker(conn, lineEnding = "\\n"): lst = [] yield conn for chunk in conn: pos = chunk.find(lineEnding) if pos > -1: lst.append(chunk[:pos]) yield "".join(lst) lst = [chunk[pos+1:]] else: lst.append(chunk) yield conn yield "".join(lst) # note that this class is only slightly modified, # simply comment out the line breaker line to see # how the server behaves without the filter... def echoServer(conn): lines = flow.wrap(lineBreaker(conn)) yield lines for data in lines: conn.write(data) yield lines # and the only thing that is changed is that we # are sending data in strange chunks, and even # putting the last chunk on hold for 2 seconds. def echoClient(conn): conn.write("Good Morning!\\nPlease ") yield conn print "server said: ", conn.next() conn.write("do not disregard ") reactor.callLater(2, conn.write, "this.\\n") yield conn print "server said: ", conn.next() reactor.callLater(0,reactor.stop) """ if not callbacks: callbacks = ('dataReceived',) trap = kwargs.get("trap", tuple()) class _Protocol(Controller, Callback, baseClass): def __init__(self): Callback.__init__(self, *trap) setattr(self, callbacks[0], self) # TODO: support more than one callback via Concurrent def _execute(self, dummy = None): cmd = self._controller self.write = self.transport.write while True: instruction = cmd._yield() if instruction: if isinstance(instruction, CallLater): instruction.callLater(self._execute) return raise Unsupported(instruction) if cmd.stop: self.transport.loseConnection() return if cmd.failure: self.transport.loseConnection() cmd.failure.trap() return if cmd.results: self.transport.writeSequence(cmd.results) cmd.results = [] def connectionMade(self): if types.ClassType == type(self.controller): self._controller = wrap(self.controller(self)) else: self._controller = wrap(self.controller()) self._execute() def connectionLost(self, reason=protocol.connectionDone): if isinstance(reason.value, ConnectionDone) or \ (isinstance(reason.value, ConnectionLost) and \ self.finishOnConnectionLost): self.finish() else: self.errback(reason) self._execute() _Protocol.finishOnConnectionLost = kwargs.get("finishOnConnectionLost",True) _Protocol.controller = controller return _Protocol def _NotImplController(protocol): raise NotImplementedError Protocol = makeProtocol(_NotImplController) Protocol.__doc__ = """ A concrete flow.Protocol for inheritance """