python debugging logging

python - Método print() para imprimir la expresión pasada literalmente junto con la salida calculada para una depuración rápida



debugging logging (9)

Deseo poder realizar la depuración de python usando print () o un método similar donde imprima la expresión pasada además de la salida habitual.

Por ejemplo, para el siguiente código:

print(42 + 42) print(type(list)) print(datetime.now())

Salida de corriente:

84 <class ''type''> 2019-08-15 22:43:57.805861

Rendimiento esperado:

42 + 42 : 84 type(list) : <class ''type''> datetime.now() : 2019-08-15 22:43:57.805861

Actualmente, se puede lograr lo mismo agregando manualmente la cadena de expresión, (no es tan elegante y viola el principio DRY).

print("42 + 42 : ", 42 + 42) print("type(list) : ", type(list)) print("datetime.now() : ", datetime.now())

Intenté anular la impresión incorporada, pero sin éxito:

import builtins def print(*args, **kwargs): return builtins.print(*args, **kwargs) # passed expression isn''t available here as string!

¿Hay una manera de lograr esto? ¡Gracias!


Cuando llama al método print, los argumentos pasados ​​no se evalúan mediante el método print, se evalúan antes de pasar al método print como argumento.

print(42 + 42) => print(84) print(type(list)) => print(<type ''type''>) print(datetime.now()) => print(datetime.datetime(2019, 8, 15, 23, 9, 50, 619157))

El método de impresión interna simplemente convierte el objeto dado en una cadena al llamar al método __str __ () del objeto pasado


En general, creo que si te encuentras usando eval , probablemente haya una mejor manera de hacer lo que intentas hacer, pero:

