Python Script launches new Python Script - python

I have a script "run.py" that must print "Hello", launch another script "run2.py", and then terminate (do not wait for run2.py to return).
run2.py is not in the local directory and is only required to print "Hello again".
How can I do this?
# run_path = "C:/Program Files (x86)/xxx/run.py"
# run2_path = "//network_share/folder/run2.py"
**run.py**
import os
print("Hello")
# What do I do here?
# os.execl("//network_share/folder/run2.py")
exit()
**run2.py**
print("Hello again")

This seems to work for a script I have in the same folder I'm running this one in.
This should verify that the first script finishes and doesn't linger while the second script runs in its own process. It is possible on some systems, due to their configuration, the child process will terminate when the parent does. But not in this case...
I put more time into this post to add code that shows how to check if the parent process is still running. This would be a good way for the child to ensure it's exited. Also shows how to pass parameters to the child process.
# launch.py
import subprocess as sp
import os
if __name__ == '__main__':
sp.Popen(['ps']) # Print out runniing processes.
print("launch.py's process id is %s." % os.getpid())
# Give child process this one's process ID in the parameters.
sp.Popen(['python3', 'runinproc.py', str(os.getpid())])
# ^^^ This line above anwers the main question of how to kick off a
# child Python script.
print("exiting launch.py")
Other script.
# runinproc.py
import time
import subprocess as sp
import sys
import os
def is_launcher_running():
try:
# This only checks the status of the process. It doesn't
# kill it, or otherwise affect it.
os.kill(int(sys.argv[1]), 0)
except OSError:
return False
else:
return True
if __name__ == '__main__':
print("runinproc.py was launched by process ID %s" % sys.argv[1])
for i in range(100):
if is_launcher_running():
# Is launch.py still running?
print("[[ launch.py is still running... ]]")
sp.Popen(['ps']) # Print out the running processes.
print("going to sleep for 2 seconds...")
time.sleep(2)
Bash output:
Todds-iMac:pyexperiments todd$ python3 launch.py
launch.py process id is 40975.
exiting launch.py
Todds-iMac:pyexperiments todd$ runinproc.py was launched by process ID 40975
going to sleep for 2 seconds...
PID TTY TIME CMD
PID TTY TIME CMD
40866 ttys000 0:00.09 -bash
40866 ttys000 0:00.09 -bash
40977 ttys000 0:00.04 /Library/Frameworks/Python.framework/Versions/3.8/Resources/Python.app/C
40977 ttys000 0:00.04 /Library/Frameworks/Python.framework/Versions/3.8/Resources/Python.app/C
going to sleep for 2 seconds...
PID TTY TIME CMD
40866 ttys000 0:00.09 -bash
40977 ttys000 0:00.04 /Library/Frameworks/Python.framework/Versions/3.8/Resources/Python.app/C
going to sleep for 2 seconds...
PID TTY TIME CMD
40866 ttys000 0:00.09 -bash
40977 ttys000 0:00.04 /Library/Frameworks/Python.framework/Versions/3.8/Resources/Python.app/C
going to sleep for 2 seconds...
Note that the first call to the shell, ps from launch.py is executed after launch.py exited. That's why it doesn't show up in the printed process list.

subprocess is your friend, but if you need to not wait, check out the P_NOWAIT--replacing example code in https://docs.python.org/3/library/subprocess.html
EG:
pid = Popen(["/bin/mycmd", "myarg"]).pid
I don't think .communicate is what you need this time around - isn't it more for waiting?

The cleanest way to do this (since both scripts are written in pure Python) is to import the other script as a module and execute its content, placed within a function:
run.py
import os
import sys
sys.path.append("//network_share/folder/")
import run2
print("Hello")
run2.main()
exit()
run2.py
def main():
print("Hello again")

Related

Can I get the PID of program launched by a subprogram?

My python script runs a program, let's call it X.exe
from subprocess import Popen
process = Popen('D:X.exe')
I can get its PID via
process.pid
After some time (which I know) X.exe launches another program - Y.exe. Can I get the PID of the Y.exe process? NOTE: I DON'T KNOW WHAT THE Y.exe WINDOW WILL BE NAMED
you can see the children of a process using psutil simply as follows.
import psutil
import subprocess
import time
proc = subprocess.Popen("start /wait", shell=True) # start another cmd.exe
time.sleep(0.5) # wait for child to start
process_object = psutil.Process(proc.pid)
children = process_object.children()
print(f"child pid is {children[0].pid}")
proc.wait()
child pid is 18292

