Twisted: How to sendLine from outside the LineReceiver class?

Q

Qp

Hello. I'm building a simple chat server and client interface, and I've got
everything working except for this:

While the client's basic.LineReceiver protocol class can sendLine when a
connection is made (that is, when the class is called automatically),
whenever I try to call sendLine from another class (my ClientFactory class)
then I get the following error.

"in sendLine
return self.transport.write(line + self.delimiter)
AttributeError: 'NoneType' object has no attribute 'write'"

So, I assume the instance of the LineReceiver is not set up properly. How
do I do this?

The code I am using for the classes is as follows. The error comes when I
call chatFactory.sendMessage():
class chatClient(basic.LineReceiver):

def connectionMade(self):
self.sendLine("A new person has entered the room!")

def lineReceived(self, line):
app.text_output.config(state=NORMAL) #enable text_output for insert
app.text_output.insert(END, line +"\n")
app.text_output.config(state=DISABLED)
app.text_input.delete(0, END) #clear text_input
app.text_output.see(END) #move scrollbar to the bottom

def connectionLost(self, reason):
reactor.stop()

class chatFactory(protocol.ClientFactory):
protocol = chatClient

def clientConnectionFailed(self, connector, reason):
reactor.stop()

def clientConnectionLost(self, connector, reason):
reactor.stop()

def sendMessage(self):
c = chatClient()
c.sendLine("Hey there")
<<<
 
A

Andrew Bennetts

[You'll probably get more answers to Twisted questions on the Twisted
mailing-list: (e-mail address removed)]

Hello. I'm building a simple chat server and client interface, and I've got
everything working except for this:

While the client's basic.LineReceiver protocol class can sendLine when a
connection is made (that is, when the class is called automatically),
whenever I try to call sendLine from another class (my ClientFactory class)
then I get the following error.

"in sendLine
return self.transport.write(line + self.delimiter)
AttributeError: 'NoneType' object has no attribute 'write'"

So, I assume the instance of the LineReceiver is not set up properly. How
do I do this?

The code I am using for the classes is as follows. The error comes when I
call chatFactory.sendMessage():
[...]
class chatFactory(protocol.ClientFactory):
protocol = chatClient
[...]
def sendMessage(self):
c = chatClient()
c.sendLine("Hey there")

I see you are using ClientFactory, so I presume you want to make a client
connection to a remote server with this protocol.

You need to connect your protocol, not just create the object out of thin
air. See:

http://twistedmatrix.com/documents/howto/clients

and also:

http://twistedmatrix.com/documents/...ternet.interfaces.IReactorTCP.html#connectTCP

I think the easiest way to do what you need is to have the protocol's
connectionMade signal the factory:

----
class ChatClient(basic.LineReceiver):

def connectionMade(self):
self.sendLine("A new person has entered the room!")
self.factory.clientReady(self)

def lineReceived(self, line):
app.text_output.config(state=NORMAL) #enable text_output for insert
app.text_output.insert(END, line +"\n")
app.text_output.config(state=DISABLED)
app.text_input.delete(0, END) #clear text_input
app.text_output.see(END) #move scrollbar to the bottom

def connectionLost(self, reason):
reactor.stop()

class ChatFactory(protocol.ClientFactory):
protocol = ChatClient

def clientConnectionFailed(self, connector, reason):
reactor.stop()

def clientConnectionLost(self, connector, reason):
reactor.stop()

def startFactory(self):
self.messageQueue = []
self.clientInstance = None

def clientReady(self, instance):
self.clientInstance = instance
for msg in self.messageQueue:
self.sendMessage(msg)

def sendMessage(self, msg='Hey there'):
if self.clientInstance is not None:
self.clientInstance.sendLine(msg)
else:
self.messageQueue.append(msg)
----

Note that I've added a simple message queue because it may take some time
for the connection to be established, so calls to sendMessage could fail if
called too soon. The queue avoids that problem.

