pythonwindowsbatch-filewindows-console

Stop KeyboardInterrupt reaching batch file processor


I'm writing some code in Python 3 on Windows that looks like this:

try:
    do something that takes a long time
    (training a neural network in TensorFlow, as it happens)
except KeyboardInterrupt:
    print('^C')
print a summary of results
still useful even if the training was cut short early

This works perfectly if run directly from the console with python foo.py.

However, if the call to Python was within a batch file, it ends up doing all the above but then still spamming the console with the 'terminate batch job' prompt.

Is there a way to stop that happening? By fully eating the ^C within Python, jumping all the way out of the batch file or otherwise?


Solution

  • Use the break (More info here) command in the batch file, which will disable CTRL+C halting the file

    EDIT: According to this site of the break command

    Newer versions of Windows (Windows ME, Windows 2000, Windows XP, and higher) only include this command for backward compatibility and turning the break off has no effect.

    I personally tested this, and can confirm, I will edit when I find a workaround

    EDIT #2: If you could have a second batch script that runs start "" /b /wait cmd /c "yourfile.bat" although, this is known to cause glitches with other nested batch files

    The flag to disable Ctrl+C is inherited by child processes, so Python will no longer raise a KeyboardInterrupt. Plus we still have bugs here in Python if reading from the console gets interrupted by Ctrl+C without getting a SIGINT from the CRT. The Python script should manually enable Ctrl+C via ctypes. Use import ctypes; kernel32 = ctypes.WinDLL('kernel32', use_last_error=True); success = kernel32.SetConsoleCtrlHandler(None, False)

    EDIT #3 As pointed by Eryksyn (in the comments), you can use cytpes to ENABLE it;

    import ctypes; 
    kernel32 = ctypes.WinDLL('kernel32', use_last_error=True); success = kernel32.SetConsoleCtrlHandler(None, False)
    

    EDIT #4: I think I found it, try this (Although it may not work) Can you use the threading import?

    import time
    from threading import Thread
    
    def noInterrupt():
        for i in xrange(4):
            print i
            time.sleep(1)
    
    a = Thread(target=noInterrupt)
    a.start()
    a.join()
    print "done"