Finger演化:整理finger代码 The Evolution of Finger: cleaning up the finger code
介绍(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.
编写可读代码(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