kill process do not kill the subprocess and do not close a terminal window

I am working on UBUNTU and I have file main.py with a code inside:
#!/usr/bin/env python3
# coding=utf-8
import os
import time
from subprocess import Popen, PIPE, call, signal
base_path = os.path.abspath('')
path_to_file = base_path + '/test_subprocess.py'
p = Popen(['gnome-terminal', "--", path_to_file])
time.sleep(2)
os.kill(p.pid, signal.SIGKILL)
I have test_subprocess.py with code like that:
#!/usr/bin/env python3
# coding=utf-8
import time
def print_message():
while True:
print('I am working!')
time.sleep(0.5)
print_message()
I tried to kill the subprocess but after
os.kill(p.pid, signal.SIGKILL)
subprocess is still working and prints 'I am working!'
How can I finish subprocess and how to close gnome terminal?
If I selected completely wrong way. Can you show me working example?
New version of test_subprocess.py
#!/usr/bin/env python3
# coding=utf-8
import sys
from subprocess import signal
import time
def print_message():
while True:
print('I am working!')
time.sleep(0.5)
if signal.SIGKILL: # it is braking a loop when parent process terminate!
print('I am killing self!')
break
print_message()
Should I do it like above?
You could try the following:
p = Popen(['gnome-terminal', "--", path_to_file])
PIDs = p.pid
os.system("kill {0}".format(PIDs))
Popen.pid The process ID of the child process.
Note that if you set the shell argument to True, this is the process
ID of the spawned shell.
http://docs.python.org/library/subprocess.html
This will at least kill the correct process. Not sure if it will close the terminal.
Edit: to kill the process and close the terminal:
p = Popen(['gnome-terminal', '--disable-factory', '-e', path_to_file], preexec_fn=os.setpgrp)
os.killpg(p.pid, signal.SIGINT)
Credit to https://stackoverflow.com/a/34690644/15793575, whih I modified for your command:
--disable-factory is used to avoid re-using an active terminal so that we can kill newly created terminal via the subprocess handle
os.setpgrp puts gnome-terminal in its own process group so that
os.killpg() could be used to send signal to this group
Popen.pid
The process ID of the child process.
Note that if you set the shell argument to True, this is the process
ID of the spawned shell.
Try setting the shell argument of the Popen constructor to False. (p = Popen(['gnome-terminal', "--", path_to_file]) -> p = Popen(['gnome-terminal', "--", path_to_file], shell=False)). I had a similar issue not long ago - this fixed it for me.

Creating a Flag file

I'm relatively new to python so please forgive early level understanding!
I am working to create a kind of flag file. Its job is to monitor a Python executable, the flag file is constantly running and prints "Start" when the executable started, "Running" while it runs and "Stop" when its stopped or crashed, if a crash occurs i want it to be able to restart the script. so far i have this down for the Restart:
from subprocess import run
from time import sleep
# Path and name to the script you are trying to start
file_path = "py"
restart_timer = 2
def start_script():
try:
# Make sure 'python' command is available
run("python "+file_path, check=True)
except:
# Script crashed, lets restart it!
handle_crash()
def handle_crash():
sleep(restart_timer) # Restarts the script after 2 seconds
start_script()
start_script()
how can i implement this along with a flag file?
Not sure what you mean with "flag", but this minimally achieves what you want.
Main file main.py:
import subprocess
import sys
from time import sleep
restart_timer = 2
file_path = 'sub.py' # file name of the other process
def start():
try:
# sys.executable -> same python executable
subprocess.run([sys.executable, file_path], check=True)
except subprocess.CalledProcessError:
sleep(restart_timer)
return True
else:
return False
def main():
print("starting...")
monitor = True
while monitor:
monitor = start()
if __name__ == '__main__':
main()
Then the process that gets spawned, called sub.py:
from time import sleep
sleep(1)
print("doing stuff...")
# comment out to see change
raise ValueError("sub.py is throwing error...")
Put those files into the same directory and run it with python main.py
You can comment out the throwing of the random error to see the main script terminate normally.
On a larger note, this example is not saying it is a good way to achieve the quality you need...

Can I close the CMD window that opened with subprocess.Popen in Python?

