Re: Why doesn't this asyncore.dispatcher.handle_read() get called?

2011-04-20 Thread Jean-Paul Calderone
On Apr 20, 12:25 pm, Dun Peal dunpea...@gmail.com wrote:
 Hi,

 I'm writing and testing an asyncore-based server. Unfortunately, it
 doesn't seem to work. The code below is based on the official docs and
 examples, and starts a listening and sending dispatcher, where the
 sending dispatcher connects and sends a message to the listener - yet
 Handler.handle_read() never gets called, and I'm not sure why. Any
 ideas?

 Thanks, D.

 import asyncore, socket, sys

 COMM_PORT = 9345

 class Handler(asyncore.dispatcher):
     def handle_read(self):
         print 'This never prints'

 class Listener(asyncore.dispatcher):
     def __init__(self, port=COMM_PORT):
         asyncore.dispatcher.__init__(self)
         self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
         self.set_reuse_addr()
         self.bind(('', port))
         self.listen(5)

     def handle_accept(self):
         client, addr = self.accept()
         print 'This prints.'
         return Handler(client)

 class Sender(asyncore.dispatcher):
     def __init__(self, host):
         asyncore.dispatcher.__init__(self)
         self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
         self.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
         self.connect( (host, COMM_PORT) )
         self.buffer = 'Msg\r\n'

     def handle_connect(self):
         pass

     def writable(self):
         return len(self.buffer)  0

     def handle_write(self):
         sent = self.send(self.buffer)
         self.buffer = self.buffer[sent:]

 def test_communication():
     from multiprocessing import Process
     def listener():
         l = Listener()
         asyncore.loop(timeout=10, count=1)
     lis = Process(target=listener)
     lis.start()
     def sender():
         s = Sender('localhost')
         asyncore.loop(timeout=10, count=1)
     sen = Process(target=sender)
     sen.start()
     lis.join()

 test_communication()

You didn't let the program run long enough for the later events to
happen.  loop(count=1) basically means one I/O event will be processed
- in the case of your example, that's an accept().  Then asyncore is
done and it never gets to your custom handle_read.

So you can try passing a higher count to loop, or you can add your own
loop around the loop call.  Or you can switch to Twisted which
actually makes testing a lot easier than this - no need to spawn
multiple processes or call accept or recv yourself.  Here's a somewhat
equivalent Twisted-based version of your program:

from twisted.internet.protocol import ServerFactory, Protocol
from twisted.internet import reactor

factory = ServerFactory()
factory.protocol = Protocol

reactor.listenTCP(0, factory)
reactor.run()

It's hard to write the equivalent unit test, because the test you
wrote for the asyncore-based version is testing lots of low level
details which, as you can see, don't actually appear in the Twisted-
based version because Twisted does them for you already.  However,
once you get past all that low-level stuff and get to the part where
you actually implement some of your application logic, you might have
tests for your protocol implementation that look something like this:

from twisted.trial.unittest import TestCase
from twisted.test.proto_helpers import StringTransport

from yourapp import Handler # Or a better name

class HandlerTests(TestCase):
def test_someMessage(self):

When the X message is received, the Y response is sent
back.

transport = StringTransport()
protocol = Handler()
protocol.makeConnection(transport)
protocol.dataReceived(X)
self.assertEqual(transport.value(), Y)

Hope this helps,
Jean-Paul

-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Why doesn't this asyncore.dispatcher.handle_read() get called?

2011-04-20 Thread Dun Peal
On Apr 20, 3:01 pm, Jean-Paul Calderone calderone.jeanp...@gmail.com
wrote:
 You didn't let the program run long enough for the later events to
 happen.  loop(count=1) basically means one I/O event will be processed
 - in the case of your example, that's an accept().  Then asyncore is
 done and it never gets to your custom handle_read.

Wow, a response from a Twisted founder and core developer =)

You were right, of course. Incrementing the count to 2 on the
asyncore.loop() calls makes the snippet work as expected.

I'd definitely rather use Twisted. It's unclear why transmitting a
bytestream message between two servers should require low-level socket
incantations such as `self.create_socket(socket.AF_INET,
socket.SOCK_STREAM)` or `self.setsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR, 1)`. The stdlib should offer a higher-level
asynchronous communication abstraction to support such a
straightforward usecase. Twisted does provide that, but my humble
needs can't justify the extra dependency cost.

Thanks a lot, D.
-- 
http://mail.python.org/mailman/listinfo/python-list