A better solution might be to not write the factory at all, and use
twisted.internet.protocol.clientCreator:

http://twistedmatrix.com/documents/current/api/twisted.internet.protocol.ClientCreator.html

----
from twisted.python import log

# Create creator and connect
clientCreator = protocol.ClientCreator(reactor, ChatClient)
deferred = clientCreator.connectTCP(host, port)

# When connected, send a line
def connectionReady(protocol):
protocol.sendLine('Hey there')
deferred.addCallback(connectionReady)

# Log any errors that occur (such as connection failed)
deferred.addErrback(log.err)
 
Q

Qp

Thanks a ton! It all makes sense now, and it works in a much better way I
was thinking about (somehow returning a working transport object).

That helps a whole lot...

Thanks again,
QP

Andrew Bennetts said:
[You'll probably get more answers to Twisted questions on the Twisted
mailing-list: (e-mail address removed)]

Hello. I'm building a simple chat server and client interface, and I've got
everything working except for this:

While the client's basic.LineReceiver protocol class can sendLine when a
connection is made (that is, when the class is called automatically),
whenever I try to call sendLine from another class (my ClientFactory class)
then I get the following error.

"in sendLine
return self.transport.write(line + self.delimiter)
AttributeError: 'NoneType' object has no attribute 'write'"

So, I assume the instance of the LineReceiver is not set up properly. How
do I do this?

The code I am using for the classes is as follows. The error comes when I
call chatFactory.sendMessage():
[...]
class chatFactory(protocol.ClientFactory):
protocol = chatClient
[...]
def sendMessage(self):
c = chatClient()
c.sendLine("Hey there")

I see you are using ClientFactory, so I presume you want to make a client
connection to a remote server with this protocol.

You need to connect your protocol, not just create the object out of thin
air. See:

http://twistedmatrix.com/documents/howto/clients

and also:

http://twistedmatrix.com/documents/current/api/twisted.internet.interfaces.I
ReactorTCP.html#connectTCP

I think the easiest way to do what you need is to have the protocol's
connectionMade signal the factory:

----
class ChatClient(basic.LineReceiver):

def connectionMade(self):
self.sendLine("A new person has entered the room!")
self.factory.clientReady(self)

def lineReceived(self, line):
app.text_output.config(state=NORMAL) #enable text_output for insert
app.text_output.insert(END, line +"\n")
app.text_output.config(state=DISABLED)
app.text_input.delete(0, END) #clear text_input
app.text_output.see(END) #move scrollbar to the bottom

def connectionLost(self, reason):
reactor.stop()

class ChatFactory(protocol.ClientFactory):
protocol = ChatClient

def clientConnectionFailed(self, connector, reason):
reactor.stop()

def clientConnectionLost(self, connector, reason):
reactor.stop()

def startFactory(self):
self.messageQueue = []
self.clientInstance = None

def clientReady(self, instance):
self.clientInstance = instance
for msg in self.messageQueue:
self.sendMessage(msg)

def sendMessage(self, msg='Hey there'):
if self.clientInstance is not None:
self.clientInstance.sendLine(msg)
else:
self.messageQueue.append(msg)
----

Note that I've added a simple message queue because it may take some time
for the connection to be established, so calls to sendMessage could fail if
called too soon. The queue avoids that problem.

A better solution might be to not write the factory at all, and use
twisted.internet.protocol.clientCreator:

http://twistedmatrix.com/documents/current/api/twisted.internet.protocol.Cli
entCreator.html

----
from twisted.python import log

# Create creator and connect
clientCreator = protocol.ClientCreator(reactor, ChatClient)
deferred = clientCreator.connectTCP(host, port)

# When connected, send a line
def connectionReady(protocol):
protocol.sendLine('Hey there')
deferred.addCallback(connectionReady)

# Log any errors that occur (such as connection failed)
deferred.addErrback(log.err)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,151
Latest member
JaclynMarl
Top