cómo obtener el número de línea de un error de exec o execfile en Python

Digamos que tengo la siguiente cadena multilínea:

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

Y quiero ejecutarlo en un ámbito particular:

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

Hay un SyntaxError en la línea 6 del comando, y quiero poder reportarlo al usuario. ¿Cómo obtengo el número de línea? He intentado esto:

 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 ) 

… pero obtengo el número de línea de la statement exec , no el número de línea dentro del comando multilínea.

Actualización: resulta que el manejo del tipo de excepción que elegí arbitrariamente para este ejemplo, el SyntaxError , es diferente del manejo de cualquier otro tipo. Para aclarar, estoy buscando una solución que haga frente a cualquier tipo de excepción.

Para los errores de syntax, el número de línea de origen está disponible como el indicador de lineno en el propio objeto de excepción, en su caso almacenado en err . Esto es específico de los errores de syntax donde el número de línea es una parte integral del error:

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

Si también quiere manejar otros errores, agregue un nuevo bloque except Exception, err , y use el módulo de traceback para calcular el número de línea para el error de tiempo de ejecución.

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

Ejemplos:

 >>> 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