2014-07-20 12 views
26

Próbuję wykonać płatność Bitcoin z poziomu Pythona. W bash I zwykle to zrobić:Jak wychwycić wynik wyjątku z Pythona subprocess.check_output()?

bitcoin sendtoaddress <bitcoin address> <amount> 

tak na przykład:

bitcoin sendtoaddress 1HoCUcbK9RbVnuaGQwiyaJGGAG6xrTPC9y 1.4214 

jeśli jest udana uzyskać identyfikator transakcji jako wyjście, ale gdy próbuję przekazać kwotę większą niż należna bitcoin , pojawia się następujący komunikat:

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

W moim programie Python ja teraz staram się robić płatności w następujący sposób:

import subprocess 

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

Jeśli jest wystarczająco bilans to działa dobrze, ale jeśli nie ma wystarczającej ilości balansu sys.exc_info() wypisuje to:

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

Nie zawierają błąd, który otrzymuję w wierszu poleceń chociaż. Moje pytanie brzmi; jak mogę uzyskać błąd wyjściowy ({"code":-4,"message":"Insufficient funds"}) z poziomu Pythona?

Wszystkie wskazówki są mile widziane!

+1

Czy chcesz zaakceptować jedną z odpowiedzi? – maxschlepzig

Odpowiedz

41

Według subprocess.check_output() docs wyjątek podniesiony w przypadku błędu ma atrybut output, którego można użyć, aby przejść do szczegółów o błędach:

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

Następnie powinno być w stanie przeanalizować ten ciąg i przeanalizować szczegóły Błąd moduł json:

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

Wywołuję program, który wyprowadza coś na standardowe wyjście, a następnie zwraca 1, ale check_output go nie przechwytuje. – JorgeeFG

+0

@JorgeeFG Następnie myślę, że coś jest nie tak z twoim programem. Proszę zauważyć, że sekcja komentarzy nie jest właściwym miejscem do zadawania nowych pytań. Jeśli potrzebujesz pomocy w konkretnym problemie, kliknij duży przycisk "Zadaj pytanie" w prawym górnym rogu strony. –

+0

Uwaga, to nie będzie działać z Pythonem w wersji 2.6 lub niższej! – whirlwin

5

Starając się „przenieść kwotę większą niż należna bitcoin” nie jest nieoczekiwany błąd. Można użyć Popen.communicate() bezpośrednio zamiast check_output() aby uniknąć niepotrzebnego podnoszenia wyjątek:

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 zachęca do stylu programowania EAFP (łatwiejsze jest prosić o wybaczenie niż pozwolenie), preferując obsługę wyjątków przez kontrole "jeśli" w takich sytuacjach. –

+5

@FerdinandBeyer: EAFP nie ma zastosowania w tym przypadku: nie wykonujesz żadnych połączeń, których nie zrobiłbyś inaczej. Kod nie ma struktury LBYL: 'if check(): do()' które mogłeś zastąpić EAFP 'try: do() z wyjątkiem Error: handle_error()'. Kod w odpowiedzi wprowadza funkcję 'check_output()' i unika zgłaszania wyjątku w gałęzi 'if p.returncode' tylko po to, aby złapać go na tym samym poziomie. Unikaj programowania kultowego, myśl – jfs

14

Nie sądzę przyjętego rozwiązania obsługuje przypadek, w którym tekst jest błąd na stderr. Z mojego testowania atrybut wyjściowy wyjątku nie zawierał wyników ze stderr, a dokument ostrzega przed użyciem stderr = PIPE w check_output(). Zamiast tego zaproponowałbym jedną niewielką poprawę rozwiązania J.F. Sebastiana poprzez dodanie obsługi stderr. W końcu próbujemy poradzić sobie z błędami, a stderr jest tam, gdzie często są zgłaszane.

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

Zgadzam się, że wyjście 'stderr' jest tutaj bardzo istotne. Alternatywnym rozwiązaniem byłoby zamiast tego użycie funkcji 'run()' (zobacz [check_output docs] (https://docs.python.org/3/library/subprocess.html#subprocess.check_output) jak wymienić). Ponieważ wtedy możesz użyć 'e.stderr' z wyjątku w raportowaniu błędów. – Sebastian

+0

To powinno być na górze. – Oleg

2

Istnieją dobre odpowiedzi tutaj, ale w tych odpowiedziach, nie była to odpowiedź, która pojawia się w tekście z wyjścia stosu ślad, który jest domyślnym zachowaniem wyjątku.

Jeśli chcesz użyć tej sformatowany informacji Traceback, może chcesz:

import traceback 

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

Jak można być w stanie powiedzieć, powyższe jest przydatna w przypadku gdy masz hasło w check_call (args) że chcesz zapobiec wyświetlaniu.

Powiązane problemy