【问题标题】:Remove threads usage from script从脚本中删除线程使用
【发布时间】:2013-05-27 18:28:35
【问题描述】:

我使用的下一个脚本用于使用 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()

【问题讨论】:

    标签: python ios python-2.7


    【解决方案1】:

    据我所知,这段代码非常混乱,因为作者使用了"imaplib2" project library,它强制使用了一个线程模型,然后这段代码永远不会使用。

    只创建了一个线程,它不需要是线程,但可以选择imaplib2。但是,作为imaplib2 documentation notes:

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

    这表明您应该能够丢弃大部分class Idler,而只使用连接M。我建议您在查看 official documentation 之前查看 Doug Hellman 为 module imaplib 撰写的本周优秀 Python 模块。您需要对代码进行逆向工程以找出它的意图,但在我看来它是这样的:

    1. 打开与 GMail 的连接
    2. 检查收件箱中未查看的邮件
    3. 统计来自 (2) 条未看到的消息
    4. 向 gateway.push.apple.com 上的某个服务发送虚拟消息
    5. 等待通知,转到 (2)

    也许该代码最有趣的一点是它似乎没有做任何事情,尽管 sendPushNotification(第 4 步)所做的事情是个谜,而且其中一行使用了 imaplib2 特定服务:

    self.M.idle(callback=callback)
    

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

    除了不必要的复杂性之外,还有另一个理由放弃 imaplib2:它独立存在于 sourceforge 和 PyPi 上,两年前一位维护者声称“将尝试使其与原本的”。你有哪一个?你会安装哪个?

    【讨论】:

    • 感谢您的回复。上面的脚本用于使用 IMAP IDLE 监听 IMAP 连接。这就是它使用 imaplib 2 而不是官方 imaplib 的方式。如果我使用 imaplib,这个脚本几乎没用,因为就像你写的那样,你自己的 imaplib 不支持 IMAP IDLE。sendPushNotification 如果有新电子邮件到达收件箱,则会向 iOS 设备发送推送通知。该脚本可以正常工作并按预期进行。当它崩溃时问题就开始了,我无法捕捉到崩溃,因为它在另一个线程上崩溃了。这就是为什么我试图将其仅转换为一个线程。
    • 啊,这说明了很多。祝你好运。
    【解决方案2】:

    不要这样做

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

    解决方案

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

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

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

    【讨论】:

      猜你喜欢
      • 2015-04-17
      • 2023-04-10
      • 1970-01-01
      • 1970-01-01
      • 2012-09-07
      • 1970-01-01
      • 1970-01-01
      • 2015-07-16
      • 2020-09-01
      相关资源
      最近更新 更多