I have a program that need to run small tasks in new CMDs.
For example:
def main()
some code
...
proc = subprocess.Popen("start.bat")
some code...
proc.kill()
subprocess,Popen opens a new cmd window and runs "start.bat" in it.
proc.kill() kills the process but doesn't close the cmd window.
Is there a way to close this cmd window?
I thought about naming the opened cmd window so i can kill it with the command:
/taskkill /f /im cmdName.exe
Is it possible ?if no, What do you suggest ?
Edit, Added Minimal, Complete, and Verifiable example:
a.py:
import subprocess,time
proc = subprocess.Popen("c.bat",creationflags=subprocess.CREATE_NEW_CONSOLE)
time.sleep(5)
proc.kill()
b.py
while True:
print("IN")
c.bat
python b.py
that's expected when a subprocess is running. You're just killing the .bat process.
You can use psutil (third party, use pip install psutil to install) to compute the child processes & kill them, like this:
import subprocess,time,psutil
proc = subprocess.Popen("c.bat",creationflags=subprocess.CREATE_NEW_CONSOLE)
time.sleep(5)
pobj = psutil.Process(proc.pid)
# list children & kill them
for c in pobj.children(recursive=True):
c.kill()
pobj.kill()
tested with your example, the window closes after 5 seconds
here is another way you can do it
import subprocess
from subprocess import Popen,CREATE_NEW_CONSOLE
command ='cmd'
prog_start=Popen(command,creationflags=CREATE_NEW_CONSOLE)
pidvalue=prog_start.pid
#this will kill the invoked terminal
subprocess.Popen('taskkill /F /T /PID %i' % pidvalue)

Starting a separate process

I want a script to start a new process, such that the new process continues running after the initial script exits. I expected that I could use multiprocessing.Process to start a new process, and set daemon=True so that the main script may exit while the created process continues running.
But it seems that the second process is silently terminated when the main script exits. Is this expected behavior, or am I doing something wrong?
From the Python docs:
When a process exits, it attempts to
terminate all of its daemonic child
processes.
This is the expected behavior.
If you are on a unix system, you could use os.fork:
import os
import time
pid=os.fork()
if pid:
# parent
while True:
print("I'm the parent")
time.sleep(0.5)
else:
# child
while True:
print("I'm just a child")
time.sleep(0.5)
Running this creates two processes. You can kill the parent without killing the child.
For example, when you run script you'll see something like:
% script.py
I'm the parent
I'm just a child
I'm the parent
I'm just a child
...
Stop the script with ctrl-Z:
^Z
[1]+ Stopped script.py
Find the process ID number for the parent. It will be the smaller of the two process ID numbers since the parent came first:
% ps axuw | grep script.py
unutbu 6826 0.1 0.1 33792 6388 pts/24 T 15:09 0:00 python /home/unutbu/pybin/script.py
unutbu 6827 0.0 0.1 33792 4352 pts/24 T 15:09 0:00 python /home/unutbu/pybin/script.py
unutbu 6832 0.0 0.0 17472 952 pts/24 S+ 15:09 0:00 grep --color=auto script.py
Kill the parent process:
% kill 6826
Restore script.py to the foreground:
% fg
script.py
Terminated
You'll see the child process is still running:
% I'm just a child
I'm just a child
I'm just a child
...
Kill the child (in a new terminal) with
% kill 6827
Simply use the subprocess module:
import subprocess
subprocess.Popen(["sleep", "60"])
Here is a related question on SO, where one of the answers gives a nice solution to this problem:
"spawning process from python"
If you are on a unix system (using docs):
#!/usr/bin/env python3
import os
import sys
import time
import subprocess
import multiprocessing
from multiprocessing import Process
def to_use_in_separate_process(*args):
print(args)
#check args before using them:
if len(args)>1:
subprocess.call((args[0], args[1]))
print('subprocess called')
def main(apathtofile):
print('checking os')
if os.name == 'posix':
print('os is posix')
multiprocessing.get_context('fork')
p = Process(target=to_use_in_separate_process, args=('xdg-open', apathtofile))
p.run()
print('exiting def main')
if __name__ == '__main__':
#parameter [1] must be some file that can be opened by xdg-open that this
#program uses.
if len(sys.argv)>1:
main(sys.argv[1])
print('we can exit now.')
else:
print('no parameters...')
print('mother program will end now!')
sys.exit(0)
In Ubuntu the following commands keep working even though the python app exit.
url = "https://www.youtube.com/watch?v=t3kcqTE6x4A"
cmd = f"mpv '{url}' && zenity --info --text 'you have watched {url}' &"
os.system(cmd)

Categories