The Evolution of Finger: pluggable backends

Finger的演化:可插入式后端

-- dreamingk [2004-08-09 02:11:23]

1. 简介(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我们要设法保护我们的服务对于网络环境的一知半解。

2. 另一个后端(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