Categorías
Python

El anti patrón del pitón más diabólico

 

Tabla de Contenidos

  • ¿Por qué hacemos esto a nosotros mismos?
  • Las Soluciones
  • lo que puede hacer NowExplicitly Prohibir en su codificación Entradas GuidelinesCreate Para existente overbroad Excepto ClausesEducate sus compañeros de los miembros del equipo
  • Prohibir explícitamente en sus Reglas de codificación
  • Crear entradas para los actuales overbroad Excepto cláusulas
  • educar a sus compañeros Miembros del equipo
  • Por qué Entrar La completa Seguimiento de la pila?
  • Prohibir explícitamente en sus Reglas de codificación
  • Crear entradas para los actuales overbroad Excepto cláusulas
  • educar a sus compañeros de los miembros del equipo

El siguiente es un puesto de invitado por Aaron Maxwell, autor de gran alcance Python.

Hay un montón de maneras de escribir código malo. Pero en Python, uno de cada reina particulares como rey.

estábamos agotados, sin embargo, de júbilo. Después de otros dos ingenieros habían intentado durante tres días a la para fijar un misterioso fallo Unicode antes de renunciar en vano, finalmente aislar la causa después de un simple día. Y diez minutos más tarde, tuvimos una solución candidata.

La tragedia es que podríamos haber saltado los días siete y recta se ha ido a los diez minutos. Pero me estoy adelantando a mí mismo …

Aquí está el remate. El siguiente fragmento de código es una de las cosas más autodestructivas un desarrollador de Python puede escribir:

try:
do_something()
except:
pass

Hay variantes de esa cantidad a lo mismo, diciendo a excepción Excepción: o excepción Excepción como E :, por ejemplo. lo único que hacen el mismo flaco favor enorme: en silencio y de forma invisible ocultar las condiciones de error que de otra manera se pueden detectar de forma rápida y expiden.

¿Por qué afirmo esto es el anti-patrón más diabólico en el mundo de hoy Python?

  • gente hace esto porque esperan un error específico que suceda allí. Sin embargo, la captura de excepciones oculta todos los errores … incluso aquellos que son completamente inesperado.
  • Cuando el error se descubrió finalmente – con demasiada frecuencia, ya que ha aparecido en la producción – es posible que tenga poca o ninguna idea en qué parte del código base que está mal se ha ido. Se le puede tomar una cantidad verdaderamente desalentador de tiempo para averiguar el error es aún ocurriendo en ese bloque try.
  • Una vez que se da cuenta del error que está ocurriendo allí, que está dificultado en gran medida en la solución de problemas por la falta de información crítica. ¿Cuál era la clase de error / excepción? Lo llamada o estructura de datos estaba involucrado? ¿Qué línea de código, y en qué archivo, se originó el error?
  • va a tirar el seguimiento de pila – un cuerpo literalmente invaluable de información que puede hacer que el differencebetween solucionar un fallo en días o minutos. Sí, minutos. Más sobre esto más adelante.
  • Lo peor de todo, esto puede fácilmente acabar dañando la moral, la felicidad, e incluso la autoestima de los ingenieros que trabajan en la base de código. Cuando el error levanta su cabeza, el solucionador de problemas puede hundir horas en la comprensión de la causa raíz solo. Ellos piensan que son un mal programador porque se necesita tanto tiempo para averiguar. Ellos no son; los errores que surgen por la captura de silencio Excepción son intrínsecamente difícil de identidad, localizar y resolver.

En mis años de casi diez aplicaciones de la experiencia de escritura en Python, tanto a nivel individual y como parte de un equipo, este patrón se ha destacado como la única gran carga para la productividad del desarrollador y la fiabilidad de las aplicaciones … especialmente a largo plazo. Si usted piensa que tiene un candidato a algo peor, me encantaría escucharlo.

¿Por qué hacemos esto a nosotros mismos?

Por supuesto, nadie deliberadamente escribe un código diseñado para estresarse al resto de desarrolladores y sabotear la fiabilidad de la aplicación. Hacemos esto porque se espera que el código en el bloque try a fallar a veces en el funcionamiento normal, de alguna manera específica. Con optimismo tratando luego coger una excepción es una manera excelente y totalmente Pythonic de acercarse a esta situación.

insidiosa, a excepción de captura y luego continuar en silencio no parece que todo lo que una idea horrible en el momento. Pero tan pronto como guarde el archivo, se haya configurado el código para crear los peores tipos de errores: Errores

  • que pueden escapar a la detección durante el desarrollo, y de ser expulsados ​​al sistema de producción en vivo. Errores
  • que pueden vivir en el código de producción de minutos, horas, días o semanas antes de darse cuenta del error ha estado ocurriendo todo el tiempo. Errores
  • que son difíciles de solucionar. Errores
  • que son difíciles de fijar incluso una vez que sabe dónde se está levantando la excepción contenida.

