Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove threads usage from script

The next script I'm using is used to listen to IMAP connection using IMAP IDLE and depends heavily on threads. What's the easiest way for me to eliminate the treads call and just use the main thread? As a new python developer I tried editing def __init__(self, conn): method but just got more and more errors

A code sample would help me a lot

#!/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="[email protected]"
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()
like image 644
Segev Avatar asked Feb 28 '26 03:02

Segev


1 Answers

As far as I can tell, this code is hopelessly confused because the author used the "imaplib2" project library which forces a threading model which this code then never uses.

Only one thread is ever created, which wouldn't need to be a thread but for the choice of imaplib2. However, as the imaplib2 documentation notes:

This module presents an almost identical API as that provided by the standard python library module imaplib, the main difference being that this version allows parallel execution of commands on the IMAP4 server, and implements the IMAP4rev1 IDLE extension. (imaplib2 can be substituted for imaplib in existing clients with no changes in the code, but see the caveat below.)

Which makes it appear that you should be able to throw out much of class Idler and just use the connection M. I recommend that you look at Doug Hellman's excellent Python Module Of The Week for module imaplib prior to looking at the official documentation. You'll need to reverse engineer the code to find out its intent, but it looks to me like:

  1. Open a connection to GMail
  2. check for unseen messages in Inbox
  3. count unseen messages from (2)
  4. send a dummy message to some service at gateway.push.apple.com
  5. Wait for notice, goto (2)

Perhaps the most interesting thing about the code is that it doesn't appear to do anything, although what sendPushNotification (step 4) does is a mystery, and the one line that uses an imaplib2 specific service:

self.M.idle(callback=callback)

uses a named argument that I don't see in the module documentation. Do you know if this code ever actually ran?

Aside from unneeded complexity, there's another reason to drop imaplib2: it exists independently on sourceforge and PyPi which one maintainer claimed two years ago "An attempt will be made to keep it up-to-date with the original". Which one do you have? Which would you install?

like image 158
msw Avatar answered Mar 01 '26 15:03

msw



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!