Ich versuche, eine Bitcoin-Zahlung innerhalb von Python durchzuführen. In Bash würde ich normalerweise folgendes tun:
bitcoin sendtoaddress <bitcoin address> <amount>
so zum Beispiel:
bitcoin sendtoaddress 1HoCUcbK9RbVnuaGQwiyaJGGAG6xrTPC9y 1.4214
Wenn dies erfolgreich ist, erhalte ich eine Transaktions-ID als Ausgabe. Wenn ich jedoch versuche, einen Betrag zu überweisen, der größer als mein Bitcoin-Guthaben ist, erhalte ich die folgende Ausgabe:
error: {"code":-4,"message":"Insufficient funds"}
In meinem Python-Programm versuche ich nun, die Zahlung wie folgt durchzuführen:
import subprocess
try:
output = subprocess.check_output(['bitcoin', 'sendtoaddress', address, str(amount)])
except:
print "Unexpected error:", sys.exc_info()
Wenn genügend Gleichgewicht vorhanden ist, funktioniert es einwandfrei, aber wenn nicht genügend Gleichgewicht vorhanden ist, wird sys.exc_info()
Folgendes ausgedruckt:
(<class 'subprocess.CalledProcessError'>, CalledProcessError(), <traceback object at 0x7f339599ac68>)
Der Fehler, den ich in der Befehlszeile erhalte, ist jedoch nicht enthalten. Meine Frage ist also: Wie kann ich den ausgegebenen Fehler ( {"code":-4,"message":"Insufficient funds"}
) aus Python heraus abrufen?
Alle Tipps sind willkommen!
quelle
Ich glaube nicht, dass die akzeptierte Lösung den Fall behandelt, in dem der Fehlertext auf stderr gemeldet wird. Bei meinen Tests enthielt das Ausgabeattribut der Ausnahme nicht die Ergebnisse von stderr, und die Dokumente warnen vor der Verwendung von stderr = PIPE in check_output (). Stattdessen würde ich eine kleine Verbesserung der Lösung von JF Sebastian vorschlagen, indem ich stderr-Unterstützung hinzufüge. Wir versuchen schließlich, mit Fehlern umzugehen, und bei stderr werden sie häufig gemeldet.
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))
quelle
stderr
Ausgabe hier sehr relevant ist. Eine alternative Lösung wäre,run()
stattdessen die Funktion zu verwenden (siehe Ersetzen in den Dokumenten check_output ). Denn dann können Siee.stderr
aus der Ausnahme in Ihrer Fehlerberichterstattung verwenden.stderr=STDOUT
beide Streams zusammenführen..communicate
die.returncode
Ausgabe leer sein wird (None
)if p.returncode != 0:
hat hier nicht funktioniert, weil ichNone
Erfolg habe. Musste verwendenif p.returncode:
Der Versuch, "einen Betrag zu überweisen, der größer als mein Bitcoin-Guthaben ist", ist kein unerwarteter Fehler. Sie können
Popen.communicate()
direkt verwenden, anstattcheck_output()
zu vermeiden, dass eine Ausnahme unnötig ausgelöst wird: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))
quelle
if check(): do()
, die Sie durch EAFP hätten ersetzen könnentry: do() except Error: handle_error()
. Der Code in der Antwort inlinescheck_output()
funktioniert und vermeidet das Auslösen einer Ausnahme in derif p.returncode
Verzweigung, nur um sie auf derselben Ebene abzufangen. Vermeiden Sie Frachtkult-Programmierung, denken Siep = Popen(['bitcoin', 'sendtoaddress', ..], stdout=PIPE, stderr=PIPE)
und die Fehlermeldung wie folgt erfassen :output, error = p.communicate()
.shell=True
Argument hinzu:p = Popen("a | b", shell=True, ..)
Hier gibt es gute Antworten, aber in diesen Antworten gibt es keine Antwort, die den Text aus der Stack-Trace-Ausgabe enthält. Dies ist das Standardverhalten einer Ausnahme.
Wenn Sie diese formatierten Traceback-Informationen verwenden möchten, möchten Sie möglicherweise:
import traceback try: check_call( args ) except CalledProcessError: tb = traceback.format_exc() tb = tb.replace(passwd, "******") print(tb) exit(1)
Wie Sie möglicherweise feststellen können, ist das oben Genannte hilfreich, wenn Sie in check_call (args) ein Kennwort haben, dessen Anzeige Sie verhindern möchten.
quelle
Das hat den Trick für mich getan. Es erfasst alle stdout-Ausgaben des Unterprozesses (für 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
quelle
Wie von @Sebastian erwähnt, sollte die Standardlösung folgende Ziele verfolgen
run()
: https://docs.python.org/3/library/subprocess.html#subprocess.runHier eine praktische Implementierung (Sie können die Protokollklasse jederzeit mit print-Anweisungen oder anderen von Ihnen verwendeten Protokollierungsfunktionen ändern):
import subprocess def _run_command(command): log.debug("Command: {}".format(command)) result = subprocess.run(command, shell=True, capture_output=True) if result.stderr: raise subprocess.CalledProcessError( returncode = result.returncode, cmd = result.args, stderr = result.stderr ) if result.stdout: log.debug("Command Result: {}".format(result.stdout.decode('utf-8'))) return result
Und Beispielverwendung (Code hat nichts damit zu tun, aber ich denke, er dient als Beispiel dafür, wie lesbar und einfach es ist, mit Fehlern bei dieser einfachen Implementierung zu arbeiten):
try: # Unlock PIN Card _run_command( "sudo qmicli --device=/dev/cdc-wdm0 -p --uim-verify-pin=PIN1,{}" .format(pin) ) except subprocess.CalledProcessError as error: if "couldn't verify PIN" in error.stderr.decode("utf-8"): log.error( "SIM card could not be unlocked. " "Either the PIN is wrong or the card is not properly connected. " "Resetting module..." ) _reset_4g_hat() return
quelle
Basierend auf der Antwort von @macetw drucke ich die Ausnahme direkt in einem Dekorateur an 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
Dann benutzen Sie in Ihrem Arbeiter einfach den Dekorateur
@force_error_output def da_worker(arg1: int, arg2: str): pass
quelle