en cuenta que no estoy diciendo que nunca para capturar excepciones. Hay son buenas razones a excepción de captura, y luego continúan – justnot silencio . Un ejemplo bueno es un proceso de misión crítica que simplemente no quiere ir siempre hacia abajo. Allí, un patrón inteligente es inyectar cláusulas de excepción try que captura, registrar el seguimiento de la pila completa en la gravedad logging.ERROR o mayor, y continuar.

las soluciones

Así que si no queremos a excepción de captura, lo que hacemos en su lugar? Hay dos opciones.

En la mayoría de los casos, la mejor opción es coger una excepción más específica. Algo como esto:

try:
do_something()
# Catch some very specific exception - KeyError, ValueError, etc.
except ValueError:
pass

Esto es lo primero que se debe tratar. Se necesita un poco de comprensión del código invocado, para que sepa qué tipos de errores que podrían aumentar. Esto es más fácil de hacer bien cuando se intenta por primera escritura del código, en contraposición a la limpieza de otra persona.

Si alguna ruta de código simplemente necesidad ampliamente capturar todas las excepciones – por ejemplo, el bucle de alto nivel para un proceso persistente de larga duración – entonces cada excepción capturada debe escribir la completo seguimiento de la pila en un registro o archivo , junto con una marca de tiempo. Si está utilizando módulo de registro de Python, esto es muy fácil – cada objeto registrador tiene un método llamado excepción, teniendo una cadena de mensaje. Si usted lo llama en el bloque de excepción, la excepción capturada automáticamente se registra por completo, incluyendo la traza.

import logging

def get_number():
return int('foo')
try:
x = get_number()
except Exception as ex:
logging.exception('Caught an error')

El registro contendrá el mensaje de error, seguido de una pila de traza difusión formateado en varias líneas:

ERROR:root:Caught an error
Traceback (most recent call last):
File "example-logging-exception.py", line 8, in
x = get_number()
File "example-logging-exception.py", line 5, in get_number
return int('foo')
ValueError: invalid literal for int() with base 10: 'foo'

muy fácil.

Qué pasa si su aplicación hace el registro de alguna otra manera – no se utiliza el módulo de registro? Suponiendo que usted no desea refactorizar su aplicación para hacerlo, sólo puede traer y formatear el rastreo asociado con la excepción. Esto es más fácil en Python 3:

# The Python 3 version. It's a little less work.
import traceback

def log_traceback(ex):
tb_lines = traceback.format_exception(ex.__class__, ex, ex.__traceback__)
tb_text = ''.join(tb_lines)
# I'll let you implement the ExceptionLogger class,
# and the timestamping.
exception_logger.log(tb_text)

try:
x = get_number()
except Exception as ex:
log_traceback(ex)

En Python 2, que tiene que hacer un poco más trabajo, porque los objetos de excepción no tienen su rastreo unido a ellos. Usted consigue esta llamando sys.exc_info () en el bloque de excepción:

import sys
import traceback

def log_traceback(ex, ex_traceback):
tb_lines = traceback.format_exception(ex.__class__, ex, ex_traceback)
tb_text = ''.join(tb_lines)
exception_logger.log(tb_text)

try:
x = get_number()
except Exception as ex:
# Here, I don't really care about the first two values.
# I just want the traceback.
_, _, ex_traceback = sys.exc_info()
log_traceback(ex, ex_traceback)

Como resultado, se puede definir una sola función de rastreo-registro que funcionará tanto para Python 2 y 3:

import traceback

def log_traceback(ex, ex_traceback=None):
if ex_traceback is None:
ex_traceback = ex.__traceback__
tb_lines = [ line.rstrip('\n') for line in
traceback.format_exception(ex.__class__, ex, ex_traceback)]
exception_logger.log(tb_lines)

lo que puede hacer ahora

“De acuerdo Aaron, me has convencido. Lloro y lloro por todas las veces que he hecho esto en el pasado. ¿Cómo puedo reparar?” Estoy tan bueno que lo preguntas. Aquí hay algunas prácticas que usted puede comenzar hoy.

Prohibir explícitamente en sus Reglas de codificación

Si su equipo hace las revisiones de código, es posible que tenga un documento de directrices de codificación. Si no, es fácil empezar – esto puede ser tan simple como crear una nueva página wiki, y su primera entrada puede ser este. Sólo tiene que añadir las dos pautas siguientes:

  • Si alguna ruta de código, simplemente debe ponerse en líneas generales todas las excepciones – por ejemplo, el bucle de alto nivel para un proceso persistente de larga duración – a continuación, cada uno de tales excepción capturada debe escribir la traza completa a una log o archivo, junto con una marca de tiempo. No sólo el tipo de excepción y el mensaje, pero la traza completa.
  • Para todos los demás, excepto las cláusulas – que en realidad debería ser la gran mayoría – el tipo de excepción capturada debe ser lo más específico posible. Algo así como KeyError o ConnectionTimeout, etc.

Crear entradas para los actuales overbroad Excepto cláusulas

Lo anterior ayuda a prevenir nuevos problemas voluntad de convertirlo en su base de código. ¿Qué pasa con las capturas excesivamente amplias existentes? Simple: hacer que un billete o problema en su sistema de seguimiento de errores para solucionarlo. Este es un paso de acción sencilla que aumenta en gran medida las posibilidades de que se resolverá y no olvidado. En serio, puede hacerlo en este momento .

