从脚本中删除线程使用情况

时间:2013-05-31 20:42:58

标签: python ios python-2.7

我正在使用的下一个脚本用于使用IMAP IDLE监听IMAP连接,并且在很大程度上依赖于线程。对于我来说,消除踏板调用并使用主线程最简单的方法是什么? 作为一个新的python开发人员,我尝试编辑def __init__(self, conn):方法但只是犯了越来越多的错误

代码示例对我有很大帮助

#!/usr/local/bin/python2.7
print "Content-type: text/html\r\n\r\n";

import socket, ssl, json, struct, re
import imaplib2, time
from threading import *

# enter gmail login details here
USER="username@gmail.com"
PASSWORD="password"
# enter device token here
deviceToken = 'my device token x x x x x'
deviceToken = deviceToken.replace(' ','').decode('hex')
currentBadgeNum = -1

def getUnseen():
    (resp, data) = M.status("INBOX", '(UNSEEN)')
    print data
    return int(re.findall("UNSEEN (\d)*\)", data[0])[0])    

def sendPushNotification(badgeNum):
    global currentBadgeNum, deviceToken
    if badgeNum != currentBadgeNum:
        currentBadgeNum = badgeNum
        thePayLoad = {
             'aps': {
                  'alert':'Hello world!',
                  'sound':'',
                  'badge': badgeNum,
                  },
             'test_data': { 'foo': 'bar' },
             }
        theCertfile = 'certif.pem'
        theHost = ('gateway.push.apple.com', 2195)

        data = json.dumps(thePayLoad)
        theFormat = '!BH32sH%ds' % len(data)
        theNotification = struct.pack(theFormat, 0, 32, 
          deviceToken, len(data), data)

        ssl_sock = ssl.wrap_socket(socket.socket(socket.AF_INET, 
          socket.SOCK_STREAM), certfile=theCertfile)
        ssl_sock.connect(theHost)
        ssl_sock.write(theNotification)
        ssl_sock.close()
        print "Sent Push alert."

# This is the threading object that does all the waiting on 
# the event
class Idler(object):
    def __init__(self, conn):
        self.thread = Thread(target=self.idle)
        self.M = conn
        self.event = Event()

    def start(self):
        self.thread.start()

    def stop(self):
        # This is a neat trick to make thread end. Took me a 
        # while to figure that one out!
        self.event.set()

    def join(self):
        self.thread.join()

    def idle(self):
        # Starting an unending loop here
        while True:
            # This is part of the trick to make the loop stop 
            # when the stop() command is given
            if self.event.isSet():
                return
            self.needsync = False
            # A callback method that gets called when a new 
            # email arrives. Very basic, but that's good.
            def callback(args):
                if not self.event.isSet():
                    self.needsync = True
                    self.event.set()
            # Do the actual idle call. This returns immediately, 
            # since it's asynchronous.
            self.M.idle(callback=callback)
            # This waits until the event is set. The event is 
            # set by the callback, when the server 'answers' 
            # the idle call and the callback function gets 
            # called.
            self.event.wait()
            # Because the function sets the needsync variable,
            # this helps escape the loop without doing 
            # anything if the stop() is called. Kinda neat 
            # solution.
            if self.needsync:
                self.event.clear()
                self.dosync()

    # The method that gets called when a new email arrives. 
    # Replace it with something better.
    def dosync(self):
        print "Got an event!"
        numUnseen = getUnseen()
        sendPushNotification(numUnseen)

# Had to do this stuff in a try-finally, since some testing 
# went a little wrong.....
while True:
    try:
        # Set the following two lines to your creds and server
        M = imaplib2.IMAP4_SSL("imap.gmail.com")
        M.login(USER, PASSWORD)
        M.debug = 4
        # We need to get out of the AUTH state, so we just select 
        # the INBOX.
        M.select("INBOX")
        numUnseen = getUnseen()
        sendPushNotification(numUnseen)

        typ, data = M.fetch(1, '(RFC822)')
        raw_email = data[0][1]

        import email
        email_message = email.message_from_string(raw_email)
        print email_message['Subject']

        #print M.status("INBOX", '(UNSEEN)')
        # Start the Idler thread
        idler = Idler(M)
        idler.start()


        # Sleep forever, one minute at a time
        while True:
            time.sleep(60)
    except imaplib2.IMAP4.abort:
      print("Disconnected.  Trying again.")   
    finally:
        # Clean up.
        #idler.stop() #Commented out to see the real error
        #idler.join() #Commented out to see the real error
        #M.close()    #Commented out to see the real error
        # This is important!
        M.logout()

2 个答案:

答案 0 :(得分:1)

据我所知,这段代码无可救药地混淆了,因为作者使用"imaplib2" project library强制了一个线程模型,而这个代码从未使用过。

只创建了一个线程,它不需要是一个线程,而是选择imaplib2。但是,正如imaplib2 documentation notes

  

该模块提供了与标准python库模块imaplib提供的API几乎相同的API,主要区别在于此版本允许在IMAP4服务器上并行执行命令,并实现IMAP4rev1 IDLE扩展。 (imaplib2可以代替现有客户端中的imaplib而代码没有变化,但请参阅下面的警告。)

这使您看起来应该能够丢弃大部分class Idler并使用连接M。我建议您在查看module imaplib之前先看看Doug Hellman在official documentation中出色的Python周模块。你需要对代码进行反向工程以找出它的意图,但它看起来像是:

  1. 打开与GMail的连接
  2. 检查收件箱中看不见的邮件
  3. 从(2)
  4. 计算看不见的消息
  5. 向gateway.push.apple.com
  6. 上的某些服务发送虚拟消息
  7. 等待通知,转到(2)
  8. 关于代码最有趣的事情可能是它似乎没有做任何事情,尽管sendPushNotification(步骤4)所做的事情是个谜,以及使用imaplib2特定服务的一行: / p>

    self.M.idle(callback=callback)
    

    使用我在模块文档中没有看到的命名参数。你知道这段代码是否实际运行过吗?

    除了不必要的复杂性之外,还有另一个理由放弃imaplib2:它在sourceforgePyPi独立存在,一位维护者在两年前声称“将继续尝试保留它最新的原始“。你有哪一个?你会安装哪个?

答案 1 :(得分:0)

不要这样做

由于您尝试删除线程使用仅仅因为您没有找到如何处理来自服务器的异常,因此我不建议删除线程使用,因为库的异步性质本身 - 闲人处理它比一个线程更顺畅。

解决方案

您需要使用try-except打包self.M.idle(callback=callback),然后在主线程中重新提升它。然后通过重新运行主线程中的代码来重新启动连接来处理异常。

您可以在此答案中找到解决方案的更多详细信息和可能的原因:https://stackoverflow.com/a/50163971/1544154

完整的解决方案在这里:https://www.github.com/Elijas/email-notifier