Finger演化:整理finger代码

The Evolution of Finger: cleaning up the finger code -- dreamingk [2004-08-09 02:09:38]

1. 介绍(Introduction)

这是Twisted教程《Twisted入门 - Finger演化》的第三部分。

This is the third part of the Twisted tutorial Twisted from Scratch, or The Evolution of Finger.

在本节教程中,我们将整理代码,以便让它更接近可读和可扩展的风格。

In this section of the tutorial, we'll clean up our code so that it is closer to a readable and extendable style.

1.1. 编写可读代码(Write Readable Code)

Application的上一个版本有许多的伎俩。我们避开了子类化,不支持在web上的用户列表之类的事情,和去掉所有的空行 -- 所有这些是为了让程序更短。这里我们将后退一步,子类化(更自然地说是一个子类)可以做一些接受多行的工作来处理它们,等等此类事情。这里显示了一种开发Twisted应用的更好的风格,尽管在前一阶段中的伎俩有时可以用在被丢弃的原型中。

The last version of the application had a lot of hacks. We avoided sub-classing, didn't support things like user listings over the web, and removed all blank lines -- all in the interest of code which is shorter. Here we take a step back, subclass what is more naturally a subclass, make things which should take multiple lines take them, etc. This shows a much better style of developing Twisted applications, though the hacks in the previous stages are sometimes used in throw-away prototypes.

   1 # Do everything properly
   2 from twisted.application import internet, service
   3 from twisted.internet import protocol, reactor, defer
   4 from twisted.protocols import basic, irc
   5 from twisted.web import resource, server, static, xmlrpc
   6 import cgi
   7 
   8 def catchError(err):
   9     return "Internal error in server"
  10 
  11 class FingerProtocol(basic.LineReceiver):
  12 
  13     def lineReceived(self, user):
  14         d = self.factory.getUser(user)
  15         d.addErrback(catchError)
  16         def writeValue(value):
  17             self.transport.write(value+'\n')
  18             self.transport.loseConnection()
  19         d.addCallback(writeValue)
  20 
  21 
  22 class FingerSetterProtocol(basic.LineReceiver):
  23 
  24     def connectionMade(self):
  25         self.lines = []
  26 
  27     def lineReceived(self, line):
  28         self.lines.append(line)
  29 
  30     def connectionLost(self, reason):
  31         self.factory.setUser(*self.lines[:2])
  32 
  33 
  34 class IRCReplyBot(irc.IRCClient):
  35 
  36     def connectionMade(self):
  37         self.nickname = self.factory.nickname
  38         irc.IRCClient.connectionMade(self)
  39 
  40     def privmsg(self, user, channel, msg):
  41         user = user.split('!')[0]
  42         if self.nickname.lower() == channel.lower():
  43             d = self.factory.getUser(msg)
  44             d.addErrback(catchError)
  45             d.addCallback(lambda m: "Status of %s: %s" % (msg, m))
  46             d.addCallback(lambda m: self.msg(user, m))
  47 
  48 
  49 class UserStatusTree(resource.Resource):
  50     def __init__(self, service):
  51         resource.Resource.__init__(self)
  52         self.service = service
  53 
  54     def render_GET(self, request):
  55         d = self.service.getUsers()
  56         def formatUsers(users):
  57             l = ['<li><a href="%s">%s</a></li>' % (user, user)
  58                  for user in users]
  59             return '<ul>'+''.join(l)+'</ul>'
  60         d.addCallback(formatUsers)
  61         d.addCallback(request.write)
  62         d.addCallback(lambda _: request.finish())
  63         return server.NOT_DONE_YET
  64 
  65     def getChild(self, path, request):
  66         if path=="":
  67             return UserStatusTree(self.service)
  68         else:
  69             return UserStatus(path, self.service)
  70 
  71 class UserStatus(resource.Resource):
  72 
  73     def __init__(self, user, service):
  74         resource.Resource.__init__(self)
  75         self.user = user
  76         self.service = service
  77 
  78     def render_GET(self, request):
  79         d = self.service.getUser(self.user)
  80         d.addCallback(cgi.escape)
  81         d.addCallback(lambda m:
  82                       '<h1>%s</h1>'%self.user+'<p>%s</p>'%m)
  83         d.addCallback(request.write)
  84         d.addCallback(lambda _: request.finish())
  85         return server.NOT_DONE_YET
  86 
  87 
  88 class UserStatusXR(xmlrpc.XMLRPC):
  89 
  90     def __init__(self, service):
  91         xmlrpc.XMLRPC.__init__(self)
  92         self.service = service
  93 
  94     def xmlrpc_getUser(self, user):
  95         return self.service.getUser(user)
  96 
  97 
  98 class FingerService(service.Service):
  99 
 100     def __init__(self, filename):
 101         self.filename = filename
 102         self._read()
 103 
 104     def _read(self):
 105         self.users = {}
 106         for line in file(self.filename):
 107             user, status = line.split(':', 1)
 108             user = user.strip()
 109             status = status.strip()
 110             self.users[user] = status
 111         self.call = reactor.callLater(30, self._read)
 112 
 113     def getUser(self, user):
 114         return defer.succeed(self.users.get(user, "No such user"))
 115 
 116     def getUsers(self):
 117         return defer.succeed(self.users.keys())
 118 
 119     def getFingerFactory(self):
 120         f = protocol.ServerFactory()
 121         f.protocol = FingerProtocol
 122         f.getUser = self.getUser
 123         return f
 124 
 125     def getResource(self):
 126         r = UserStatusTree(self)
 127         x = UserStatusXR(self)
 128         r.putChild('RPC2', x)
 129         return r
 130 
 131     def getIRCBot(self, nickname):
 132         f = protocol.ReconnectingClientFactory()
 133         f.protocol = IRCReplyBot
 134         f.nickname = nickname
 135         f.getUser = self.getUser
 136         return f
 137 
 138 application = service.Application('finger', uid=1, gid=1)
 139 f = FingerService('/etc/users')
 140 serviceCollection = service.IServiceCollection(application)
 141 internet.TCPServer(79, f.getFingerFactory()
 142                    ).setServiceParent(serviceCollection)
 143 internet.TCPServer(8000, server.Site(f.getResource())
 144                    ).setServiceParent(serviceCollection)
 145 internet.TCPClient('irc.freenode.org', 6667, f.getIRCBot('fingerbot')
 146                    ).setServiceParent(serviceCollection)

Source listing - listings/finger/finger18.py

return index-->TwistedTUT