69

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.

deinonychusaur
  • 7,094
  • 3
  • 30
  • 44
  • What might be possible is to have the new process running in parallel to the old one at first. Theoretically you could give the new process the process id of the old one (via an additional command line option) and kill the old one as soon as the new one is up and running. – Jakob S. Jul 04 '12 at 13:25
  • 1
    possible duplicate of [Restarting a self-updating python script](http://stackoverflow.com/questions/1750757/restarting-a-self-updating-python-script) – Mark Lakata Jan 27 '14 at 19:18

18 Answers18

77

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)
maxymoo
  • 35,286
  • 11
  • 92
  • 119
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • wow! i did not know this is possible. thanks for pointing that out! – Jakob S. Jul 04 '12 at 13:28
  • 2
    This gives me an invalid syntax error with python 2.7.9. – clankill3r Mar 08 '17 at 19:57
  • 2
    @clankill3r The asterisk in the command is a wildcard that should not be taken literally. It's because there are a myriad of commands that start with "exec" and you have to pick the right one for your use case. See the link. But some examples in this comment would be helpful indeed. – Tails Feb 14 '20 at 09:39
  • 2
    Exception `PermissionError: [Errno 13] Permission denied` occurs, any solution for this? – oeter Jan 15 '21 at 11:46
  • Edit: I need to add the execution permission for the script to make it work: `sudo chmod +x test.py` – oeter Jan 15 '21 at 14:03
  • 24
    Awesome alternative is to use: `os.execv(sys.executable, ['python'] + sys.argv)`. This way you do not need to use `chmod` (to not see Permission denied error) and you do not need to add shebang at the start of the script (to not see `OSError: [Errno 8] Exec format error`). – Andreas Gajdosik Feb 23 '21 at 22:26
  • 1
    Does this method properly shutdown the caller's python runtime? Does it need to? – Osman-pasha Jul 14 '22 at 03:35
  • @AndreasGajdosik does this also shut down the parent script? – Matej Novosad Nov 27 '22 at 18:49
  • How should this be implemented when the script was executed as a module? E.g. `python -m path.to.module`. – lusk Aug 20 '23 at 16:41
44

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)
s3ni0r
  • 451
  • 5
  • 4
8

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()
Basic Block
  • 729
  • 9
  • 17
5

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.

dda
  • 6,030
  • 2
  • 25
  • 34
Kamornik Cola
  • 644
  • 8
  • 20
  • Thanks! Worked for me too. Except, I had to change python to python3 or else the modules didnt import on the 2nd restart. – Jeremy Jul 09 '21 at 21:22
4

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

3

Works at Windows (Without args)

os.startfile(__file__)
sys.exit()

OR

os.startfile(sys.argv[0])
sys.exit()
Ville
  • 119
  • 7
2

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)
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
1

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]))
Dr ALOUI
  • 331
  • 4
  • 8
1

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()
mdkb
  • 372
  • 1
  • 14
0

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","")
0

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.

0

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.

Peter Badida
  • 11,310
  • 10
  • 44
  • 90
0
os.execv(sys.executable, ['python3'] + sys.argv)

or

os.execv(sys.executable, ['python2'] + sys.argv)
Íhor Mé
  • 896
  • 9
  • 13
0

I do it as follows:

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

works like a charm

dragonfly
  • 53
  • 3
0

you can open with webbrowser.open then exit the script

import sys
import webbrowser as wb

wb.open(sys.argv()[0])
exit() # or quit()
0

Without unexpected errors and extra unknowns:

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

I also needed to rerun a script sometimes ago. I did that by embedding another function into it. It calls the new function which in turn calls it when need arises. Obviously not the best practice :) But it did work!

O-T-S
  • 1
  • 2
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Aug 02 '23 at 18:25
-1

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.
Valerij
  • 1
  • 1