Skip to content

Instantly share code, notes, and snippets.

@alpha-beta-soup
Created February 28, 2016 00:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alpha-beta-soup/37e47fa6ddccd3bb733f to your computer and use it in GitHub Desktop.
Save alpha-beta-soup/37e47fa6ddccd3bb733f to your computer and use it in GitHub Desktop.
# This is the Twisted Get Poetry Now! client, version 1.0.
# NOTE: This should not be used as the basis for production code.
# It uses low-level Twisted APIs as a learning exercise.
import datetime, errno, optparse, socket
from twisted.internet import main
def parse_args():
usage = """usage: %prog [options] [hostname]:port ...
This is the Get Poetry Now! client, Twisted version 1.0.
Run it like this:
python get-poetry.py port1 port2 port3 ...
If you are in the base directory of the twisted-intro package,
you could run it like this:
python twisted-client-1/get-poetry.py 10001 10002 10003
to grab poetry from servers on ports 10001, 10002, and 10003.
Of course, there need to be servers listening on those ports
for that to work.
"""
parser = optparse.OptionParser(usage)
_, addresses = parser.parse_args()
if not addresses:
print parser.format_help()
parser.exit()
def parse_address(addr):
if ':' not in addr:
host = '127.0.0.1'
port = addr
else:
host, port = addr.split(':', 1)
if not port.isdigit():
parser.error('Ports must be integers.')
return host, int(port)
return map(parse_address, addresses)
class PoetrySocket(object):
poem = ''
def __init__(self, task_num, address, complete_timeout=5):
self.task_num = task_num
self.address = address
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if self.sock.connect_ex(address) == 0:
self.sock.setblocking(0)
# tell the Twisted reactor to monitor this socket for reading
from twisted.internet import reactor
reactor.addReader(self)
else:
print "Could not connect to Task", self.task_num
self.timeout = reactor.callLater(complete_timeout, self.forceClose)
def fileno(self):
'''retutns the file descriptor we want to monitor'''
try:
return self.sock.fileno()
except socket.error:
return -1
def forceClose(self):
self.connectionLost('Poem did not complete in allowed timeframe')
def connectionLost(self, reason=None):
'''when the connection is closed'''
self.sock.close()
# stop monitoring this socket
from twisted.internet import reactor
print "Reactor no longer monitored: %s" % reason
reactor.removeReader(self)
# see if there are any poetry sockets left
for reader in reactor.getReaders():
if isinstance(reader, PoetrySocket):
return
reactor.stop() # no more poetry
def doRead(self):
'''
needed for addReader: any argument of that function needs to implement
the IReadDescriptor interface.
'''
bytes = ''
while True:
try:
bytesread = self.sock.recv(1024)
if not bytesread:
break
else:
bytes += bytesread
except socket.error, e:
if e.args[0] == errno.EWOULDBLOCK:
break
return main.CONNECTION_LOST
if not bytes:
print 'Task %d finished' % self.task_num
self.timeout.cancel()
return main.CONNECTION_DONE
else:
msg = 'Task %d: got %d bytes of poetry from %s'
print msg % (self.task_num, len(bytes), self.format_addr())
self.poem += bytes
def logPrefix(self):
'''we need to implmenet ILoggingContext, as IFileDescriptor inherits
from it'''
return 'poetry'
def format_addr(self):
host, port = self.address
return '%s:%s' % (host or '127.0.0.1', port)
def poetry_main():
addresses = parse_args()
start = datetime.datetime.now()
sockets = [PoetrySocket(i + 1, addr) for i, addr in enumerate(addresses)]
from twisted.internet import reactor
reactor.run()
elapsed = datetime.datetime.now() - start
for i, sock in enumerate(sockets):
print 'Task %d: %d bytes of poetry' % (i + 1, len(sock.poem))
print 'Got %d poems in %s' % (len(addresses), elapsed)
if __name__ == '__main__':
poetry_main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment