Because this isn’t working. What I’m trying to do is catch the exit and terminate a multiprocessing.Process and then exiting the script. I’m doing this 'cause process_wrapper is stopping the script from closing. It hangs and I have to click the close button a second time.
I’m thinking that both of these are being ran in their own threads.
@atexit.register
def exit_handler():
if process_wrapper is not None:
process_wrapper .terminate()
Keyboard.stop()
def kill_handler(*args): # noqa
if process_wrapper is not None:
process_wrapper .terminate()
Keyboard.stop()
sys.exit(0)
signal(SIGTERM, kill_handler)
signal(SIGINT, kill_handler)
try to call kill() instead of terminate. Btw, what does that process do? I mean, does it execute some external program, or is it just something you have made? If it’s external program, it probably has a handle for SIGTERM and that’s what you are seeing
It is a wrapper for TightVNC to check if I’m connected to my notebook. When I disconnect it shuts off the display. The script has a system try icon so if for any reason I get on the computer and can double click the icon in the system try and shut off my display.
Process_Wrapper: Optional[processing.Process] = None
def start():
global Process_Wrapper
if get_object(object_name = 'tvnserver.exe', parent_name = 'services.exe', object_type = psutil.Process) is None:
print('This is a shell program for TightVNC Server.\nTightVNC Server must be running to use this script.')
exit()
Reciver, Sender = Pipe(duplex = True)
Process_Wrapper = processing.Process(
name = 'shell',
args = (Reciver, ),
target = wrapper_shell
)
Process_Wrapper.start()
ICON = Icon(
name = 'TightVNC Service',
title = 'TightVNC Service',
icon = Image.open(r'TightVNC Service.ico'),
menu = Menu(
MenuItem(
text = 'Power Off Displays',
action = lambda: Sender.send(True),
visible = False,
default = True
),
MenuItem(
text = 'Exit',
action = lambda: close()
)
)
)
ICON.run()
if __name__ == '__main__':
start()
I have a problem with both SendMessage, and PostMessage hanging in Windows 11 so I created another process that I could run long enough to shut off the display and then terminate it.
def wrapper_shell(reciver: Optional[PipeConnection] = None):
RESET: bool = False # noqa
TightVNC: Optional[psutil.Process] = get_object(object_name = 'tvnserver.exe', parent_name = 'services.exe', object_type = psutil.Process)
if TightVNC is None:
return
while True:
RESET = False
if not TightVNC.is_running():
break
if not len(TightVNC.children()) and not len(active_children()):
processing.Process(target = power_saving).start()
sleep(1)
active_children()[0].terminate()
while not len(TightVNC.children()) and not RESET:
if reciver is not None and reciver.poll(timeout = 0):
RESET = reciver.recv()
sleep(1)
sleep(1)
def power_saving():
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, 2) # off
I tried to use multiprocessing.active_children to close any child processes but it didn’t work so I gave the process a name so I cansee if it was returning None in those two functions.
The process_wrapper in your signal handler and Process_Wrapper in your main are spelled differently. Is this just how you pasted the code here or is it like that in your code? Different case means it’s a different variable. Since you are not getting an exception, I am assuming the spelling is correct.
Are signal handlers located in a different file/module by any chance? If they are indeed getting None, it seems they are reading the initial value of the Process_Wrapper, not the one after process spawning. And it’s better to check using Process_Wrapper.is_alive() if the process is still running.
I figured out what is going on and why everything is coming back None. It finally clicked to try multiprocessing.current_process().name. Somehow both of the functions are inside the Process and when I stop the script both the functions are being ran inside the Process.
@register
def exit_handler():
print(f'current_process().name == {processing.current_process().name}')
global ICON, KEYBOARD, RECIEVER, SENDER
print(f'ICON is None == {ICON is None}')
print(f'SENDER is None == {SENDER is None}')
print(f'RECIEVER is None == {ICON is None}')
print(f'KEYBOARD is None == {KEYBOARD is None}')
for process in processing.active_children():
process.terminate()
KEYBOARD.stop()
print()
def kill_handler(*args): # noqa
print(f'current_process().name == {processing.current_process().name}')
global ICON, KEYBOARD, RECIEVER, SENDER
print(f'ICON is None == {ICON is None}')
print(f'SENDER is None == {SENDER is None}')
print(f'RECIEVER is None == {ICON is None}')
print(f'KEYBOARD is None == {KEYBOARD is None}')
for process in processing.active_children():
process.terminate()
KEYBOARD.stop()
print()
sys.exit(0)
signal(SIGTERM, kill_handler)
signal(SIGINT, kill_handler)
>>> current_process().name == wrapper_shell
>>> ICON is None == True
>>> SENDER is None == True
>>> RECIEVER is None == True
>>> KEYBOARD is None == False
>>>
>>> current_process().name == wrapper_shell
>>> ICON is None == True
>>> SENDER is None == True
>>> RECIEVER is None == True
>>> KEYBOARD is None == False
Anyway, It is the child process that is closing and the MainProcess that isn’t closing.
I finally got it figured out. My problem was this …
def shell_process(reciver: Optional[PipeConnection] = None):
"""
I was already using Pipe to break out of a while loop
to power off my monitor
"""
pass
def kill_handler(sugnum, frame):
"""
This function was being looped through several
times because I was using sys.exit(). sys.exit()
was generating along with the code below, all
kinds of exceptions.
"""
if mp.current_process().name == 'ShellProcess':
for process in mp.active_children():
process.terminate() # was returning AtrributeError
if mp.current_process().name == 'MainProcess':
for process in mp.active_children():
process.terminate() # was returning AtrributeError
"""I was also getting NoneType, and KeyboardInterupt errors"""
pass
signal(SIGINT, kill_handler)
signal(SIGTERM, kill_handler)
def main():
"""
I was also having an issue with the main function as well.
Stupid as that is.
"""
RECIEVER, SENDER = mp_con.Pipe(duplex = True)
mp.Process(
name = 'ShellProcess',
args = (RECIEVER, ),
target = shell_process
).start()
"""code for Icon configuration"""
ICON.run()
if __name__ == '__main__':
main()
But I got it figured out … finally …
TERMINATE = False
def shell_process(reciver: Optional[PipeConnection] = None):
global RECIEVER
RECIEVER = reciver
def kill_handler(sugnum, frame): # noqa
if mp.current_process().name == 'ShellProcess':
RECIEVER.send(True)
if mp.current_process().name == 'MainProcess':
global TERMINATE
TERMINATE = True
signal(SIGINT, kill_handler)
signal(SIGTERM, kill_handler)
if __name__ == '__main__':
RECIEVER, SENDER = Pipe(duplex = True)
SHELL = mp.Process(
name = 'ShellProcess',
args = (RECIEVER, ),
target = shell_process
)
SHELL.daemon = True
SHELL.start()
"""code for Icon configuration"""
ICON.run_detached()
while not TERMINATE:
if SENDER.poll():
TERMINATE = SENDER.recv()
close()
So you know, this is only a small part of the code. There is allot more to it including some of my own modules.