58

複数のモジュールをロードする Python ベースの GTK アプリケーションがあります。次のように(Linux)ターミナルから実行されます。

./myscript.py --some-flag setting

プログラム内から、ユーザーは (Git を使用して) 新しいバージョンをダウンロードできます。そのようなものが存在する/ダウンロードされている場合、新しくコンパイルされたコンテンツ(依存関係/インポートを含む)でプログラムを再起動するボタンが表示されます。できれば、sys.argvすべてのフラグをそのまま保持するために、の内容を使用して再起動することもできます。

したがって、私が見つけられなかった/必要としているのは、プログラムの現在のインスタンスを強制終了し、同じ引数を使用して新しいインスタンスを開始する素敵な再起動手順です。

できればこのソリューションは Windows と Mac でも機能するはずですが、必須ではありません。

4

14 に答える 14

67

os.exec*()コマンドファミリーを探しています。

最初に実行されたときとまったく同じコマンドライン引数で現在のプログラムを再起動するには、次を使用できます。

os.execv(sys.argv[0], sys.argv)
于 2012-07-04T13:22:16.723 に答える
39

これはより精巧な答えだと思います.開いているファイルオブジェクトと記述子が多すぎて、メモリの問題やネットワークデバイスへの同時接続が発生する可能性があるためです。

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)
于 2015-10-25T19:37:03.293 に答える
5

更新 - 将来の参照のためのいくつかの例を含む上記の回答の

私は持っているrunme.sh

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

そしてserver.py、アプリケーション自体を再起動する必要がある場所があるので、次のようにしました。

os.system('runme.sh')

しかし、それはrunme.shに従ってアプリケーション自体を再起動しなかったので、この方法を使用したとき:

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

その後、私は自分自身を再起動することができました

于 2013-09-22T15:43:29.600 に答える
1

私はこれに対する解決策を探していましたが、スタック オーバーフローの投稿で機能するものは何も見つかりませんでした。os.system が subprocess に置き換えられたなど、一部の回答が古すぎる可能性があります。Python 3を使用して、Linux lubuntu 17.10を使用しています。

2つの方法がうまくいきました。どちらも新しいシェル ウィンドウを開き、その中で main.py スクリプトを実行してから、古いシェル ウィンドウを閉じます。

1. .sh スクリプトで main.py を使用します。

@YumYumYum メソッドから適応。kill オプションは必要ありませんでした (ただし、python プロセスを名前で強制終了することはなくkillall python3、テスト時にそれを達成するために使用する必要がありました)。

私は lxterminal を使用していますが、おそらくどれでも使用できます。

restart.sh というファイル (実行可能にするための chmod +x)

#!/bin/bash

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

次に、main.py で呼び出す必要があるときにこれを使用します。

import subprocess

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

2. main.py 内から

@Stuffe メソッドから適応。これは main.py スクリプトの内部にあり、新しいシェル ウィンドウを開き、新しいスクリプトを実行してから、古いスクリプトを終了します。time.sleep 遅延が必要かどうかはわかりませんが、とにかく使用しました。

import subprocess, time

cmd = 'python3 /home/my/folder/main.py'
subprocess.run('lxterminal -e ' + cmd, shell=True)   
time.sleep(0.2)
quit()
于 2019-10-23T08:55:56.200 に答える
0

古い回答execでは、これは問題ありませんが、長期的にはスケーラブルではありません。変更を監視するバックグラウンドでマスター/スレーブプロセス関係またはデーモン/サービスのアプローチもありますが、ほとんどはOS固有であるか、同じOSファミリー間で異なります(init.d対systemd対何でも)。

ブートストラップ手法と単純な呼び出しを使用することによる中間点もあるsubprocess.Popen()ため、元のプログラムを開始したユーザーが実行可能ファイル ( など) を実行する権限を持っていると仮定して/usr/bin/pythonも、まったく同じ実行可能ファイルを使用するため、権限エラーなしで動作するはずです。ブートストラップは、最初の起動後に独自のブートストラップによって自分自身をプルする、つまりリスタータを作成して呼び出すメイン プログラムであるためです。

したがって、他の回答に書かれているように、単純なプログラム(再)スターターは次のように書くことができます:

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()

必要に応じて、(再)スターター ファイルを削除するなど、後でクリーンアップを実行する必要がある場合があります。

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()

このファイルは、メイン プログラムから呼び出されます。ただし、メインプログラムには例外があり、を利用sys.exit()したり、OS シグナルによって強制終了されたりする可能性があります。Python は、そのようなイベントの後に何かをシームレスに行うための複数のフックを提供します。そのうちの 1 つはモジュールによって実装されatexitます。atexitは、Python の例外や一部のシグナル ( SIGINT) (さらなる改善のためのチェックsignalモジュール) を気にしないため、独自のシグナル ハンドラを実装する前に妥当な選択です。

これにより、プログラムが停止したときに実行される関数を登録できます。その関数は Python では何でもかまいませんので、ファイルを書き込むこともできます。

ファイルの内容自体は、F 文字列、format()Jinja を使用してテンプレート化することも、メイン プログラム ( restartable.py) から除外することもできます。また、値argparsepython restarter.py --exe <path> --argv <one> [--argv <two>, ...], --cwd <cwd>. ユースケースと、OS サービス/デーモンまたはマスター/スレーブ プロセスのスポーンと監視を実装する前に、それをどの程度スケーリングするかによって異なります。

以下にサンプルを示します。

# 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()

注: 一時フォルダーを使用すると失敗する可能性があるため、その場合は に切り替えてjoin(dirname(realpath(__file__)), "restarter.py")、メイン プログラムから呼び出すだけです。

于 2021-09-24T18:56:41.033 に答える