Python: Keep processes started by subprocess.Popen alive after exiting - python

I am making a virtual assistant that can start several programs using subprocess.Popen("path/to/app.exe"). But when I exit the python program, all of processes are killed. I want the processes (the applications started with Popen) to be independent and remain alive after main process is killed.
I have tried adding start_new_session=True as argument in subprocess.Popen() as some posts have suggested, but it's still not working.
I don't think showing the code is necessary, but still, here you go.
app_path = r'C:\Users\myusername\AppData\Local\Discord\app-1.0.9001\discord.exe'
subprocess.Popen(app_path) # also tried adding start_new_session=True as argument

Since you're on Windows, you can call the start command, which exists for this very purpose: to run another program independently of the one that starts it.
The start command is provided by the command-line interpreter cmd.exe. It is not an executable: there is no start.exe. It is a "shell command" (in Linux terminology), which is why shell=True must be passed when creating the subprocess.
You won't be able to communicate with the subprocess started in this way, that is, not via the pipe mechanism provided by the subprocess module. So instead of Popen, you may just use the convenience function run:
from subprocess import run
app = 'notepad'
run(['start', app], shell=True)
The example starts the Notepad editor (instead of Discord in the question) in order to make it easier to reproduce.
In cases where the full path to the app contains spaces, we can either call start like so
app = r'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe'
run(f'start "" "{app}"', shell=True)
using the Edge browser in this example, or pass the directory separately:
folder = r'C:\Program Files (x86)\Microsoft\Edge\Application'
app = 'msedge.exe'
run(['start', '/d', folder, app], shell=True)
This is needed because start treats a single argument as the window title if that argument is in quotes. And only if not does it treat it as the command. See "Can I use the start command with spaces in the path?" (on SuperUser) for more details.

