How to make a script automatically restart itself? - python

How do you make a python program automatically restart itself? So let's say there is a really simple program like:
var = input("Hi! I like cheese! Do you like cheese?").lower()
if var == "yes":
print("That's awesome!")
Now, in a Python Shell, you would have to press either the Run button and then 'Run Module (F5)' or just the F5 key on your keyboard. That is the first time you run it. When the program ended, you would go back to your Cheese.py file and then press F5 to run the program again.
Everybody with me here?
OK, so my question is, how do you make the program restart itself automatically without you having to manually do it?

It depends on what you mean by "restart itself." If you just want to continuously execute the same code, you can wrap it in a function, then call it from within a while True loop, such as:
>>> def like_cheese():
... var = input("Hi! I like cheese! Do you like cheese?").lower() # Corrected the call to `.lower`.
... if var == "yes":
... print("That's awesome!")
...
>>> while True:
... like_cheese()
...
Hi! I like cheese! Do you like cheese?yes
That's awesome!
Hi! I like cheese! Do you like cheese?yes
That's awesome!
If you want to actually restart the script you can execute the script again, replacing the current process with the new one by doing the following:
#! /bin/env python3
import os
import sys
def like_cheese():
var = input("Hi! I like cheese! Do you like cheese?").lower()
if var == "yes":
print("That's awesome!")
if __name__ == '__main__':
like_cheese()
os.execv(__file__, sys.argv) # Run a new iteration of the current script, providing any command line args from the current iteration.
This will continuously re-run the script, providing the command line arguments from the current version to the new version. A more detailed discussion of this method can be found in the post "Restarting a Python Script Within Itself" by Petr Zemek.
One item that this article notes is:
If you use the solution above, please bear in mind that the exec*()
functions cause the current process to be replaced immediately,
without flushing opened file objects. Therefore, if you have any
opened files at the time of restarting the script, you should flush
them using f.flush() or os.fsync(fd) before calling an exec*()
function.

or you can try
$ chmod a+x "name".py
Then, you can run the script via
$ ./daemon.py
In such a situation, to restart the script, use the following code:
os.execv(__file__, sys.argv)
Otherwise, when you run the script via
$ python daemon.py
use this code:
os.execv(sys.executable, ['python'] + sys.argv)
Either way, do not forget to import the sys module

I use terminal on my Mac to re-start some of my python scripts with the function below.
import subprocess
def run_again(cmd):
subprocess.call(["bash", "-c", "source ~/.profile; " + cmd])
Note: Don't forget the space character after "profile;" or the function may fail silently!
Then at the bottom of my script to be re-started:
if some_condition:
run_again("python my_script.py %s" % my_new_arguments)
For the original question about the cheese script:
if var != 'yes':
run_again("python my_cheese_script.py")

You can just use a shell script like test.sh and make sure in your linux terminal you chmod +x test.sh
As for the code:
#!/bin/bash
while :
do
sleep 5
gnome-terminal --wait -- sh -c "python3 myscript.py 'myarg1'"
done

You can wrap something in while True: to make it execute repeatedly, as True will always evaluate to True, like this:
while True:
var = input("Hi! I like cheese! Do you like cheese?").lower() # <-- You had missed parentheses here
if var == "yes":
print("That's awesome!")
There's another issue with your code though; you haven't called lower by putting parentheses after it.

Related

How to restart a Python script?

In a program I am writing in python I need to completely restart the program if a variable becomes true, looking for a while I found this command:
while True:
if reboot == True:
os.execv(sys.argv[0], sys.argv)
When executed it returns the error [Errno 8] Exec format error. I searched for further documentation on os.execv, but didn't find anything relevant, so my question is if anyone knows what I did wrong or knows a better way to restart a script (by restarting I mean completely re-running the script, as if it were been opened for the first time, so with all unassigned variables and no thread running).
There are multiple ways to achieve the same thing. Start by modifying the program to exit whenever the flag turns True. Then there are various options, each one with its advantages and disadvantages.
Wrap it using a bash script.
The script should handle exits and restart your program. A really basic version could be:
#!/bin/bash
while :
do
python program.py
sleep 1
done
Start the program as a sub-process of another program.
Start by wrapping your program's code to a function. Then your __main__ could look like this:
def program():
### Here is the code of your program
...
while True:
from multiprocessing import Process
process = Process(target=program)
process.start()
process.join()
print("Restarting...")
This code is relatively basic, and it requires error handling to be implemented.
Use a process manager
There are a lot of tools available that can monitor the process, run multiple processes in parallel and automatically restart stopped processes. It's worth having a look at PM2 or similar.
IMHO the third option (process manager) looks like the safest approach. The other approaches will have edge cases and require implementation from your side to handle edge cases.
This has worked for me. Please add the shebang at the top of your code and os.execv() as shown below
#!/usr/bin/env python3
import os
import sys
if __name__ == '__main__':
while True:
reboot = input('Enter:')
if reboot == '1':
sys.stdout.flush()
os.execv(sys.executable, [sys.executable, __file__] + [sys.argv[0]])
else:
print('OLD')
I got the same "Exec Format Error", and I believe it is basically the same error you get when you simply type a python script name at the command prompt and expect it to execute. On linux it won't work because a path is required, and the execv method is basically encountering the same error.
You could add the pathname of your python compiler, and that error goes away, except that the name of your script then becomes a parameter and must be added to the argv list. To avoid that, make your script independently executable by adding "#!/usr/bin/python3" to the top of the script AND chmod 755.
This works for me:
#!/usr/bin/python3
# this script is called foo.py
import os
import sys
import time
if (len(sys.argv) >= 2):
Arg1 = int(sys.argv[1])
else:
sys.argv.append(None)
Arg1 = 1
print(f"Arg1: {Arg1}")
sys.argv[1] = str(Arg1 + 1)
time.sleep(3)
os.execv("./foo.py", sys.argv)
Output:
Arg1: 1
Arg1: 2
Arg1: 3
.
.
.

