IMAP更加完善而功能强大。Twisted是一个使用Python编写网络应用程序的框架,它被设计成多任务,并且是贯穿整个库都使用异步I/O的。
一、Twisted中的IMAP简介
大多数网络客户端库,如poplib和imaplib工作的方式都一样。您需要编写调用这些库的程序,库调用服务器和您的代码(也就是说,函数并没有返回),直到从网络上得到一个结果。
Twisted在它的头部返回这种方法。当您访问网络的时候,您通知Twisted该把结果传递给哪个函数,您真正的调用会立刻返回,当在网络上取得一个结果的时候,您的函数将被调用,而给结果将作为一个参数。Twisted开发人员把这种情况称为“别调用我们,我们将调用你”,这个模式也适用于基于事件的程序。被一个结果调用的函数也称为回调函数。
二、理解Twisted基础
#!/usr/bin/env python from twisted.internet import defer, reactor, protocol from twisted.mail.imap4 import IMAP4Client import sys class IMAPClient(IMAP4Client): def connectionMade(self): print 'I have successfully connected to the server' d=self.getCapabilities() d.addCallback(self.gotcapabilities) def gotcapabilities(self, caps): if caps==None: print 'Server did not return a capability list.' else: for key, value in caps.items(): print '%s: %s' %(key, str(value)) self.logout() reactor.stop() class IMAPFactory(protocol.ClientFactory): protocol=IMAPClient def clientConnectionFailed(self, connector, reason): print 'Client connection failed:', reason reactor.stop() reactor.connectTCP(sys.argv[1], 143, IMAPFactory()) reactor.run()
大多数Twisted客户端程序基本上都包含两个类:一个protocol类(例子中的IMAPClient)和一个factory类(IMAPFactory)。factory类管理和服务器的链接,protocol类实现和服务器的会话。例子中,当程序开始的时候,先建立一个网络链接,默认IMAP端口143,同时一个IMAPFactory对象也被传递过来。程序最后一行reactor.run()。在Twisted中,reactor是用来处理网络事件的部分,直到reactor.stop()被调用,对reactor.run()的调用才返回。在建立链接后,reactor调用connectionMade()。
getCapabilities()可以返回IMAP服务器支持的一系列可选的特性,它返回一个被称为Deferred的对象,它是Twisted中基于事件的模型核心。它用来通知Twisted在收到一个回复的时候该做什么。一旦收到一个Deferred对象,程序就调用该对象上的addCallback,这个函数通知reactor在收到应答后该调用什么函数。在这里,gotcapabilities是唯一没有重载基类方法中的方法。在调用gotcapabilities时没有使用括号是因为您不想在调用addCallback()的时候调用gotcapabilities(),相反,您只是把函数传递给addCallback(),并让Twisted稍后调用它。gotcapabilities函数从Twisted收到一个参数,这个参数是您向getCapabilities()提交请求的结果。最后登出并停止reactor,程序结束。
1、登录:
#!/usr/bin/env python #-*-coding:utf-8-*- from twisted.internet import defer, reactor, protocol from twisted.mail.imap4 import IMAP4Client import sys, getpass class IMAPClient(IMAP4Client): def connectionMade(self): print 'I have successfully connected to the server' IMAPLogin(self) print 'connectionMade returning' class IMAPFactory(protocol.ClientFactory): protocol=IMAPClient def __init__(self, username, password): self.username=username self.password=password def clientConnectionFailed(self, connector, reason): print 'Client connection failed:', reason reactor.stop() class IMAPLogin(object): def __init__(self, proto): self.proto=proto self.factory=proto.factory d=self.proto.login(self.factory.username, self.factory.password) d.addCallback(self.loggedin) d.addCallback(self.stopreactor) print "IMAPLogic.__init__returning" def loggedin(self, data): print "I'm logged in" return self.logout() def logout(self): print 'Logging out' d=self.proto.logout() return d def stopreactor(self, data=None): print 'Stopping reactor' reactor.stop() password=getpass.getpass("Enter password for %s on %s: " %(sys.argv[2], sys.argv[1])) reactor.connectTCP(sys.argv[1], 143, IMAPFactory(sys.argv[2], password)) reactor.run()
程序首先进行reactor的连接,然后reactor运行run。这会调用protol类中的connectionMade方法。connecitonMade初始化一个IMAPLogic的实例。初始化IMAPLogic实例时,protol类调用了login方法生成一个deferred对象d,然后d增加两个callback函数。deferred(递延)对象,当从网络中获得结果后,便把结果作为参数并调用callback函数。在这个例子中,当用户登录时,Twisted从网络中获取到应答,然后把它作为参数调用loggedin函数。loggedin函数掉哟功能logout这一函数而logout函数返回proto类的logout方法生成的deferred对象。因为这一对象没有callback函数,所以被直接传递给第一个deferred对象的下一个callback函数,即stopreactor()。
可以简单地认为这几个函数之间传递的是deferred对象。。。(个人理解)
2、错误处理
Twisted无法产生异常,但有一个调用errback的概念,和callback类似,但它只在有错误发生的时候才被调用。
#!/usr/bin/env python from twisted.internet import defer, reactor, protocol from twisted.mail.imap4 import IMAP4Client import sys, getpass class IMAPClient(IMAP4Client): def connectionMade(self): print 'I have successfully connected to the server' IMAPLogic(self) print 'connectionMade returning' class IMAPFactory(protocol.ClientFactory): protocol=IMAPClient def __init__(self, username, password): self.username=username self.password=password def clientConnectionFailed(self, connector, reason): print 'Client connection failed:', reason reactor.stop() class IMAPLogic(object): def __init__(self, proto): self.proto=proto self.factory=proto.factory self.logintries=1 d=self.login() d.addCallback(self.loggedin) d.addErrback(self.loginerror) d.addCallback(self.logout) d.addCallback(self.stopreactor) d.addErrback(self.errorhappened) print 'IMAPLogic.__init__ returning' def login(self): print 'Logging in..' return self.proto.login(self.factory.username, self.factory.password) def loggedin(self, data): print "I'm logged in" def logout(self, data=None): d=self.proto.logout() return d def stopreactor(self, data=None): print 'Stopping reactor' reactor.stop() def errorhappened(self, failure): print 'An error occured:', failure.getErrorMessage() print 'Because of the error , I am logging out and stopping reactor...' d=self.logout() d.addBoth(self.stopreactor) return failure def loginerror(self, failure): print 'Your login failed (attempt %d).' % self.logintries if self.logintries>=3: print 'You have triedto log in three times;I am giving up' return failure self.logintries+=1 sys.stdout.write('New username: ') self.factory.username=sys.stdin().readline().strip().encode('utf-8') self.factory.password=getpass.getpass("New password: ").encode('utf-8') d=self.login() d.addErrback(self.loginerror) return d password=getpass.getpass("Enter password for %s on %s: " % (sys.argv[2], sys.argv[1])).encode('utf-8') reactor.connectTCP(sys.argv[1], 143, IMAPFactory(sys.argv[2].encode('utf-8'), password)) reactor.run()
3、基本下载
下载整个邮箱:
#!/usr/bin/env python from twisted.internet import defer,reactor,protocol from twisted.mail.imap4 import IMAP4Client import sys, getpass, email class IMAPClient(IMAP4Client): def connectionMade(self): IMAPLogin(self) class IMAPFactory(protocol.ClientFactory): protocol=IMAPClient def __init__(self, username, password): self.username=username self.password=password def clientConnectionFailed(self, connector, reason): print 'Client connnection failed:', reason reactor.stop() class IMAPLogin(object): def __init__(self, proto): self.proto=proto self.factory=proto.factory d=self.proto.login(self.factory.username, self.factory.password) d.addCallback(lambda x: self.proto.examine('INBOX')) d.addCallback(lambda x: self.proto.fetchSpecific('1:*', peek=1)) d.addCallback(self.gotmessages) d.addCallback(self.logout) d.addCallback(self.stopreactor) d.addErrback(self.errorhappened) def gotmessages(self, data): destfd=open(sys.argv[3], 'at') for key, value in data.items(): print 'Writing message', key msg=email.message_from_string(value[0][2]) destfd.write(msg.as_string(unixfrom=1)) destfd.write('\n') destfd.close() def logout(self, data=None): return self.proto.logout() def stopreactor(self, data=None): reactor.stop() def errorhappened(self, failure): print 'An error occured:', failure.getErrorMessage() print 'Because of the error, I am logging out and stopping reactor...' d=self.logout() d.addBoth(self.stopreactor) return failure password=getpass.getpass('Enter password for %s on %s:'%(sys.argv[2], sys.argv[1])) #print password reactor.connectTCP(sys.argv[1], 143, IMAPFactory(sys.argv[2], password)) reactor.run()
fetchSpecific()函数可以从一封邮件文件夹中下载一封多封邮件。第一个参数,'1:*'是该文件夹中对应的所有邮件范围,通过把peek设置成true,邮件在您阅读他们的时候并不被更改,如果peek没有被设置,所有被下载的邮件都会添加上\Seen标志。gotmessages()函数被下载的邮件调用,它传入一个字典,该字典的键就是邮件的number,它的值是该邮件组成成分的列表。