Подтвердить что ты не робот

Как поймать выход исключения из Python subprocess.check_output()?

Я пытаюсь выполнить биткойновский платеж из Python. В bash я обычно делал бы это:

bitcoin sendtoaddress <bitcoin address> <amount>

так, например:

bitcoin sendtoaddress 1HoCUcbK9RbVnuaGQwiyaJGGAG6xrTPC9y 1.4214

Если он успешный, я получаю идентификатор транзакции как вывод, но если я попытаюсь передать сумму, большую, чем мой биткойновый баланс, я получаю следующий вывод:

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

Он не включает ошибку, которую я получаю в командной строке. Поэтому мой вопрос: как я могу получить выводимую ошибку ({"code":-4,"message":"Insufficient funds"}) из Python?

Все советы приветствуются!

4b9b3361

Ответ 1

В соответствии с 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']

Ответ 2

Я не думаю, что принятое решение обрабатывает случай, когда текст ошибки сообщается на stderr. Из моего теста атрибут вывода исключений не содержал результатов от stderr и предупреждений docs против использования stderr = PIPE в check_output(). Вместо этого я бы предложил небольшое улучшение для решения J.F Sebastian, добавив поддержку stderr. Мы, в конце концов, пытаемся обрабатывать ошибки, а 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))

Ответ 3

Попытка "переносить сумму, большую, чем мой биткойновый баланс", не является неожиданной ошибкой. Вместо check_output() можно использовать 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))

Ответ 4

Здесь есть хорошие ответы, но в этих ответах не было ответа, который выводит текст из вывода трассировки стека, который является поведением исключений по умолчанию.

Если вы хотите использовать эту отформатированную информацию о трассировке, вы можете:

import traceback

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

Как вы могли бы сказать, это полезно, если у вас есть пароль в check_call (args), который вы хотите предотвратить.