The Evolution of Finger: pluggable backends Finger的演化:可插入式后端
Contents
简介(Introduction)
This is the fifth part of the Twisted tutorial Twisted from Scratch, or The Evolution of Finger.
这是Twisted教程--“Twisted from Scratch, or The Evolution of Finger”的第五部分
In this part we will add new several new backends to our finger service using the component-based architecture developed in The Evolution of Finger: moving to a component based architecture. This will show just how convenient it is to implement new back-ends when we move to a component based architecture. Note that here we also use an interface we previously wrote, FingerSetterFactory, by supporting one single method. We manage to preserve the service's ignorance of the network.
在这个部分中,我们将为我们在前边做过的“基于组件(Component-based)”的结构构建的Finger服务上增加一些新的后端支持。 你会发现在“基于组件(Component-based)”的结构的基础上,实现新的后端支持是多么的便利。 你要注意的是,我们为了支持一个方法,会使用一个过去写过的接口--FingerSettingFactory。 我们要设法保护我们的服务对于网络环境的一知半解。
另一个后端(Back-end)
1 from twisted.internet import protocol, reactor, defer, utils
2 import pwd
3
4 # Another back-end
5
6 class LocalFingerService(service.Service):
7
8 __implements__ = IFingerService
9
10 def getUser(self, user):
11 # need a local finger daemon running for this to work
12 return utils.getProcessOutput("finger", [user])
13
14 def getUsers(self):
15 return defer.succeed([])
16
17
18 f = LocalFingerService()
19
20 Source listing - listings/finger/finger19b_changes.py
21 Full source code here:
22
23 # Do everything properly, and componentize
24 from twisted.application import internet, service
25 from twisted.internet import protocol, reactor, defer, utils
26 from twisted.protocols import basic, irc
27 from twisted.python import components
28 from twisted.web import resource, server, static, xmlrpc
29 import cgi
30 import pwd
31
32 class IFingerService(components.Interface):
33
34 def getUser(self, user):
35 """Return a deferred returning a string"""
36
37 def getUsers(self):
38 """Return a deferred returning a list of strings"""
39
40 class IFingerSetterService(components.Interface):
41
42 def setUser(self, user, status):
43 """Set the user's status to something"""
44
45 class IFingerSetterService(components.Interface):
46
47 def setUser(self, user, status):
48 """Set the user's status to something"""
49
50 def catchError(err):
51 return "Internal error in server"
52
53 class FingerProtocol(basic.LineReceiver):
54
55 def lineReceived(self, user):
56 d = self.factory.getUser(user)
57 d.addErrback(catchError)
58 def writeValue(value):
59 self.transport.write(value+'\n')
60 self.transport.loseConnection()
61 d.addCallback(writeValue)
62
63
64 class IFingerFactory(components.Interface):
65
66 def getUser(self, user):
67 """Return a deferred returning a string"""
68
69 def buildProtocol(self, addr):
70 """Return a protocol returning a string"""
71
72
73 class FingerFactoryFromService(protocol.ServerFactory):
74
75 __implements__ = IFingerFactory,
76
77 protocol = FingerProtocol
78
79 def __init__(self, service):
80 self.service = service
81
82 def getUser(self, user):
83 return self.service.getUser(user)
84
85 components.registerAdapter(FingerFactoryFromService,
86 IFingerService,
87 IFingerFactory)
88
89 class FingerSetterProtocol(basic.LineReceiver):
90
91 def connectionMade(self):
92 self.lines = []
93
94 def lineReceived(self, line):
95 self.lines.append(line)
96
97 def connectionLost(self, reason):
98 if len(self.lines) == 2:
99 self.factory.setUser(*self.lines)
100
101
102 class IFingerSetterFactory(components.Interface):
103
104 def setUser(self, user, status):
105 """Return a deferred returning a string"""
106
107 def buildProtocol(self, addr):
108 """Return a protocol returning a string"""
109
110
111 class FingerSetterFactoryFromService(protocol.ServerFactory):
112
113 __implements__ = IFingerSetterFactory,
114
115 protocol = FingerSetterProtocol
116
117 def __init__(self, service):
118 self.service = service
119
120 def setUser(self, user, status):
121 self.service.setUser(user, status)
122
123
124 components.registerAdapter(FingerSetterFactoryFromService,
125 IFingerSetterService,
126 IFingerSetterFactory)
127
128 class IRCReplyBot(irc.IRCClient):
129
130 def connectionMade(self):
131 self.nickname = self.factory.nickname
132 irc.IRCClient.connectionMade(self)
133
134 def privmsg(self, user, channel, msg):
135 user = user.split('!')[0]
136 if self.nickname.lower() == channel.lower():
137 d = self.factory.getUser(msg)
138 d.addErrback(catchError)
139 d.addCallback(lambda m: "Status of %s: %s" % (msg, m))
140 d.addCallback(lambda m: self.msg(user, m))
141
142
143 class IIRCClientFactory(components.Interface):
144
145 """
146 @ivar nickname
147 """
148
149 def getUser(self, user):
150 """Return a deferred returning a string"""
151
152 def buildProtocol(self, addr):
153 """Return a protocol"""
154
155
156 class IRCClientFactoryFromService(protocol.ClientFactory):
157
158 __implements__ = IIRCClientFactory,
159
160 protocol = IRCReplyBot
161 nickname = None
162
163 def __init__(self, service):
164 self.service = service
165
166 def getUser(self, user):
167 return self.service.getUser(user)
168
169 components.registerAdapter(IRCClientFactoryFromService,
170 IFingerService,
171 IIRCClientFactory)
172
173 class UserStatusTree(resource.Resource):
174
175 __implements__ = resource.IResource,
176
177 def __init__(self, service):
178 resource.Resource.__init__(self)
179 self.service = service
180 self.putChild('RPC2', UserStatusXR(self.service))
181
182 def render_GET(self, request):
183 d = self.service.getUsers()
184 def formatUsers(users):
185 l = ['<li><a href="%s">%s</a></li>' % (user, user)
186 for user in users]
187 return '<ul>'+''.join(l)+'</ul>'
188 d.addCallback(formatUsers)
189 d.addCallback(request.write)
190 d.addCallback(lambda _: request.finish())
191 return server.NOT_DONE_YET
192
193 def getChild(self, path, request):
194 if path=="":
195 return UserStatusTree(self.service)
196 else:
197 return UserStatus(path, self.service)
198
199 components.registerAdapter(UserStatusTree, IFingerService,
200 resource.IResource)
201
202 class UserStatus(resource.Resource):
203
204 def __init__(self, user, service):
205 resource.Resource.__init__(self)
206 self.user = user
207 self.service = service
208
209 def render_GET(self, request):
210 d = self.service.getUser(self.user)
211 d.addCallback(cgi.escape)
212 d.addCallback(lambda m:
213 '<h1>%s</h1>'%self.user+'<p>%s</p>'%m)
214 d.addCallback(request.write)
215 d.addCallback(lambda _: request.finish())
216 return server.NOT_DONE_YET
217
218
219 class UserStatusXR(xmlrpc.XMLRPC):
220
221 def __init__(self, service):
222 xmlrpc.XMLRPC.__init__(self)
223 self.service = service
224
225 def xmlrpc_getUser(self, user):
226 return self.service.getUser(user)
227
228
229 class FingerService(service.Service):
230
231 __implements__ = IFingerService,
232
233 def __init__(self, filename):
234 self.filename = filename
235 self._read()
236
237 def _read(self):
238 self.users = {}
239 for line in file(self.filename):
240 user, status = line.split(':', 1)
241 user = user.strip()
242 status = status.strip()
243 self.users[user] = status
244 self.call = reactor.callLater(30, self._read)
245
246 def getUser(self, user):
247 return defer.succeed(self.users.get(user, "No such user"))
248
249 def getUsers(self):
250 return defer.succeed(self.users.keys())
251
252 # Another back-end
253
254 class LocalFingerService(service.Service):
255
256 __implements__ = IFingerService
257
258 def getUser(self, user):
259 # need a local finger daemon running for this to work
260 return utils.getProcessOutput("finger", [user])
261
262 def getUsers(self):
263 return defer.succeed([])
264
265
266 application = service.Application('finger', uid=1, gid=1)
267 f = LocalFingerService()
268 serviceCollection = service.IServiceCollection(application)
269 internet.TCPServer(79, IFingerFactory(f)
270 ).setServiceParent(serviceCollection)
271 internet.TCPServer(8000, server.Site(resource.IResource(f))
272 ).setServiceParent(serviceCollection)
273 i = IIRCClientFactory(f)
274 i.nickname = 'fingerbot'
275 internet.TCPClient('irc.freenode.org', 6667, i
276 ).setServiceParent(serviceCollection)
277
278 Source listing - listings/finger/finger19b.py
279
280 We've already written this, but now we get more for less work: the network code is completely separate from the back-end.
281
282 Yet Another Back-end: Doing the Standard Thing
283 from twisted.internet import protocol, reactor, defer, utils
284 import pwd
285 import os
286
287
288 # Yet another back-end
289
290 class LocalFingerService(service.Service):
291
292 __implements__ = IFingerService
293
294 def getUser(self, user):
295 user = user.strip()
296 try:
297 entry = pwd.getpwnam(user)
298 except KeyError:
299 return defer.succeed("No such user")
300 try:
301 f = file(os.path.join(entry[5],'.plan'))
302 except (IOError, OSError):
303 return defer.succeed("No such user")
304 data = f.read()
305 data = data.strip()
306 f.close()
307 return defer.succeed(data)
308
309 def getUsers(self):
310 return defer.succeed([])
311
312
313
314 f = LocalFingerService()
315
316 Source listing - listings/finger/finger19c_changes.py
317 Full source code here:
318
319 # Do everything properly, and componentize
320 from twisted.application import internet, service
321 from twisted.internet import protocol, reactor, defer, utils
322 from twisted.protocols import basic, irc
323 from twisted.python import components
324 from twisted.web import resource, server, static, xmlrpc
325 import cgi
326 import pwd
327 import os
328
329 class IFingerService(components.Interface):
330
331 def getUser(self, user):
332 """Return a deferred returning a string"""
333
334 def getUsers(self):
335 """Return a deferred returning a list of strings"""
336
337 class IFingerSetterService(components.Interface):
338
339 def setUser(self, user, status):
340 """Set the user's status to something"""
341
342 class IFingerSetterService(components.Interface):
343
344 def setUser(self, user, status):
345 """Set the user's status to something"""
346
347 def catchError(err):
348 return "Internal error in server"
349
350 class FingerProtocol(basic.LineReceiver):
351
352 def lineReceived(self, user):
353 d = self.factory.getUser(user)
354 d.addErrback(catchError)
355 def writeValue(value):
356 self.transport.write(value+'\n')
357 self.transport.loseConnection()
358 d.addCallback(writeValue)
359
360
361 class IFingerFactory(components.Interface):
362
363 def getUser(self, user):
364 """Return a deferred returning a string"""
365
366 def buildProtocol(self, addr):
367 """Return a protocol returning a string"""
368
369
370 class FingerFactoryFromService(protocol.ServerFactory):
371
372 __implements__ = IFingerFactory,
373
374 protocol = FingerProtocol
375
376 def __init__(self, service):
377 self.service = service
378
379 def getUser(self, user):
380 return self.service.getUser(user)
381
382 components.registerAdapter(FingerFactoryFromService,
383 IFingerService,
384 IFingerFactory)
385
386 class FingerSetterProtocol(basic.LineReceiver):
387
388 def connectionMade(self):
389 self.lines = []
390
391 def lineReceived(self, line):
392 self.lines.append(line)
393
394 def connectionLost(self, reason):
395 if len(self.lines) == 2:
396 self.factory.setUser(*self.lines)
397
398
399 class IFingerSetterFactory(components.Interface):
400
401 def setUser(self, user, status):
402 """Return a deferred returning a string"""
403
404 def buildProtocol(self, addr):
405 """Return a protocol returning a string"""
406
407
408 class FingerSetterFactoryFromService(protocol.ServerFactory):
409
410 __implements__ = IFingerSetterFactory,
411
412 protocol = FingerSetterProtocol
413
414 def __init__(self, service):
415 self.service = service
416
417 def setUser(self, user, status):
418 self.service.setUser(user, status)
419
420
421 components.registerAdapter(FingerSetterFactoryFromService,
422 IFingerSetterService,
423 IFingerSetterFactory)
424
425 class IRCReplyBot(irc.IRCClient):
426
427 def connectionMade(self):
428 self.nickname = self.factory.nickname
429 irc.IRCClient.connectionMade(self)
430
431 def privmsg(self, user, channel, msg):
432 user = user.split('!')[0]
433 if self.nickname.lower() == channel.lower():
434 d = self.factory.getUser(msg)
435 d.addErrback(catchError)
436 d.addCallback(lambda m: "Status of %s: %s" % (msg, m))
437 d.addCallback(lambda m: self.msg(user, m))
438
439
440 class IIRCClientFactory(components.Interface):
441
442 """
443 @ivar nickname
444 """
445
446 def getUser(self, user):
447 """Return a deferred returning a string"""
448
449 def buildProtocol(self, addr):
450 """Return a protocol"""
451
452
453 class IRCClientFactoryFromService(protocol.ClientFactory):
454
455 __implements__ = IIRCClientFactory,
456
457 protocol = IRCReplyBot
458 nickname = None
459
460 def __init__(self, service):
461 self.service = service
462
463 def getUser(self, user):
464 return self.service.getUser(user)
465
466 components.registerAdapter(IRCClientFactoryFromService,
467 IFingerService,
468 IIRCClientFactory)
469
470 class UserStatusTree(resource.Resource):
471
472 __implements__ = resource.IResource,
473
474 def __init__(self, service):
475 resource.Resource.__init__(self)
476 self.service = service
477 self.putChild('RPC2', UserStatusXR(self.service))
478
479 def render_GET(self, request):
480 d = self.service.getUsers()
481 def formatUsers(users):
482 l = ['<li><a href="%s">%s</a></li>' % (user, user)
483 for user in users]
484 return '<ul>'+''.join(l)+'</ul>'
485 d.addCallback(formatUsers)
486 d.addCallback(request.write)
487 d.addCallback(lambda _: request.finish())
488 return server.NOT_DONE_YET
489
490 def getChild(self, path, request):
491 if path=="":
492 return UserStatusTree(self.service)
493 else:
494 return UserStatus(path, self.service)
495
496 components.registerAdapter(UserStatusTree, IFingerService,
497 resource.IResource)
498
499 class UserStatus(resource.Resource):
500
501 def __init__(self, user, service):
502 resource.Resource.__init__(self)
503 self.user = user
504 self.service = service
505
506 def render_GET(self, request):
507 d = self.service.getUser(self.user)
508 d.addCallback(cgi.escape)
509 d.addCallback(lambda m:
510 '<h1>%s</h1>'%self.user+'<p>%s</p>'%m)
511 d.addCallback(request.write)
512 d.addCallback(lambda _: request.finish())
513 return server.NOT_DONE_YET
514
515
516 class UserStatusXR(xmlrpc.XMLRPC):
517
518 def __init__(self, service):
519 xmlrpc.XMLRPC.__init__(self)
520 self.service = service
521
522 def xmlrpc_getUser(self, user):
523 return self.service.getUser(user)
524
525
526 class FingerService(service.Service):
527
528 __implements__ = IFingerService,
529
530 def __init__(self, filename):
531 self.filename = filename
532 self._read()
533
534 def _read(self):
535 self.users = {}
536 for line in file(self.filename):
537 user, status = line.split(':', 1)
538 user = user.strip()
539 status = status.strip()
540 self.users[user] = status
541 self.call = reactor.callLater(30, self._read)
542
543 def getUser(self, user):
544 return defer.succeed(self.users.get(user, "No such user"))
545
546 def getUsers(self):
547 return defer.succeed(self.users.keys())
548
549 # Yet another back-end
550
551 class LocalFingerService(service.Service):
552
553 __implements__ = IFingerService
554
555 def getUser(self, user):
556 user = user.strip()
557 try:
558 entry = pwd.getpwnam(user)
559 except KeyError:
560 return defer.succeed("No such user")
561 try:
562 f = file(os.path.join(entry[5],'.plan'))
563 except (IOError, OSError):
564 return defer.succeed("No such user")
565 data = f.read()
566 data = data.strip()
567 f.close()
568 return defer.succeed(data)
569
570 def getUsers(self):
571 return defer.succeed([])
572
573
574 application = service.Application('finger', uid=1, gid=1)
575 f = LocalFingerService()
576 serviceCollection = service.IServiceCollection(application)
577 internet.TCPServer(79, IFingerFactory(f)
578 ).setServiceParent(serviceCollection)
579 internet.TCPServer(8000, server.Site(resource.IResource(f))
580 ).setServiceParent(serviceCollection)
581 i = IIRCClientFactory(f)
582 i.nickname = 'fingerbot'
583 internet.TCPClient('irc.freenode.org', 6667, i
584 ).setServiceParent(serviceCollection)
Source listing - listings/finger/finger19c.py
Not much to say except that now we can be churn out backends like crazy. Feel like doing a back-end for Advogato, for example? Dig out the XML-RPC client support Twisted has, and get to work!
没什么多余的话好说,除非我们此时此刻我们还要竭尽全力作一个令人疯狂的backend。 感觉就像是给Advogato(译者注:一个著名的开源软件站点 www.advogato.org)作一个back-end,让我举个例子? 发现XML-RPC客户端已经支持Twisted,并能正常工作了。
return index-->TwistedTUT
Version: 1.3.0