rarify
rarify

Reputation: 109

Twisted Protocol Instance Variables?

CLIENT:

#!/usr/bin/env python

from twisted.internet import reactor, protocol

class EchoClient(protocol.Protocol):
    def __init__(self, arg):
        self.arg = arg

    def connectionMade(self):
        self.transport.write("hello, world!")

    def dataReceived(self, data):
        print "Server said:", data
        self.transport.loseConnection()

    def connectionLost(self, reason):
        print "connection lost"

class EchoFactory(protocol.ClientFactory):
    protocol = EchoClient

    def buildProtocol(self, address):
        proto = protocol.ClientFactory.buildProtocol(self, address, 12)
        self.connectedProtocol = proto
        return proto

    def clientConnectionFailed(self, connector, reason):
        print "Connection failed - goodbye!"
        reactor.stop()

    def clientConnectionLost(self, connector, reason):
        print "Connection lost - goodbye!"
        reactor.stop()

def main():
    f = EchoFactory()
    reactor.connectTCP("localhost", 8000, f)
    reactor.run()

if __name__ == '__main__':
    main()

SERVER:

#!/usr/bin/env python

from twisted.internet import reactor, protocol
from twisted.application import service, internet

class Echo(protocol.Protocol):
    def dataReceived(self, data):
        self.transport.write(data)

def main():
    factory = protocol.ServerFactory()
    factory.protocol = Echo
    reactor.listenTCP(8000,factory)
    reactor.run()

if __name__ == '__main__':
    main()

ERROR:

exceptions.TypeError: buildProtocol() takes exactly 2 arguments (3 given)

QUESTION:

How can I get the EchoClient class in the CLIENT to accept parameters and assign instance variables ( such as arg in the EchoClient constructor above)? As noted below, it was previously suggested that I override the buildProtocol function, but my attempt at doing so has lead me to the above error. I am not really sure where to go from here. I suppose my question can be generalize to: how can I add instance variables to a protocol?

Upvotes: 2

Views: 2851

Answers (2)

SingleNegationElimination
SingleNegationElimination

Reputation: 156158

you wrote:

def buildProtocol(self, address):
    proto = protocol.ClientFactory.buildProtocol(self, address, 12)

that is, you are overriding ClientFactory.buildProtocol and calling the parent class with a different signature than it knows how to handle.

Passing data from the factory to the client is only a little tricky. You can provide any __init__ you want to the factory, but twisted creates instances of IProtocol itself. Fortunately, most factories assign themselves to the factory attribute of the protocol, once it's ready to go:

class MyClientProtocol(protocol.Protocol):
    def connectionMade(self):
        # use self.factory here:
        self.transport.write(self.factory.arg)

class MyClientFactory(protocol.ClientFactory):
    protocol = MyClientProtocol

    def __init__(self, arg):
        self.arg = arg

In fact, the whole ProtocolFactory business is to support this kind of use; but be mindful; many instances of Protocol will share a single instance of their factory; use the factory for configuration but manage state in the protocol.

It's certainly possible that the way the standard family of Protocol/Factory implementations don't suit your needs, and that's also reasonable, so long as you fully implement the IProtocol and IProtocolFactory interfaces. The base classes exist because they handle most of the cases for you, not because they are the only possible implementation.

Upvotes: 7

kirelagin
kirelagin

Reputation: 13616

It's not clear from your question what exactly your tryed and what exactly the error was, but anyway you have to do two steps:

  1. Make EchoClient's constructor take whatever arguments you need it to take and initialise whatever field you need it to initialise.
  2. Override buildProtocol method in your factory to supply those arguments to your protocol.

Upvotes: 1

Related Questions