2014-07-20 1 views
26

Sto provando a eseguire un pagamento Bitcoin da Python. In bash Io ​​normalmente faccio:Come catturare l'output delle eccezioni da Python subprocess.check_output()?

bitcoin sendtoaddress <bitcoin address> <amount> 

così per esempio:

bitcoin sendtoaddress 1HoCUcbK9RbVnuaGQwiyaJGGAG6xrTPC9y 1.4214 

se è successo ottengo un ID di transazione come output, ma se provo a trasferire un importo più grande del mio equilibrio bitcoin , ottengo il seguente output:

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

Nel mio programma Python io ora cerco di fare il pagamento come segue:

import subprocess 

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

Se c'è abbastanza equilibrio che funziona bene, ma se non c'è abbastanza equilibrio sys.exc_info() stampe fuori questo:

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

Non include l'errore che ottengo sulla riga di comando però. Quindi la mia domanda è; come posso ottenere l'errore di output ({"code":-4,"message":"Insufficient funds"}) da Python?

Tutti i suggerimenti sono benvenuti!

+1

Ti interessa accettare una delle risposte? – maxschlepzig

risposta

41

Secondo il subprocess.check_output() docs, l'eccezione sollevata in caso di errore ha un attributo output che è possibile utilizzare per accedere ai dettagli dell'errore:

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

Si dovrebbe quindi essere in grado di analizzare questa stringa e analizzare i dettagli di errore con il modulo json:

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

Sto chiamando un programma che restituisce qualcosa allo stdout e restituisce 1, ma check_output non lo sta catturando – JorgeeFG

+0

@JorgeeFG Quindi immagino ci sia qualcosa di sbagliato nel tuo programma. Si prega di notare che la sezione commenti non è il posto giusto per fare nuove domande. Se hai bisogno di aiuto per il tuo particolare problema, fai clic sul grande pulsante "Chiedi domanda" in alto a destra della pagina. –

+0

Nota, questo non funzionerà con Python 2.6 o sotto! – whirlwin

5

Cercando di "trasferire un importo più grande di mio equilibrio bitcoin" non è un errore imprevisto. Si potrebbe utilizzare Popen.communicate() direttamente invece di check_output() per evitare di sollevare un'eccezione inutilmente:

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

Python incoraggia uno stile di programmazione EAFP (è più facile chiedere perdono che il permesso), preferendo la gestione delle eccezioni rispetto ai controlli "se" in situazioni come queste. –

+5

@FerdinandBeyer: EAFP non si applica in questo caso: non si effettuano chiamate che non si effettuerebbero diversamente. Il codice non ha la struttura LBYL: 'se check(): do()' che avresti potuto sostituire con EAFP 'try: do() tranne Error: handle_error()'. Il codice nella risposta inline 'check_output()' funzione ed evita di generare un'eccezione nel ramo 'if p.returncode' solo per catturarlo allo stesso livello. Evita la programmazione settoriale del carico, pensa – jfs

14

non credo che la soluzione accettata gestisce il caso in cui il testo di errore viene segnalato su stderr. Dal mio test l'attributo di output dell'eccezione non conteneva i risultati di stderr e il doc warn rispetto all'uso di stderr = PIPE in check_output(). Invece, suggerirei un piccolo miglioramento alla soluzione di J.F Sebastian aggiungendo il supporto per gli stderr. Dopotutto, stiamo cercando di gestire gli errori e stderr è dove vengono spesso segnalati.

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

Concordo sul fatto che l'output di 'stderr' sia molto rilevante qui. Una soluzione alternativa sarebbe utilizzare invece la funzione 'run()' (vedere i [documenti check_output] (https://docs.python.org/3/library/subprocess.html#subprocess.check_output) come sostituire). Perché in questo caso è possibile utilizzare 'e.stderr' dall'eccezione nella segnalazione degli errori. – Sebastian

+0

Questo dovrebbe essere nella parte superiore. – Oleg

2

ci sono buone risposte qui, ma in queste risposte, non c'è stata una risposta che si presenta con il testo dall'uscita stack trace, che è il comportamento predefinito di un'eccezione.

Se si desidera utilizzare tali informazioni traceback formattato, si potrebbe voler:

import traceback 

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

Come si può essere in grado di dire, quanto sopra è utile nel caso in cui si dispone di una password nel check_call (args) che si desidera impedire la visualizzazione.