web-dev-qa-db-ja.com

Python subprocess.check_output()からの例外出力をキャッチする方法は?

Python内からBitcoinで支払いをしようとしています。 bashでは、通常これを行います。

bitcoin sendtoaddress <bitcoin address> <amount>

例えば:

bitcoin sendtoaddress 1HoCUcbK9RbVnuaGQwiyaJGGAG6xrTPC9y 1.4214

成功した場合、出力としてトランザクションIDを取得しますが、ビットコインの残高よりも大きい金額を転送しようとすると、次の出力が表示されます。

error: {"code":-4,"message":"Insufficient funds"}

私のPythonプログラムでは、次のように支払いをしようとしています。

import subprocess

try:
    output = subprocess.check_output(['bitcoin', 'sendtoaddress', address, str(amount)])
except:
    print "Unexpected error:", sys.exc_info()

十分なバランスがある場合は正常に機能しますが、バランスが十分でない場合はsys.exc_info()がこれを出力します:

(<class 'subprocess.CalledProcessError'>, CalledProcessError(), <traceback object at 0x7f339599ac68>)

ただし、コマンドラインで表示されるエラーは含まれていません。だから私の質問は; Python内から出力されたエラー({"code":-4,"message":"Insufficient funds"})を取得するにはどうすればよいですか?

すべてのヒントを歓迎します!

46
kramer65

subprocess.check_output() docs によると、エラー時に発生した例外には、エラーの詳細にアクセスするために使用できるoutput属性があります。

try:
    subprocess.check_output(...)
except subprocess.CalledProcessError as e:
    print e.output

その後、この文字列を分析し、jsonモジュールでエラーの詳細を解析できるはずです。

if e.output.startswith('error: {'):
    error = json.loads(e.output[7:]) # Skip "error: "
    print error['code']
    print error['message']
78
Ferdinand Beyer

エラーテキストがstderrで報告される場合は、受け入れられたソリューションで処理できるとは思わない。私のテストから、例外の出力属性にはstderrからの結果が含まれておらず、ドキュメントはcheck_output()でstderr = PIPEを使用することに対して警告しています。代わりに、stderrサポートを追加することにより、J.F Sebastianのソリューションを少し改善することをお勧めします。結局のところ、私たちはエラーを処理しようとしていますが、stderrはそれらが頻繁に報告される場所です。

from subprocess import Popen, PIPE

p = Popen(['bitcoin', 'sendtoaddress', ..], stdout=PIPE, stderr=PIPE)
output, error = p.communicate()
if p.returncode != 0: 
   print("bitcoin failed %d %s %s" % (p.returncode, output, error))
24
Phil R

「ビットコインの残高よりも大きな額を送金する」ことは、予期しないエラーではありません。 Popen.communicate()の代わりにcheck_output()を直接使用して、不必要に例外を発生させないようにすることができます。

from subprocess import Popen, PIPE

p = Popen(['bitcoin', 'sendtoaddress', ..], stdout=PIPE)
output = p.communicate()[0]
if p.returncode != 0: 
   print("bitcoin failed %d %s" % (p.returncode, output))
7
jfs

ここには良い答えがありますが、これらの答えには、スタックトレース出力からのテキストを思い付く答えがありません。これは例外のデフォルトの動作です。

そのフォーマットされたトレースバック情報を使用したい場合は、次のことができます。

import traceback

try:
    check_call( args )
except CalledProcessError:
    tb = traceback.format_exc()
    tb = tb.replace(passwd, "******")
    print(tb)
    exit(1)

わかるかもしれませんが、上記の方法は、check_call(args)に表示したくないパスワードがある場合に役立ちます。

2
macetw