Pythonで次のことを行う必要があります。プロセス(サブプロセスモジュール?)を生成したいのですが、次のようになります。
これを達成するための最もエレガントな方法は何ですか?
subprocess
モジュールがあなたの友達になります。プロセスを開始してPopen
オブジェクトを取得し、それを次のような関数に渡します。これはタイムアウト時にのみ例外を発生させることに注意してください。必要に応じて、例外をキャッチし、Popen
プロセスでkill()
メソッドを呼び出すことができます。 (killはPython 2.6、btw)の新機能です)
import time
def wait_timeout(proc, seconds):
"""Wait for a process to finish, or raise exception after timeout"""
start = time.time()
end = start + seconds
interval = min(seconds / 1000.0, .25)
while True:
result = proc.poll()
if result is not None:
return result
if time.time() >= end:
raise RuntimeError("Process timed out")
time.sleep(interval)
プロセスPIDを知っている限り、 psutil を使用してこれを行うには少なくとも2つの方法があります。プロセスがそのように作成されていると仮定します。
import subprocess
subp = subprocess.Popen(['progname'])
...次のようなビジーループで作成時間を取得できます。
import psutil, time
TIMEOUT = 60 * 60 # 1 hour
p = psutil.Process(subp.pid)
while 1:
if (time.time() - p.create_time()) > TIMEOUT:
p.kill()
raise RuntimeError('timeout')
time.sleep(5)
...または単に、これを行うことができます:
import psutil
p = psutil.Process(subp.pid)
try
p.wait(timeout=60*60)
except psutil.TimeoutExpired:
p.kill()
raise
また、あなたがそれに取り組んでいる間、あなたは以下の追加のAPIに興味があるかもしれません:
>>> p.status()
'running'
>>> p.is_running()
True
>>>
私は同様の質問をし、この答えを見つけました。完全を期すために、指定された時間が経過した後にハングしているプロセスを終了する方法をもう1つ追加したいと思います。pythonシグナルライブラリ https://docs.python.org /2/library/signal.html
ドキュメントから:
import signal, os
def handler(signum, frame):
print 'Signal handler called with signal', signum
raise IOError("Couldn't open device!")
# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)
# This open() may hang indefinitely
fd = os.open('/dev/ttyS0', os.O_RDWR)
signal.alarm(0) # Disable the alarm
とにかく新しいプロセスを生成したかったので、これは問題の最善の解決策ではないかもしれません。
優れたパッシブな方法は、threading.Timerを使用してコールバック関数を設定することでもあります。
from threading import Timer
# execute the command
p = subprocess.Popen(command)
# save the proc object - either if you make this onto class (like the example), or 'p' can be global
self.p == p
# config and init timer
# kill_proc is a callback function which can also be added onto class or simply a global
t = Timer(seconds, self.kill_proc)
# start timer
t.start()
# wait for the test process to return
rcode = p.wait()
t.cancel()
プロセスが時間内に終了し、wait()が終了してコードがここで続行される場合、cancel()はタイマーを停止します。その間にタイマーが切れて別のスレッドでkill_procが実行された場合、wait()もここで続行され、cancel()は何もしません。 rcodeの値によって、タイムアウトしたかどうかがわかります。最も単純なkill_proc :(もちろんそこで余分なことは何でもできます)
def kill_proc(self):
os.kill(self.p, signal.SIGTERM)
subprocess
モジュールについての彼の素晴らしい提案に対してKoodosからPeterShinnersへ。以前はexec()
を使用していましたが、実行時間、特に終了を制御できませんでした。この種のタスクの最も単純なテンプレートは次のとおりです。実行時間を監視するためにsubprocess.run()
関数のタイムアウトパラメーターを使用しています。もちろん、必要に応じて標準出力とエラーを取得することもできます。
from subprocess import run, TimeoutExpired, CalledProcessError
for file in fls:
try:
run(["python3.7", file], check=True, timeout=7200) # 2 hours timeout
print("scraped :)", file)
except TimeoutExpired:
message = "Timeout :( !!!"
print(message, file)
f.write("{message} {file}\n".format(file=file, message=message))
except CalledProcessError:
message = "SOMETHING HAPPENED :( !!!, CHECK"
print(message, file)
f.write("{message} {file}\n".format(file=file, message=message))