recomiendo continuar haciendo un billete único para cada repositorio o aplicación, a través de la auditoría de código para encontrar todos los lugares de excepción es capturado. (Usted puede ser capaz de encontrar a todos ellos con sólo grepping sobre la base de código de “excepción” y “salvo excepciones”.) Para cada aparición, o bien convertirlo a coger un tipo de excepción muy específica; o si no está inmediatamente claro qué eso debería ser, en lugar de modificar el bloque de excepción para registrar la traza completa.

Opcionalmente, el desarrollador puede crear entradas de auditoría adicionales para cualquier específica try / except bloque. Esto es una cosa buena que hacer si usted tiene la sensación de la clase excepción se puede hacer más específica, pero no saben que parte del código lo suficientemente bien como para estar seguros. En ese caso, se pone en código para registrar la traza completa; crear un ticket separado para investigar más a fondo; y asignarlo a alguien que podría ser más clara. Si encuentras gastando más de cinco minutos a pensar en un determinado bloque try / except, te recomiendo que hagas esto y pasar a la siguiente.

educar a sus compañeros de los miembros del equipo

¿Tiene usted las reuniones regulares de ingeniería? Semanal, quincenal o mensualmente? Pedir cinco minutos en el siguiente para explicar este antipatrón, el coste que tiene para su productividad como un equipo, y las soluciones simples.

Aún mejor, vaya a su ventaja tecnología o gerente de ingeniería de antemano y les dice al respecto. Eso será una venta mucho más fácil, ya que son por lo menos tan preocupados por la productividad del equipo como eres. Envíalas el enlace a este ensayo. Heck, si es necesario, yo te ayudaré – obtener en el teléfono conmigo, y voy a convencerlos.

Se puede llegar aún más amplia en su comunidad. ¿Usted va a una reunión local de Python? ¿Tienen charlas relámpago, o se puede negociar de otra forma de cinco a quince minutos de tiempo de orador en la próxima reunión? Servir a sus colegas ingenieros evangelizando esta noble causa.

Por qué Entrar La completa Seguimiento de la pila?

Varias veces anteriormente, que han insistido en el registro de la traza completa, y el mensaje no sólo el objeto de excepción. Si esto parece más trabajo, eso es debido a que puede ser: la traza tiene saltos de línea que puede ensuciar con el formato de su sistema de registro, puede que tenga que ensuciar con el módulo de rastreo, y así sucesivamente. No se acaba de registrar el mensaje en sí suficiente?

No, no lo es. Un mensaje de excepción bien elaborado sólo le dice que la cláusula de excepción es – lo archivo y qué línea de código. Es comúnmente ni siquiera reducirla mucho, pero asumamos que el mejor de los casos aquí. Inicio de sesión simplemente el mensaje es mejor que no registro nada, pero por desgracia, no le dice nada acerca de donde se origina el error. En general, se puede estar en un archivo o módulo completamente diferente, y con frecuencia no es muy fácil de adivinar.

Más allá de esto, las aplicaciones reales desarrollados por los equipos tienden a tener múltiples rutas de código que pueden llamar el bloque de excepción de fondos. Tal vez el error se produce sólo cuando la barra método de la clase Foo se llama, pero nunca cuando barra de funciones () se llama. Inicio de sesión sólo el mensaje no le ayudará discernir entre estos dos.

La mejor historia de guerra que tengo es de trabajar en un equipo de ingeniería de tamaño mediano, de unos cincuenta fuerte. Yo era relativamente nuevo, y me dieron un error Unicode que se despierta periódicamente el que estaba de guardia durante más de cuatro meses. La excepción fue capturado, y el mensaje registrado, pero ninguna otra información se registró. Dos ingenieros de más alto rango habían trabajado en él durante días cada uno, y luego abandonado, diciendo que no podían entenderlo.

Estos fueron los ingenieros inteligentes formidables y de miedo, también. Por último, de la desesperación, intentaron pasar a mí. Utilizando sus extensas notas, inmediatamente me puse en reproducir el problema lo suficientemente bien como para obtener un seguimiento de la pila. Y después de seis horas, al fin entendí. Una vez que tuve que bleeping seguimiento de la pila, se puede adivinar el tiempo que me llevó a tener una solución?

minutos

diez. de ese derecho. Una vez que tuvimos un seguimiento de pila, la solución era obvia. Una semana literal de tiempo ingeniero podría haberse salvado si hubiéramos estado seguimientos de pila de registro desde el principio. Recuerde anterior, cuando digo un seguimiento de pila puede hacer la diferencia entre la resolución de un error en días y resolver en cuestión de minutos? Yo no estaba bromeando.

(Curiosamente, algo bueno salió de él. Es experiencias como ésta, que me llevó a empezar a escribir más sobre Python, y cómo nosotros, como ingenieros puede ser más eficaz con el idioma.)

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *