我正在尝试从Python中进行比特币支付。在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"}
)?
欢迎所有提示!
答案 0 :(得分:71)
根据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']
答案 1 :(得分:22)
我认为接受的解决方案不会处理在stderr上报告错误文本的情况。从我的测试中,异常的输出属性不包含stderr的结果,并且docs警告不要在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))
答案 2 :(得分:7)
尝试“转移大于比特币余额的金额”并非意外错误。您可以直接使用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))
答案 3 :(得分:3)
这帮了我大忙。它捕获子进程的所有stdout输出(对于python 3.8):
from subprocess import check_output, STDOUT
cmd = "Your Command goes here"
try:
cmd_stdout = check_output(cmd, stderr=STDOUT, shell=True).decode()
except Exception as e:
print(e.output.decode()) # print out the stdout messages up to the exception
print(e) # To print out the exception message
答案 4 :(得分:2)
这里有很好的答案,但在这些答案中,没有一个答案来自堆栈跟踪输出中的文本,这是异常的默认行为。
如果您希望使用该格式化的追溯信息,您可能希望:
import traceback
try:
check_call( args )
except CalledProcessError:
tb = traceback.format_exc()
tb = tb.replace(passwd, "******")
print(tb)
exit(1)
正如您可能知道的那样,如果您希望阻止显示的check_call(args)中有密码,则上述内容非常有用。
答案 5 :(得分:0)
基于@macetw的答案,我将异常直接打印到装饰器中的stderr。
Python 3
from functools import wraps
from sys import stderr
from traceback import format_exc
from typing import Callable, Collection, Any, Mapping
def force_error_output(func: Callable):
@wraps(func)
def forced_error_output(*args: Collection[Any], **kwargs: Mapping[str, Any]):
nonlocal func
try:
func(*args, **kwargs)
except Exception as exception:
stderr.write(format_exc())
stderr.write("\n")
stderr.flush()
raise exception
return forced_error_output
Python 2
from functools import wraps
from sys import stderr
from traceback import format_exc
def force_error_output(func):
@wraps(func)
def forced_error_output(*args, **kwargs):
try:
func(*args, **kwargs)
except Exception as exception:
stderr.write(format_exc())
stderr.write("\n")
stderr.flush()
raise exception
return forced_error_output
然后在您的工作人员中使用装饰器
@force_error_output
def da_worker(arg1: int, arg2: str):
pass