3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Pythonでエラーが出たときに再起動させる

Posted at

はじめに

プログラムを組むにあたり、バグを出さないよう論理的にコーディングするのは当然のことだ。
とはいえ、バグがないことを断言することはできない。人間だもの。

いろいろなエラーを起こすプログラム

こんなプログラムを書いてみた。
実行するたびにさまざまなステップでエラーで停止したり最後まで完走したりするという内容だ。

make_error.py
import random
import math
import sys

print ("="*20)
"""
後でここに追記する
"""
p = random.randint(-3, 3)
print (f"{p}です")

if p == 3:
    print (p + "の倍数だとアホになります")  
elif  p == -3:
    print (p , "だと中断します")  
    exit()
elif  p == -2:
    print (p , "だとKeyboardInterruptが発生します")  
    raise KeyboardInterrupt

print ("平方根:")
print (f" sqrt({p}) = {math.sqrt(p)}")
print ("逆数:")
print (f" 1/{p} = {1/p}")

subprocess

プログラムから他プログラムを呼び出し、またその結果(標準出力)を拾うことができる。それがsubprocessだ。MS-DOSの時代を体験していない私には標準出力やパイプは少々難解だった、がんばって勉強した。
以下の記事がたいへん役に立った。

上のmake_error.pyに以下のコードを追記しよう。追記するとこれ単品では動かなくなってしまう。それを避けるのは容易だが本題から外れるので省略している。で、できないわけじゃないからね。勘違いしないでよね。

make_error.pyに追加
args = sys.argv
e = sys.argv[1]
if e != "":
    print (f"前回のエラー:{e}")
else:
    print ("前回は正常に終了しました\n")

そして実行するのはmake_error.pyではなく次のsubprocess_watch.pyだ。

subprocess_watch.py
import time
import subprocess as sp

def run_subprocess(e):
    proc = sp.Popen(["python", "make_error.py", e], stdout = sp.PIPE, stderr = sp.PIPE)
    return proc
    
proc = run_subprocess("")
while True:
    ecode = proc.poll()
    if ecode is None:  # サブプロセスが実行中
        time.sleep(2)
    else:              # サブプロセス終了
        for line in proc.stdout.readlines():
            print (line.decode(encoding="cp932"), end="")  # utf-8だと全角文字でエラーになる(Windows)

        if ecode == 0: # 正常終了
            e = ""
        else:          # 異常終了
            e = proc.stderr.readlines()[-1].decode()
        proc = run_subprocess(e)

これによりmake_error.pyがエラーで落ちても再起動し、かつ前回のエラーメッセージを拾うことができる。
もちろん大元のsubprocess_watch.pyが落ちてしまってはダメだけど。

結果(例)
====================
前回は正常に終了しました

2です
平方根:
 sqrt(2) = 1.4142135623730951
逆数:
 1/2 = 0.5
====================
前回は正常に終了しました

-1です
平方根:
====================
前回のエラー:ValueError: math domain error

-3です
-3 だと中断します
====================
前回は正常に終了しました

-2です
-2 だとKeyboardInterruptが発生します
====================
前回のエラー:KeyboardInterrupt

1です
平方根:
 sqrt(1) = 1.0
逆数:
 1/1 = 1.0

まともな使い方

以上のことを家族見守りサービスを自作するの開発中に習得した。
それにより不慮のエラーが発生しても再起動することができるようになった。
subprpcess.png
どのようなエラーが発生したかわかるようになったのならば分析して修正しろって? ごもっともです。

終わりに

今回はPythonプログラムの再起動に使っただけだが、用途はそれだけに限らない。Pythonでできないことを外に出し、その結果をもらってPythonで処理するという利用法もある。

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?