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,
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)
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:
- 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.
- 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)