for statement in ["42 + 42", "type(list)", "datetime.now()"]: print("{} : {}".format(statement, eval(statement))


Las cadenas f admitirán algo como esto en Python 3.8 (actualmente en versión beta).

De los docs :

Una cadena f como f ''{expr =}'' se expandirá al texto de la expresión, un signo igual, luego la representación de la expresión evaluada. Por ejemplo:

>>> user = ''eric_idle'' >>> member_since = date(1975, 7, 31) >>> f''{user=} {member_since=}'' "user=''eric_idle'' member_since=datetime.date(1975, 7, 31)"

Los especificadores de formato de cadena f habituales permiten un mayor control sobre cómo se muestra el resultado de la expresión:

>>> delta = date.today() - member_since >>> f''{user=!s} {delta.days=:,d}'' ''user=eric_idle delta.days=16,075''

El especificador = mostrará la expresión completa para que se puedan mostrar los cálculos:

>>> print(f''{theta=} {cos(radians(theta))=:.3f}'') theta=30 cos(radians(theta))=0.866


Puede definir una función de superprint e imprimirla y luego evaluar una cadena:

from datetime import datetime def superprint(str): print(str," : ",eval(str)) a = "42 + 42" b = "type(list)" c = "datetime.now()" superprint(a) superprint(b) superprint(c)

SALIDA

42 + 42 : 84 type(list) : <class ''type''> datetime.now() : 2019-08-15 14:44:43.072780

Si puede vivir arrojando todo lo que desea imprimir entre comillas, esto podría funcionar para usted.


Puede usar https://github.com/cool-RR/PySnooper

In [1]: from datetime import datetime In [2]: import pysnooper In [3]: @pysnooper.snoop() ...: def output(): ...: print(42 + 42) ...: print(type(list)) ...: print(datetime.now()) ...: In [4]: output() Source path:... <ipython-input-3-d5732f8e9c36> 22:14:08.934915 call 2 def output(): 22:14:08.935031 line 3 print(42 + 42) 84 22:14:08.935061 line 4 print(type(list)) <class ''type''> 22:14:08.935083 line 5 print(datetime.now()) 2019-08-25 22:14:08.935100 22:14:08.935109 return 5 print(datetime.now()) Return value:.. None


Puede usar el módulo de inspect para obtener la línea de origen ( code_context ) de la persona que llama:

from inspect import getframeinfo, currentframe def vprint(value): caller = currentframe().f_back info = getframeinfo(caller) label = ''''.join(info.code_context).strip() label = label.replace(''vprint('', '''')[:-1].strip() print(label, ''='', value) >>> vprint(12 + 3) 12 + 3 = 15 >>> vprint(type(list)) type(list) = <type ''type''> >>> vprint(lambda x: x + 1) lambda x: x + 1 = <function <lambda> at 0x7f93c104b9b0>

Funcionará bien solo para evaluaciones de una sola línea. Como code_context devuelve solo la línea ejecutada (y no toda la instrucción), esto puede suceder:

>>> vprint([''''] + ...: [''a'', ''b'']) [''a'', ''b''] = ['''', ''a'', ''b''] >>> vprint(math.log( ...: 2 * math.pi)) 2 * math.pi) = 1.83787706641

Nota: romper la línea con / corrige esto (la sangría seguirá siendo extraña):

>>> vprint(math.log( / ...: 2 * math.pi)) math.log( 2 * math.pi) = 1.83787706641


Simplemente use el rastreo y busque el argumento de llamada.

La ventaja de esta solución es que no tiene que poner la expresión entre paréntesis.

import re import traceback def prnt_expression(expression): for s in traceback.format_stack(): match = re.search(''prnt_expression/((.*)/)'', s) if match: expression_string = match.group(1) break print(f''{expression_string} : {expression}'')

llámalo así:

prnt_expression(42 + 42)


Usando Augusto Men''s Answer como base, el siguiente anula el método de impresión incorporada de python (). Esto ayudará a realizar la depuración sin requerir ningún cambio en el código bajo prueba, simplemente agregue esta definición de función en la parte superior de cualquier archivo y funcionará.

from inspect import getframeinfo, currentframe import builtins def print(*args, **kwargs): info = getframeinfo(currentframe().f_back) label = ''''.join(info.code_context).strip() label = label.replace(''print('', '''', 1)[:-1].strip() # (optional) return builtins.print(label, '':'', *args, **kwargs) print(42 + 42) print(type(list)) print(datetime.now()) print([i for i in range(5)]) if 1 < 2: print(''True'')

Salida:

42 + 42 : 84 type(list) : <class ''type''> datetime.now() : 2019-08-28 16:00:10.812306 [i for i in range(5)] : [0, 1, 2, 3, 4] if 1 < 2: ''True'' : True


Editar: Lo siento, mi respuesta no es realmente correcta, consulte @Augusto Men.

Bueno, nada es imposible en Python, pero usar eval no siempre puede funcionar:

import inspect from datetime import datetime def my_print(a): assert(callable(a)) source = inspect.getsource(a).replace("my_print(lambda:", "").strip()[:-1] print(source + " : " + str(a())) # pass lambda function to my_print print("my_print_result:") my_print(lambda: 42 + 42) my_print(lambda: type(list)) my_print(lambda: datetime.now()) def eval_print(s): print(s + " : " + str(eval(s))) # another way is passing string to eval_print # but eval has its own special evaluation rules # which will not work as expected when used in function print("/neval_print result:") eval_print("42 + 42") eval_print("type(list)") eval_print("datetime.now()") def test(): local_test_1 = 1 my_print(lambda: local_test_1 + local_test_1) eval_print("local_test_1 + local_test_1") print("/ntest in function:") test()

salida:

my_print_result: 42 + 42 : 84 type(list) : <class ''type''> datetime.now() : 2019-08-26 07:06:30.550408 eval_print result: 42 + 42 : 84 type(list) : <class ''type''> datetime.now() : 2019-08-26 07:06:30.551110 test in function: local_test_1 + local_test_1 : 2 NameError: name ''local_test_1'' is not defined

Cuando use eval en la función, tendremos NameError , porque eval en Python tiene una regla especial:

Las funciones eval() y exec() no tienen acceso al entorno completo para resolver nombres. Los nombres pueden resolverse en los espacios de nombres locales y globales de la persona que llama. Las variables libres no se resuelven en el espacio de nombres adjunto más cercano, sino en el espacio de nombres global.

referenciado desde 4. Modelo de ejecución: documentación de Python 3.7.4