Answered here: https://stackoverflow.com/a/34718600/4355695
subprocess.Popen(full_command, shell=True, close_fds=True)
(In my linux system I have to put shell=True if I'm passing a full command as string instead of a split-up array of arguments. In windows it may differ, idk)

Related

What's the alternative to subprocess.CREATE_NEW_CONSOLE in Linux?

I have a Python 3.9 script that starts another process in a new console. The 'other process' keeps running even after the original one has completed.
This is what I have on Windows:
# startup.py script
# =================
import sys, subprocess
if __name__ == '__main__':
print('start startup.py script')
arguments = ['python', 'other_process.py']
arguments.extend(sys.argv[1:])
subprocess.Popen(
arguments,
creationflags = subprocess.CREATE_NEW_CONSOLE,
)
print('end startup.py script')
It works great. Below you see the original console on the left, in which I invoke startup.py. I also pass it a --help flag, which is then simply passed to the other_process.py script.
On the right, you see the other_process.py script running. Please note that the original startup.py script has already finished, while the other_process.py script is still running. That's exactly what I need:
The subprocess.CREATE_NEW_CONSOLE parameter doesn't work on Linux. I've heard that setting shell=True would have a similar effect, but it doesn't spawn a new console.
How can I get the same effect on Linux?
Unix doesn’t provide this option/service, but you can run a terminal emulator:
subprocess.Popen(["gnome-terminal","--"]+arguments)
There isn’t a standard means of finding which terminal emulator to use (or even which are available), unfortunately. Checking shutil.which for a few common ones might be the right idea; from Wikipedia’s list, I’d recommend gnome-terminal, konsole, and xterm. You still then have to deal with the slightly different syntax to run a command in each.

How to run python script using subprocess from another delivery

I need to run another python script which generating data in my script I current working with. I use subprocess to run it:
cmd = 'python /home/usr/script.py arg1 arg2 arg3'
subprocess.Popen(cmd, shell=True)
But have a problem. Previous script generate few directories in 'current directory', it means in directory it was run in. And I can't modify previous script, cause it's not mine. How to set current directory to dir where I want to get data? \n
Another small problem is that when I run subprocess.Popen() my script doesn't end. Should I run it in another way?
the best way is to use subprocess.call instead (waits & terminates, Popen without the relevant wait() may create a zombie process) and use the cwd= parameter to specify current dir for the subprocess:
cmd = ['python','/home/usr/script.py','arg1','arg2','arg3']
return_code = subprocess.call(cmd, cwd="/some/dir")
(also pass the command as a list, and drop shell=True, you don't need it here)

Launch a single python script as different processes differing by command line arguments

I have python script that takes command line arguments. The way I get the command line arguments is by reading a mongo database. I need to iterate over the mongo query and launch a different process for the single script with different command line arguments from the mongo query.
Key is, I need the launched processes to be:
separate processes share nothing
when killing the process, I need to be able to kill them all easily.
I think the command killall -9 script.py would work and satisfies the second constraint.
Edit 1
From the answer below, the launcher.py program looks like this
def main():
symbolPreDict = initializeGetMongoAllSymbols()
keys = sorted(symbolPreDict.keys())
for symbol in keys:
# Display key.
print(symbol)
command = ['python', 'mc.py', '-s', str(symbol)]
print command
subprocess.call(command)
if __name__ == '__main__':
main()
The problem is that mc.py has a call that blocks
receiver = multicast.MulticastUDPReceiver ("192.168.0.2", symbolMCIPAddrStr, symbolMCPort )
while True:
try:
b = MD()
data = receiver.read() # This blocks
...
except Exception, e:
print str(e)
When I run the launcher, it just executes one of the mc.py (there are at least 39). How do I modify the launcher program to say "run the launched script in background" so that the script returns to the launcher to launch more scripts?
Edit 2
The problem is solved by replacing subprocess.call(command) with subprocess.Popen(command)
One thing I noticed though, if I say ps ax | grep mc.py, the PID seem to be all different. I don't think I care since I can kill them all pretty easily with killall.
[Correction] kill them with pkill -f xxx.py
There are several options for launching scripts from a script. The easiest are probably to use the subprocess or os modules.
I have done this several times to launch things to separate nodes on a cluster. Using os it might look something like this:
import os
for i in range(len(operations)):
os.system("python myScript.py {:} {:} > out.log".format(arg1,arg2))
using killall you should have no problem terminating processes spawned this way.
Another option is to use subprocess which has got a wide range of features and is much more flexible than os.system. An example might look like:
import subprocess
for i in range(len(operations)):
command = ['python','myScript.py','arg1','arg2']
subprocess.call(command)
In both of these methods, the processes are independent and share nothing other than a parent PID.

Python subprocess.check_output() seems to ignore arguments

First of all, I read as many related questions to subprocess.check_output() as I could find, but still struggle to identify the problem.
If I execute kill -l 1 in the shell, I get the corresponding signal name for 1, which is HUP. I need the same behaviour in my python script, so I use:
>>> subprocess.check_output(['kill', '-l', '1'])
b'HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2 PIPE ALRM TERM STKFLT\nCHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM PROF WINCH POLL PWR SYS\n'
The subprocess seems to ignore the '1' in the argument list and instead executes kill -l.
I tried different versions, the argument as a list or string, with shell optione True and False, but none seem to work.
Any ideas what could be the reason? Using python3.4 on a Ubuntu14.04.
Thanks!
Possible cause: The kill command in your shell is executing a shell built-in (most shells have one, because you need to be able to kill without a process launch when you have runaway fork bombs and the like), whereas check_output (not executing within a shell by default) is running the kill executable found in your PATH (often /bin/kill, but not always, running type -P kill in bash will tell you where that executable is).
Odds are, the built-in supports the switches you're looking for, the executable does not. In bash, try running type -P kill, then explicitly running the /full/path/to/kill -l 1 to see if the kill check_output is finding actually supports that invocation. Often there are subtle differences between different implementations of kill.
The best solution to this is probably to avoid expensive and pointless subprocess launches and check the Python definitions for the signals. For example, in Python 3.5, it's trivial to construct a mapping from the signals known by Python to their Python names:
import signal
sigdict = {sig.value: sig.name for sig in signal.Signals}
print(sigdict[1])
CTRL_BREAK_EVENT # <-- The output on my Windows box. On your machine, it would probably be SIGHUP
In older Python where the names aren't enums, you can use similar code using dir of the module, filtering for names whose __module__ is signal and whose values are integers to construct the mapping.

Python: Spawn New Minimized Shell

I am attempting to to launch a python script from within another python script, but in a minimized console, then return control to the original shell.
I am able to open the required script in a new shell below, but it's not minimized:
#!/usr/bin/env python
import os
import sys
import subprocess
pyTivoPath="c:\pyTivo\pyTivo.py"
print "Testing: Open New Console"
subprocess.Popen([sys.executable, pyTivoPath], creationflags = subprocess.CREATE_NEW_CONSOLE)
print
raw_input("Press Enter to continue...")
Further, I will need to be able to later remotely KILL this shell from the original script, so I suspect I'll need to be explicit in naming the new process. Correct?
Looking for pointers, please. Thanks!
Note: python27 is mandatory for this application. Eventually will also need to work on Mac and Linux.
Do you need to have the other console open? If you now the commands to be sent, then I'd recommend using Popen.communicate(input="Shell commands") and it will automate the process for you.
So you could write something along the lines of:
# Commands to pass into subprocess (each command is separated by a newline)
commands = (
"command1\n" +
"command2\n"
)
# Your process
py_process = subprocess.Popen(*yourprocess_here*, stdin=PIPE, shell=True)
# Feed process the needed input
py_process.communicate(input=commands)
# Terminate when finished
py_process.terminate()
The code above will execute the process you specify and even send commands but it won't open a new console.

Categories

Resources