Python: Check if Program is being closed

all.
Is there a way, using Python, to check if the script that is currently running is requested to close? For example, If I press the X-Button (close program button) on the top-right to close it, or end the script in any other way, can the script do some code before it ends? Example:
# script goes here...
if Script_To_Be_Closed: # replace this with an actual line of code.
do_stuff
There are multiple options you may use, like trapping keyboardinterrupts, but the simplest is atexit, which executes a function whenever a scripts is ended (except of a hard process kill indeed).
import atexit
def my_exit_function(some_argument):
// Your exit code goes here
print(some_argument)
if __name__ == '__main__':
atexit.register(my_exit_function, 'some argument', )
// Your script goes here
You can use a shell script to do the job
You can see the script command shown below which calls itself after executing the command to run the python file. once the python file is closed the next line will force the python command to run again. you can also customise the behaviour the way you want.
main.py
#!/bin/bash
python3 ./main.py
source ./infiniteRun.sh
If you need to stop the job just edit the file and remove the last line source ./infiniteRun.sh and save the file.

How can I open a terminal, execute a python script on it and then wait for the script to end?

Essentially, what I need to do is create a function that opens a new terminal, executes a python script on it and afterwards, waits for the script to end.
From what I've been reading online, the best way to do this is to use the Python library subprocess. However, I've been finding it very difficult to use.
When I run the following code:
def function():
cmd = "gnome-terminal; python3 simple_action_client.py"
subprocess.check_output(cmd, shell=True)
print("I'm done!")
The print is executed after the terminal opens, meaning that the "check_output" function only waits for the first part of the cmd to be executed.
Essentially, what I would like to do is the following:
def function():
terminal_command = "gnome-terminal"
script_command = "python3 script.py"
subprocess.run(terminal_command, shell = True)
subprocess.check_output(script_command, shell = True)
print("I'm done!")
But when I do something like this, the script doesn't run on the new terminal and I want it to run there.
Is this possible to do?
Thank you for the help!
You are chaining commands to run in the local shell using
def function():
cmd = "gnome-terminal; python3 simple_action_client.py"
subprocess.check_output(cmd, shell=True)
print("I'm done!")
You need to adjust the cmd line to tell gnome-terminal to execute the command
def function():
cmd = "gnome-terminal --wait -- python3 simple_action_client.py"
subprocess.check_output(cmd, shell=True)
print("I'm done!")
Notice the "--" instead of the ";" and the "--wait" to wait for the command inside the shell to exit

Python terminal emulator: Cannot use "cd" command.

I found this code snippet online for a python terminal emulator, I thought it looked cool so I went ahead and attempted to use it. I noticed that I'm not able to use the "cd" command, I'm stuck in the directory I ran the file in. Why is this? What's going on? And how can I modify this code to make it run like a perfect native terminal? I'm still very new to programming and have only played with the subprocess module once in my life. Please help!
import subprocess
import re
while True:
# prevents lots of python error output
try:
s = raw_input('> ')
except:
break
# check if you should exit
if s.strip().lower() == 'exit':
break
# try to run command
try:
cmd = subprocess.Popen(re.split(r'\s+', s), stdout=subprocess.PIPE)
cmd_out = cmd.stdout.read()
# Process output
print cmd_out
except OSError:
print 'Invalid command'
When you open a new process you change the current directory for the new process and not for the calling process. You should use os.chdir instead to change the directory of your program. So you need to parse the command line and check if the command is cd and then decide not to call Popen but os.chdir instead.

How to make a Python program handle a here document?

I've written a Python wrapper (pyprog) to run a program (someprogram), something like this:
...do some setup stuff in Python...
print("run [y=yes]")
CHOICE=input()
...do some setup stuff in Python...
if CHOICE == "y":
status=subprocess.call(["someprogram"])
sys.exit(status)
A user wants to use a shell script to run the program and feed it input using a here document like this:
#!/bin/sh
pyprog > pyprog.log << EOF
y
file1
file2
EOF
Is there a way to spawn the subprocess so that the here document will work (the "y" gets consumed by the Python input(), and the "file1" and "file2" continue along as stdin to someprogram)? Right now, the Python input() takes the "y", but the rest of it disappears.
You need to connect sys.stdin to the stdin of the call.
status=subprocess.call(["someprogram"], stdin=sys.stdin)
import sys
status=subprocess.call(["someprogram"], stdin=sys.stdin)
I've used something like this a few times before: https://gist.github.com/887225
Basically it's a python script that accepts a number of command line parameters, performs some transformation based on what was input, then uses os.system() to evoke a shell command.
In this example I'm calling Java, passing in a class path then running the ProgramName.jar program.

Categories