how to get a subprocess to not get stuck if application crashes? - python

I am launching a series of subprocess one after the other, something like
for variables in my_list:
proc = subprocess.Popen(**variables)
proc.wait()
however if one of them crashes Windows pops up a window saying that the program has stopped working; since the process hasn't been killed yet, the program waits until I click the "close program" button
I'm wondering if there is a way to tell Python to open this process without UI or make it so that if it crashes it doesn't pop up error reports. The documentation doesn't seem to address any of this.
I'm using python 2.7, so I can't make use of the timeout flag that is present in python 3.

The error mode is inherited by child processes. Set it via ctypes. For example:
import ctypes
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
SEM_FAILCRITICALERRORS = 1
SEM_NOGPFAULTERRORBOX = 2
prev_error_mode = kernel32.SetErrorMode(SEM_FAILCRITICALERRORS |
SEM_NOGPFAULTERRORBOX)

Related

Closing a window opened by Subprocess

I am working on a script(let's call it p1.py) which calls another python script(let's call it p2.py). This p2.py is called with subprocess run(cmd, shell=True) and at some certain point, p2.py opens up a window that needs to be canceled with ESC. In the meantime, p1.py is just waiting for p2.py to be over. Unfortunately, this window is not avoidable coming from the library which I don't want to touch for compatibility. The question is that any ways to avoid this window or anyways to close it programmatically?
Parameters:
Language: Python 3.7
OS: Ubuntu/Linux
You can use subprocess.Popen('python py2.py') instead of subprocess.run().
In py1.py this will return immediately and give you an object to kill the process of py2.py later. However, you need to know how long you have to wait or get some other signal for py1.py when py2.py is done and the window is showing.
Try something like this in py1.py:
import subprocess
import signal
import time
p = subprocess.Popen('python py2.py')
time.sleep(...) # wait until py2.py is done
p.send_signal(signal.SIGTERM)
Note: This does not work with the shell=True parameter.

Check if the console application has the focus

I'm working on a Python console app.
I want to check if the focus is on the console of my application.
I can assume my code will be executed on a Windows PC.
At the moment I'm using this unsafe version:
import win32gui
# Before the execution starts, I assume the focus will be on the console
CURRENT_CONSOLE = win32gui.GetForegroundWindow()
...
# Check if the console has the focus
if win32gui.GetForegroundWindow() == CURRENT_CONSOLE:
...
The obvious problem is that the user can change the focus before the execution arrives to the row that defines CURRENT_CONSOLE.
There is another problem:
If I'm debugging Visual Code with the integrated console, my method cannot tell whether the focus is on the console or somewhere else in the Visual Code window (for example on the code).
Try this:
import win32gui,win32process,os
focus_window_pid = win32process.GetWindowThreadProcessId(win32gui.GetForegroundWindow())[1]
current_process_pid = os.getppid()
print(focus_window_pid == current_process_pid )
win32process.GetWindowThreadProcessId(win32gui.GetForegroundWindow())[1] will get the parent process pid, so we need to use os.getppid() to get the parent pid of python process.
But if you are using other method to run your python script(eg: vscode), this method may not work.

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?
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"

sending keyboard interrupt programmatically

