2015-03-03 9 views
7

Powiedzmy mam następujący ciąg multi-line:jak uzyskać numer linii błędu z Exec lub execfile w Pythonie

cmd = """ 
    a = 1 + 1 
    b = [ 
     2 + 2, 
     4 + 4, 
    ] 
    bork bork bork 
""" 

i chcę go wykonać w określonym zakresie:

scope = {} 
exec(cmd, scope) 
print scope[ 'b' ] 

W wierszu 6 polecenia znajduje się SyntaxError i chcę mieć możliwość zgłoszenia tego użytkownikowi. Jak uzyskać numer linii? Próbowałem to:

try: 
    exec(cmd, scope) # <-- let's say this is on line 123 of the source file 
except Exception, err: 
    a, b, c = sys.exc_info() 
    line_number = c.tb_lineno # <-- this gets me 123, not 6 
    print "%s at line %d (%s)" % (a, line_number, b.message) 

... ale pojawia się numer wiersza rachunku exec, a nie numer linii w poleceniu multi-line.

Aktualizacja: okazuje się, że obsługa wyjątku, który arbitralnie wybrałem dla tego przykładu, SyntaxError, różni się od obsługi dowolnego innego typu. Aby wyjaśnić, szukam rozwiązania, które radzi sobie z każdym rodzajem wyjątku.

+0

Pan spojrzał w [ 'pdb'] (https://docs.python.org/2/library/pdb.html)? – jonrsharpe

Odpowiedz

7

W przypadku błędów składni, numer wiersza źródłowego jest dostępny jako flaga lineno na samym obiekcie wyjątku, w twoim przypadku przechowywany w err. Jest to charakterystyczne dla błędów składniowych, w których liczba linii jest integralną częścią błędu:

>>> cmd = """ 
... 1 \ + 
... 2 * " 
... """ 
>>> try: 
... exec cmd 
... except SyntaxError as err: 
... print err.lineno 
... 
2 

Jeśli chcesz również obsługiwać inne błędy, dodaj nowy except blok except Exception, err i użyć modułu traceback obliczyć linię numer błędu wykonania.

import sys 
import traceback 

class InterpreterError(Exception): pass 

def my_exec(cmd, globals=None, locals=None, description='source string'): 
    try: 
     exec(cmd, globals, locals) 
    except SyntaxError as err: 
     error_class = err.__class__.__name__ 
     detail = err.args[0] 
     line_number = err.lineno 
    except Exception as err: 
     error_class = err.__class__.__name__ 
     detail = err.args[0] 
     cl, exc, tb = sys.exc_info() 
     line_number = traceback.extract_tb(tb)[-1][1] 
    else: 
     return 
    raise InterpreterError("%s at line %d of %s: %s" % (error_class, line_number, description, detail)) 

Przykłady:

>>> my_exec("1+1") # no exception 
>>> 
>>> my_exec("1+1\nbork") 
... 
InterpreterError: NameError at line 2 of source string: name 'bork' is not defined 
>>> 
>>> my_exec("1+1\nbork bork bork") 
... 
InterpreterError: SyntaxError at line 2 of source string: invalid syntax 
>>> 
>>> my_exec("1+1\n'''") 
... 
InterpreterError: SyntaxError at line 2 of source string: EOF while scanning triple-quoted string 
+0

Interesujące! Nie oczekiwałem, że "SyntaxError" będzie inny niż inne typy. Oczywiście chcę też móc zgłaszać inne błędy. Twoja odpowiedź postawiła mnie na właściwej drodze. Aby móc oznaczyć ją jako zaakceptowaną, jeśli nie masz nic przeciwko, zmienię twoją odpowiedź tak, aby zawierała pełne rozwiązanie, do którego to doprowadziło (i abyś mógł ją dalej ulepszyć, jeśli myślisz, że powinna być). – jez

+1

@jez Zwykle zmiany są zarezerwowane dla poprawek, a nie dla dużych interwencji w odpowiedzi. Przynajmniej sprawię, że kod zostanie zastosowany przez PEP 8. Prawdopodobnym powodem, dla którego "SyntaxError" jest inny, jest to, że pochodzi z kompilatora, zanim kod kiedykolwiek miał szansę zostać uruchomiony. Inne błędy pochodzą z silnika wykonawczego, a numer wiersza musi zostać wyodrębniony z układu śledzenia. Możesz myśleć o niej jak o różnicy między błędem kompilacji a błędem czasu wykonywania w języku statycznym - są różne, ponieważ są podnoszone przez bardzo różne mechanizmy iw różnym czasie. – user4815162342

+0

Notowany na wszystkich liczbach. Normalnie śmieję się z twarzy pep8, ale myślę, że wasza przestrzeń to wasze zasady. I oczywiście usuń go, jeśli uważasz, że jest nieodpowiedni - po prostu chciałem zaakceptować twoją odpowiedź, ponieważ doprowadziło mnie to do rozwiązania, i chciałem, aby zaakceptowana odpowiedź zawierała kompletne rozwiązanie (źle się posługuje sugerowanym podejściem, aby napisać własną odpowiedź, która Akceptuję) – jez