Как поймать выход исключения из 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?
Все советы приветствуются!
Ответы
Ответ 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), который вы хотите предотвратить.