Restart python-script from within itself

Question:

I have a python-based GTK application that loads several modules. It is run from the (linux) terminal like so:

./myscript.py --some-flag setting

From within the program the user can download (using Git) newer versions.
If such exists/are downloaded, a button appear that I wish would restart the program with newly compiled contents (including dependencies/imports). Preferably it would also restart it using the contents of sys.argv to keep all the flags as they were.

So what I fail to find/need is a nice restart procedure that kills the current instance of the program and starts a new using the same arguments.

Preferably the solution should work for Windows and Mac as well but it is not essential.

Asked By: deinonychusaur

||

Answers:

You’re looking for os.exec*() family of commands.

To restart your current program with exact the same command line arguments as it was originally run, you could use the following:

os.execv(sys.argv[0], sys.argv)

UPDATE – of the above answer with some example for future reference

I have runme.sh

#!/bin/bash
kill -9 server.py
python /home/sun/workspace/c/src/server.py &

And i have server.py where i need to restart the application itself, so i had:

os.system('runme.sh')

but that did not restart the application itself by following runme.sh, so when i used this way:

os.execl('runme.sh', '')

Then i was able to restart itself

Answered By: user285594

I think this is a more elaborate answer, as sometimes you may end up with too many open file objects and descriptors, that can cause memory issues or concurrent connections to a network device.

import os
import sys
import psutil
import logging

def restart_program():
    """Restarts the current program, with file objects and descriptors
       cleanup
    """

    try:
        p = psutil.Process(os.getpid())
        for handler in p.get_open_files() + p.connections():
            os.close(handler.fd)
    except Exception, e:
        logging.error(e)

    python = sys.executable
    os.execl(python, python, *sys.argv)
Answered By: s3ni0r

I know this solution isn’t technically what you are asking for but if you want to be 100% sure you freed everything or don’t want to rely on dependencies you could just run the script in from a loop in another:

import os, time

while 1:
    os.system("python main.py")
    print "Restarting..."
    time.sleep(0.2) # 200ms to CTR+C twice

Then you can restart main.py as simply as:

quit()
Answered By: Basic Block

Inspired by @YumYumYum and fixed the problem
Using restart.sh and os.execl

restart.sh

#!/bin/bash/
pkill -f main.py
python main.py

Add this to your main.py

os.excel("restart.sh","")
Answered By: Imagine Breaker

I have just a little amelioration on the code of #s3niOr.

In my case there are spaces in the path of the python file. So by adding a proper formatting, the problem can be solved.

Notice that in my case my python file has no other arguments. So if you do have other arguments, you have to deal with them.

This solves the problem of restarting a python script that has spaces in its path :

import os
import sys
import psutil
import logging

def restart_program():
    """Restarts the current program, with file objects and descriptors
       cleanup
    """

    try:
        p = psutil.Process(os.getpid())
        for handler in p.get_open_files() + p.connections():
            os.close(handler.fd)
    except Exception, e:
        logging.error(e)

    python = sys.executable
    os.execl(python, python, ""{}"".format(sys.argv[0]))
Answered By: Dr ALOUI

Works at Windows
(Without args)

os.startfile(__file__)
sys.exit()

OR

os.startfile(sys.argv[0])
sys.exit()
Answered By: Ville

I’m using this to give an option for the users to restart the script within the console. Hope it could be a help.

def close_restart(self,csvfile):

    choice = input('Do you want to restart the program? Please select 'Y' if you would like to restart.')

    if choice == 'Y' or choice == 'y':
        print('Restarting now...')
        os.execl(sys.executable, sys.executable, *sys.argv)

    else:
        print('Thank you for using the tool!')
        print('The program will close in 10s...')
        time.sleep(10)

So the user can input an option ‘Y/N’ to restart the program or not.

Answered By: Silvis Sora

I was looking for a solution to this and found nothing that works on any of the stack overflow posts. Maybe some of the answers are too out of date, e.g. os.system has been replaced by subprocess. I am on linux lubuntu 17.10, using python 3.

Two methods worked for me. Both open a new shell window and run the main.py script in it and then close the old one.

1. Using main.py with an .sh script.

Adapted from @YumYumYum method. I did not need the kill option, (though it did not kill my python process by name anyway and I had to use killall python3 to achieve it when testing).

I use lxterminal but you could probably use any.

In the file called restart.sh (chmod +x to make it executable)

#!/bin/bash

lxterminal -e python3 /home/my/folder/main.py &

Then in the main.py use this when you need to call it

import subprocess

subprocess.run('/home/my/folder/restart.sh', shell=True)
quit()

2. From within main.py

Adapted from @Stuffe method. This one is internal to the main.py script and opens a new shell window then runs the new script, then quits out of the old script. I am not sure it needs the time.sleep delay but I used it anyway.

import subprocess, time

cmd = 'python3 /home/my/folder/main.py'
subprocess.run('lxterminal -e ' + cmd, shell=True)   
time.sleep(0.2)
quit()
Answered By: mdkb

For me this part worked like a charm:

def restart():
    import sys
    print("argv was",sys.argv)
    print("sys.executable was", sys.executable)
    print("restart now")

    import os
    os.execv(sys.executable, ['python'] + sys.argv)

I got it here.

Answered By: Kamornik Cola

The old answers utilize exec which is fine, but not scalable in the long run. There’s also an approach of master/slave process relationship or a daemon/service in the background which will watch for the changes but is mostly OS specific or even different between the same family of OSs (init.d vs systemd vs whatever).

