Python: print function hang when printing global list of objects
Question: I'm currently writing a Python Telegram bot which is used to monitor Raspi IOs
and send messages to a channel. So basically it has a function that will
update a logging variable `llog`.

This function (`logUpdate`), as it's named, will remove entries that are more
than 5 mins old. In it, I tried to check the content of the global variable.
Upon printing, it just hangs.

This doesn't seem to block any other functionalities of the bot because I can
still call out other bot commands.

I don't think it's the bot. It must be some kind of data access problems.

I attach some code snippet below:

    
    
    #!usr/bin/python
    
    ##
    ### RF Security bot start script
    ##
    
    ##
    ### Imports
    ##
    import telegram     as tg
    import telegram.ext as tgExt
    import RPi.GPIO     as gpio
    import time
    from datetime import datetime as dt
    
    ##
    ### Common variables
    ##
    NULLSENSOR = 0
    PRESSENSOR = 1
    MAGSENSOR  = 2
    
    sensDict = {NULLSENSOR:"No sensor",
                PRESSENSOR:"Pressure sensor",
                MAGSENSOR:"Magnetic sensor"}
    
    # Event class
    class ev(object):
    
            timestamp = 0
            sType     = NULLSENSOR
    
            def __init__(self, ts=0, st=NULLSENSOR):
                    self.timestamp = ts
                    self.sType     = st
    
    
            def toString(self):
    
                    if(sType == PRESSENSOR):
                            return str("-> @"+timestamp.strftime('%c')+
                                       ": Pressure sensor triggered\n")
                    elif(sType == MAGSENSOR):
                            return str("-> @"+timestamp.strftime('%c')+
                                       ": Magnetic sensor triggered\n")
                    else:
                            return ""
    
    
    # Report log
    llog = []     # Data log
    lmutex = True # Log mutex for writing
    
    ##
    ### Hardware configuration
    ##
    
    # GPIO callbacks
    def pressureCallback(channel):
            global llog
            global lmutex
            global trigCntGlobal
            global trigCntPress
    
            ep = ev(ts=dt.now(), st=PRESSENSOR)
    
            print("---> Pressure sensor triggered at "+
                  ep.timestamp.strftime("%c"))
    
            rfSecuBot.sendMessage('@channel', "Pressure sensor "+
                            "triggered.")
    
            while(not lmutex):
                    pass
    
            lmutex = False
    
            llog.insert(0, ep)
            trigCntGlobal = trigCntGlobal + 1
            trigCntPress = trigCntPress + 1
    
            lmutex = True
    
    
    def magneticCallback(channel):
            global llog
            global lmutex
            global trigCntGlobal
            global trigCntMag
            global rfSecuBot
    
            em = ev(ts=dt.now(), st=PRESSENSOR)
    
            print("---> Magnetic sensor triggered at "+
                  em.timestamp.strftime("%c"))
    
            rfSecuBot.sendMessage('@channel', "Magnetic sensor "+
                            "triggered.")
    
            while(not lmutex):
                    pass
    
            lmutex = False
    
            llog.insert(0, em)
            trigCntGlobal = trigCntGlobal + 1
            trigCntMag = trigCntMag + 1
    
            lmutex = True
    
    
    # Periodic logging function
    def logUpdate():
            global llog
            global lmutex
    
            updTime = dt.now()
            print("---> Updating log\n")
            while(not lmutex):
                    pass
    
            lmutex = False
    
            for i in llog:                          ########### STUCK HERE
                    print(i.toString())             ###########
    
            # Check log timestamps
            for i in llog:
                    if((updTime - i.timestamp).total_seconds() > 300):
                            llog.remove(i)
    
            for i in llog:                          ########### WAS STUCK HERE
                    print(i.toString())             ###########  TOO
    
            lmutex = True
    
            print("---> Log updated\n")
    
    
    # Formatting function
    def logFormat():
            global llog
            global lmutex
    
            logUpdate() # Asynchronous call to logUpdate to make sure
                        #  that the log has been updated at the time
                        #  of formatting
    
            while(not lmutex):
                    pass
    
            lmutex = False
    
            flog = []
            cnt = 0
    
            for i in llog:
                    if(cnt < 10):
                            flog.append(i.toString())
                            cnt = cnt + 1
                    else:
                            break
    
            lmutex = True
    
            print("----> Formatted string:")
            print(flog+"\n")
            return flog
    
    
    def listFormat():
            global llog
            global lmutex
    
            logUpdate() # Asynchronous call to logUpdate to make sure
                        #  that the log has been updated at the time
                        #  of formatting
    
            while(not lmutex):
                    pass
    
            lmutex = False
    
            flog = []
            flog.append("      Sensors      \n")
    
            dLen = len(sensDict.keys())
    
            if(dLen <= 1):
                    flog.append(sensDict.get(NULLSENSOR))
            else:
                    sdItr = sensDict.iterkeys()
                    st = sdItr.next() # Had to add extra var
                    while(dLen > 1):
                            st = sdItr.next()
                            trigCnt = 0
    
                            for i in llog:
                                    if(i.sType == st):
                                            trigCnt = trigCnt + 1
    
                            if(trigCnt < 1):
                                    pass
                            else:
                                    flog.append("-> "+st+"\n")
                                    flog.append("    No. of times tripped: "+
                                                trigCnt+"\n")
    
            lmutex = True
    
            print("----> Formatted string:")
            print(flog+"\n")
            return flog
    
    
    ##
    ### Software configuration
    ##
    
    def blist(bot, update):
            print("--> List command received\n")
    
            listString = "List of sensor trips in the last 5 minutes:\n"
            listString = listString+listFormat()
    
            print("> "+listString+"\n")
        bot.sendMessage('@channel', listString)
    
    
    def log(bot, update):
            print("--> Log command received\n")
    
            logString = "Log of last 10 occurrences:\n"
            logString = logString+logFormat()
    
            print("> "+logString+"\n")
        bot.sendMessage('@channel', logString)
    
    
    rfSecuBotUpd.start_polling(poll_interval=1.0,clean=True)
    
    while True:
            try:
                    time.sleep(1.1)
            except KeyboardInterrupt:
                    print("\n--> Ctrl+C key hit\n")
                    gpio.cleanup()
                    rfSecuBotUpd.stop()
                    rfSecuBot = 0
                    quit()
                    break
    
    ## Callback registration and handlers are inserted afterwards
    
    # Just in case...
    print("--> Bot exiting\n")
    gpio.cleanup()
    rfSecuBotUpd.stop()
    rfsecuBot = 0
    print("\n\n\t *** EOF[] *** \t\n\n")
    quit()
    
    # EOF []
    

P.S. I think someone might suggest a 'class' version of this. Think it'll
work?


Answer: In the `toString` function, I forgot to put `self` in front of the should-be
members `sType` and `timestamp`:

    
    
    def toString(self):
    
                if(sType == PRESSENSOR):
                        return str("-> @"+timestamp.strftime('%c')+
                                   ": Pressure sensor triggered\n")
                elif(sType == MAGSENSOR):
                        return str("-> @"+timestamp.strftime('%c')+
                                   ": Magnetic sensor triggered\n")
                else:
                        return ""
    

Which is why the value returned was always an empty string.

Note to self: check your variables!!!

On that note, that kind of explained why it didn't seem to block the thread.

