1
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
2
# See LICENSE for details.
5
# Author: Clark Evans (cce@clarkevans.com)
11
This allows one to use flow module to create protocols, a protocol is actually
12
a controller, but it is specialized enough to deserve its own module.
18
from stage import Callback
19
from twisted.internet import protocol
20
from twisted.internet.error import ConnectionLost, ConnectionDone
22
def makeProtocol(controller, baseClass = protocol.Protocol,
23
*callbacks, **kwargs):
25
Construct a flow based protocol
27
This takes a base protocol class, and a set of callbacks and creates a
28
connection flow based on the two. For example, the following would build a
29
simple 'echo' protocol::
31
from __future__ import generators
32
from twisted.internet import reactor, protocol
33
from twisted.flow import flow
43
conn.write("hello, world!")
45
print "server said: ", conn.next()
46
reactor.callLater(0,reactor.stop)
48
server = protocol.ServerFactory()
49
server.protocol = flow.makeProtocol(echoServer)
50
reactor.listenTCP(PORT,server)
51
client = protocol.ClientFactory()
52
client.protocol = flow.makeProtocol(echoClient)
53
reactor.connectTCP("localhost", PORT, client)
56
Of course, the best part about flow is that you can nest stages. Therefore
57
it is quite easy to make a lineBreaker generator which takes an input
58
connection and produces and output connection. Anyway, the code is almost
59
identical as far as the client/server is concerned::
61
# this is a filter generator, it consumes from the
62
# incoming connection, and yields results to
63
# the next stage, the echoServer below
64
def lineBreaker(conn, lineEnding = "\\n"):
68
pos = chunk.find(lineEnding)
70
lst.append(chunk[:pos])
78
# note that this class is only slightly modified,
79
# simply comment out the line breaker line to see
80
# how the server behaves without the filter...
82
lines = flow.wrap(lineBreaker(conn))
88
# and the only thing that is changed is that we
89
# are sending data in strange chunks, and even
90
# putting the last chunk on hold for 2 seconds.
92
conn.write("Good Morning!\\nPlease ")
94
print "server said: ", conn.next()
95
conn.write("do not disregard ")
96
reactor.callLater(2, conn.write, "this.\\n")
98
print "server said: ", conn.next()
99
reactor.callLater(0,reactor.stop)
102
callbacks = ('dataReceived',)
103
trap = kwargs.get("trap", tuple())
104
class _Protocol(Controller, Callback, baseClass):
106
Callback.__init__(self, *trap)
107
setattr(self, callbacks[0], self)
108
# TODO: support more than one callback via Concurrent
109
def _execute(self, dummy = None):
110
cmd = self._controller
111
self.write = self.transport.write
113
instruction = cmd._yield()
115
if isinstance(instruction, CallLater):
116
instruction.callLater(self._execute)
118
raise Unsupported(instruction)
120
self.transport.loseConnection()
123
self.transport.loseConnection()
127
self.transport.writeSequence(cmd.results)
129
def connectionMade(self):
130
if types.ClassType == type(self.controller):
131
self._controller = wrap(self.controller(self))
133
self._controller = wrap(self.controller())
135
def connectionLost(self, reason=protocol.connectionDone):
136
if isinstance(reason.value, ConnectionDone) or \
137
(isinstance(reason.value, ConnectionLost) and \
138
self.finishOnConnectionLost):
143
_Protocol.finishOnConnectionLost = kwargs.get("finishOnConnectionLost",True)
144
_Protocol.controller = controller
147
def _NotImplController(protocol):
148
raise NotImplementedError
149
Protocol = makeProtocol(_NotImplController)
150
Protocol.__doc__ = """ A concrete flow.Protocol for inheritance """