An application that asks for a keyboard interrupt. How can I send for a keyboard interrupt programmatically? I need it for automation.
Like <C-c> or <C-x>
KeyboardInterrupt
Code running in a separate thread can cause a KeyboardInterrupt to be generated in the main thread by calling thread.interrupt_main().
See https://docs.python.org/2/library/thread.html#thread.interrupt_main
Since you mention automation I assume you want a SendKeys for Python. Try this: http://rutherfurd.net/python/sendkeys/
My suggestion to solve this problem is to use the following code pattern. I used it to programmatically start a tensorboard server and shut it down by sending a CTRL-C when the object it belongs to is deleted. Generally speaking, this should work for any example that provokes a subprocess that is supposed to be send a KeyBoardInterrupt:
Import signal and subprocess
import signal
import subprocess
Create the subprocess using subprocess.Popen. Important: set the creationflags parameter to subprocess.CREATE_NEW_PROCESS_GROUP. This is necessary to later be able to send the KeyboardInterrupt event.
command= <enter your command that is supposed to be run in different process as a string>
process = subprocess.Popen(command.split(),stdout=subprocess.PIPE,stdin=subprocess.PIPE,creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
Wherever you want to send the keyboardInterrupt, do the following:
process.send_signal(signal.CTRL_C_EVENT)
That is it! Please see the the official subprocess documentation for insights on why the creationflags parameter of popen has to be set that way.
This is how the code looks for my example in a less generic way:
import signal
import subprocess
import time
class ExperimentTracker():
def __init__(self):
self.tensorboard_process=None
def __del__(self):
#shutdown tensorboard server and terminate process
self.tensorboard_process.send_signal(signal.CTRL_C_EVENT)
time.sleep(0.2)
self.tensorboard_process.kill()
def launch_tensorboard(self):
#launch tensorboard
bashCommand= "tensorboard --logdir runs"
self.tensorboard_process = subprocess.Popen(bashCommand.split(),stdout=subprocess.PIPE,stdin=subprocess.PIPE,creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
time.sleep(2) #sleep for 2 seconds to give tensorboard time to be launched
If say you want to run a program via shell ./program. In Linux what you could do is:
# Made a function to handle more complex programs which require multiple inputs.
run(){
./program
}
# Making a child process that will run the program while you stop it later.
run &
childPid=($!)
# Process id of the program which you want to interrupt (via `run`).
# Time after which you want to interrupt
sleep 5
# Actual command to send the interrupt
kill -SIGINT $childPid
Let me know if it works in windows as well.

How to kill headless X server started via Python?

I want to get screenshots of a webpage in Python. For this I am using http://github.com/AdamN/python-webkit2png/ .
newArgs = ["xvfb-run", "--server-args=-screen 0, 640x480x24", sys.argv[0]]
for i in range(1, len(sys.argv)):
if sys.argv[i] not in ["-x", "--xvfb"]:
newArgs.append(sys.argv[i])
logging.debug("Executing %s" % " ".join(newArgs))
os.execvp(newArgs[0], newArgs)
Basically calls xvfb-run with the correct args. But man xvfb says:
Note that the demo X clients used in the above examples will not exit on their own, so they will have to be killed before xvfb-run will exit.
So that means that this script will <????> if this whole thing is in a loop, (To get multiple screenshots) unless the X server is killed. How can I do that?
The documentation for os.execvp states:
These functions all execute a new
program, replacing the current
process; they do not return. [..]
So after calling os.execvp no other statement in the program will be executed. You may want to use subprocess.Popen instead:
The subprocess module allows you to
spawn new processes, connect to their
input/output/error pipes, and obtain
their return codes. This module
intends to replace several other,
older modules and functions, such as:
Using subprocess.Popen, the code to run xlogo in the virtual framebuffer X server becomes:
import subprocess
xvfb_args = ['xvfb-run', '--server-args=-screen 0, 640x480x24', 'xlogo']
process = subprocess.Popen(xvfb_args)
Now the problem is that xvfb-run launches Xvfb in a background process. Calling process.kill() will not kill Xvfb (at least not on my machine...). I have been fiddling around with this a bit, and so far the only thing that works for me is:
import os
import signal
import subprocess
SERVER_NUM = 99 # 99 is the default used by xvfb-run; you can leave this out.
xvfb_args = ['xvfb-run', '--server-num=%d' % SERVER_NUM,
'--server-args=-screen 0, 640x480x24', 'xlogo']
subprocess.Popen(xvfb_args)
# ... do whatever you want to do here...
pid = int(open('/tmp/.X%s-lock' % SERVER_NUM).read().strip())
os.kill(pid, signal.SIGINT)
So this code reads the process ID of Xvfb from /tmp/.X99-lock and sends the process an interrupt. It works, but does yield an error message every now and then (I suppose you can ignore it, though). Hopefully somebody else can provide a more elegant solution. Cheers.

Categories