Home > Blockchain >  What is the best explicit and implicit way to use a class contains an infinite loop in its methods?
What is the best explicit and implicit way to use a class contains an infinite loop in its methods?

Time:11-30

I'm trying to code a class which includes a method that loops infinetly until an explicit interruption request made like below:

# module_2

import sys
from time import sleep
from pathlib import Path


class Receiver:
    
    
    def __init__(self, *args, **kwargs):
        
        self.identified = False
        self.id_= hex(id(self))
        
        
    @property
    def get_id(self):
        
        self.identified = True
        return self.id_
        
        
    def write_id(self):
        
        self.identified = True
        with open('active_listeners.lst', 'a') as f:
            f.write(str(self.id_))
        return True
    
    
    def _is_interrupted(self):
        
        flag = Path.cwd().joinpath(Path(f'{self.id_}.interrupt'))
        
        if flag.exists():
            flag.unlink()
            return True
        return False
        
        
    def listen(self):
        
        if not self.identified:
            print("can't start listening without identification or else can't stop listening.")
            print("use 'get_id' or 'write_id' to identify me.")
            return False
        
        while True:
            try:
                print('listening...')
                sleep(5)
                result = 'foo'
                print(f'got result: {result}')
                with open(f'results_{self.id_}.tmp', 'w') as f:
                    f.write(result)
                        
                if self._is_interrupted():
                    sys.exit(0)
            
            except KeyboardInterrupt:
                sys.exit(0)
    
                            
if __name__ == '__main__':
            
            r = Receiver()
            r.write_id()
            r.listen()

The Receiver could be used either executing it as a standalone object or through importing it into another module like module_1.

I have two question here,

  1. What is the best way of using Receiver by importing it in another module (say main) without suspending the code execution flow of main module when Receiver.listen() called? (listen is I/O bound and I'm on Windows)

  2. What is the best way of using Receiver without explicitly importing it but starting the module_2 from main module as a completely separeted process as if executing 'python -m module_2' in a separeted shell on Windows. In this use, I need the shell to remain opened for monitoring the outputs and stop listening with 'ctrl c'

CodePudding user response:

  1. Run it in a thread sort of like you do when it's run as main:
from pathlib import Path
from threading import Thread
from module2 import Receiver

r = Receiver()
r.write_id()
t = Thread(target=r.listen)
t.start()

#do some other stuff...

#Path.cwd().joinpath(Path(f'{r.id_}.interrupt'))
#shorter way to write this: 
flag = Path.cwd() / f'{r.id_}.interrupt'
with open(flag, 'w'): #"touch" your stopflag
    pass
t.join() #wait for the thread to exit

Using keyboardinterrupt in that instance is bad practice because it could kill the whole script with no waring by calling sys.exit(0). Generally libraries shouldn't call exit on their own. Always let the main script do that. It better to simply break from the loop, and let the function return execution to whatever started it. If it's in its own process, when the function returns it will reach the end of the file and exit on its own anyway.

  1. An easy way to run it as its own main script with its own cmd window is to call the "start" command using os.system (This command will have to change if you run on another OS).
from sys import argv
from time import sleep
import os

if __name__ == "__main__":
    if '-child' not in argv: #don't fork bomb yourself...
        cmd = f"start cmd /k python \"{__file__}\" -child"
        #/k will keep the window open after the script completes
        #/c will close immediately after completion
        os.system(cmd)
    for i in range(10): #just a simple example to demonstrate starting a new window...
        print(i)
        sleep(1)
  • Related