Process termination breaks python curses

Using multiprocessing python and curses, it seems that the completion of the process interferes with the display of curses.
For example, in the following code, why does terminating the process stop curses from displaying text? (press b after press a)
More precisely, it seems that not only the string "hello" is no longer displayed, but also the entire curses window.

import curses from multiprocessing import Process from time import sleep def display(stdscr): stdscr.clear() curses.newwin(0,0) stdscr.timeout(500) p = None while True: stdscr.addstr(1, 1, "hello") stdscr.refresh() key = stdscr.getch() if key == ord('a') and not p: p = Process(target = hang) p.start() elif key == ord('b') and p: p.terminate() def hang(): sleep(100) if __name__ == '__main__': curses.wrapper(display) 

I am running python 3.6 on GNU / Linux.

Edit:
I can still play this more stripped-down version that does not call sleep (). Now just pressing "a" causes an error.

 import curses from multiprocessing import Process def display(stdscr): stdscr.clear() curses.newwin(0,0) stdscr.timeout(500) p = None while True: stdscr.addstr(1, 1, "hello") stdscr.refresh() key = stdscr.getch() if key == ord('a') and not p: p = Process(target = hang) p.start() p.terminate() def hang(): while True: temp = 1 + 1 if __name__ == '__main__': curses.wrapper(display) 
+1
source share
2 answers

The following code works:

 import curses from multiprocessing import Process p = None def display(stdscr): stdscr.clear() curses.newwin(0,0) stdscr.timeout(500) while True: stdscr.addstr(1, 1, "hello") stdscr.refresh() key = stdscr.getch() if key == ord('a') and not p: p.start() p.terminate() def hang(): while True: temp = 1 + 1 if __name__ == '__main__': p = Process(target = hang) curses.wrapper(display) 

I created a new Process before initializing the user interface using curses.wrapper() . OK, why does this work? To do this, we need to know how Proccess works and what exactly it does when you call Process(target = hang) :

fork

The parent process uses os.fork () to format the Python interpreter. The child process, when it begins, is virtually identical to the parent process. All parent resources are inherited by the child process. Please note that securely formatting a multi-threaded process is problematic.

Available for Unix only. The default value for Unix.

Now, what does this tell us? You create new Processes when you have already created the curses screen. What does curses.wrapper () do mean?

Before calling func wrapper (), turns on cbreak mode, disables echo, turns on the terminal keyboard and initializes colors if the terminal supports color. When exiting (in both ordinary and exceptional cases), it restores the cooked mode, turns on the echo and turns off the terminal keyboard.

Well, we have a newly created child process that has the same resources as its parent. When you call terminate() to kill a child, it frees up all resources, including the curses shell. It restores the previous settings of the terminal and, therefore, violates your interface.

To fix this, you must implement your program in different ways. Create a new process in advance, use IPC to communicate with your process, use Process Pools if you need several processes, Threading or Thread Pools if you have IO related tasks.

+1
source

Perhaps you are using this on windows? One of the documented requirements of the multiprocessing module on this platform is that all the top-level code ( curses.wrapper(display) in your case) MUST be inside the if __name__ == '__main__': block if __name__ == '__main__': so that it is not accidentally executed in your child process.

I think that what happens here is that your generated process initializes the curses themselves (which is associated with setting the console accordingly), and then returns the console to its normal state when it ends, thereby canceling the settings made by the original program.

+1
source

Source: https://habr.com/ru/post/1275503/


All Articles