There’s also a middle ground by using a bootstraping technique and a simple subprocess.Popen() call thus assuming that the user who started the original program had the permissions to run the executable (such as /usr/bin/python) should also work without any permission errors due to utilizing the exactly same executable. Bootstraping because it’s the main program that’s creating and calling the restarter a.k.a. pulling itself by own bootstraps after the initial start.

So a simple program (re)starter can be written like this, as written in the other answers:

from subprocess import Popen
from time import sleep

def main():
    sleep(<delay>)
    Popen([<executable path>, *<list of argv>], cwd=<cwd path>)

if __name__ == "__main__":
    main()

Depending on your needs you might want to do some cleanup afterwards such as removing the (re)starter file.

import sys
from os import remove
from os.path import realpath
from subprocess import Popen
from time import sleep

def start():
    sleep(<delay>)
    # unpack -----------------v
    Popen([<executable path>, *<list of argv>], cwd=<cwd path>)

def cleanup():
    remove(realpath(sys.argv[0]))

def main():
    start()
    cleanup()

if __name__ == "__main__":
    main()

This file would be called from your main program. However, your main program may have an exception, utilize sys.exit(), may be killed by a OS signal and so on. Python provides multiple hooks how to do something after such an event seamlessly, one of which is implemented by the atexit module. atexit doesn’t care about Python exceptions and about some signals either (SIGINT) (for further improvement check signal module), so it’s a reasonable choice before implementing own signal handler(s).

This allows you to register a function that will execute once your program stops. That function can be anything in Python, so it can write a file too.

The file content itself can be templated with F-strings, format(), Jinja, or can be even kept out of the main program (restartable.py) and the values might be even provided via CLI + argparse such as python restarter.py --exe <path> --argv <one> [--argv <two>, ...], --cwd <cwd>. Everything depending on the use-case and how far do you want to scale it before implementing an OS service/daemon or a master/slave process spawning+watching.

Here is a sample:

# restartable.py
import sys
from atexit import register

# getcwd() is necessary if you want to prevent issues
# with implicitly changing working directory by a mistake
from os import getpid, getcwd
from os.path import exists, realpath, join, dirname
from subprocess import Popen
from tempfile import NamedTemporaryFile

RESTARTER = """
import sys
from atexit import register
from os import remove
from os.path import realpath
from subprocess import Popen
from time import sleep

def start():
    # unnecessary, but may provide enough breathing space
    # for the previous process to shut down correctly
    # alternatively, watch for a file/file contents
    # or utilize a socket
    sleep({delay})
    # repr() on strings, list is passed as is to unpack it properly
    # will fail on custom objects / serialization
    Popen([{exe!r}, *{argv}], cwd={cwd!r})

def cleanup():
    # remove itself because it's always created
    # by the main program on exit
    remove(realpath(sys.argv[0]))

def main():
    # the register() call can be encapsulated in a condition
    # so it restarts only in some cases
    register(cleanup)
    start()

if __name__ == "__main__":
    main()
""".lstrip("n")

def restart():
    with NamedTemporaryFile(mode="w", delete=False) as file:
        file.write(RESTARTER.format(
            delay=5,  # 5s until restart
            exe=sys.executable,
            argv=sys.argv,
            cwd=getcwd()
        ))

        # call the restarting program by the Python executable
        # which started the main program
        Popen([sys.executable, file.name])

def main():
    # create a "norestart.txt" in the folder of "restartable.py" to halt
    if not exists(join(dirname(realpath(__file__)), "norestart.txt")):
        register(restart)

    # tail -f log.txt to check it works properly
    # or "ps aux|grep python"
    with open("log.txt", "a") as file:
        file.write(f"Hello, from {getpid()}n")


if __name__ == "__main__":
    main()

Note: It might fail by using the temporary folder, so in that case just switch it to the join(dirname(realpath(__file__)), "restarter.py") and call that from the main program.

Answered By: Peter Badida
os.execv(sys.executable, ['python3'] + sys.argv)

or

os.execv(sys.executable, ['python2'] + sys.argv)
Answered By: Íhor Mé

I do it as follows:

    def restartApp(self):
        python = sys.executable
        os.execl(python, python, *sys.argv)

works like a charm

Answered By: dragonfly

Try this work with Windows:

when you want to restart the script call this function

import os
def rest():
    os.system('cls')
    script_name = os.path.basename(__file__)
    os.system(script_name)
Answered By: user17824785

Instead of this:

from os import execl
from sys import executable, executable, argv
execl(executable, executable, *argv)

which works well, I decided to make my own…

NameProject.py:

system('bash rerun.sh')

rerun.sh:

#!/bin/bash
(killall NameProject.app; cd ..; cd ..; cd ..; open NameProject.app) || (killall python; python NameProject.py)
  1. In the right place, call System from the OC package in my .py project.
  2. Since the py-file and sh are in the same folder, therefore I do not specify the path: I delete application file from the list of included programs, since my program does not have its own application file name, it is always replaced by the Python factory name; then I will start on my new program.
Answered By: Valerij

you can open with webbrowser.open then exit the script

import sys
import webbrowser as wb

wb.open(sys.argv()[0])
exit() # or quit()
Answered By: Frost Dream

Without unexpected errors and extra unknowns:

import os
os.execv(sys.executable, ['python'] + sys.argv)
Answered By: Dmitry Tuchin
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.