Mi primera auditoría de código: revisando scikit-aero

Introducción

Como ya sabéis Javier Gutiérrez, profesor de la Universidad de Sevilla, está escribiendo una serie de entradas en Pybonacci sobre desarrollo dirigido por pruebas en Python, que os animo a leer si no lo habéis hecho todavía. Pues bien, después de conocernos en la PyConES rescatamos la idea de aplicar estos conceptos a problemas no tan genéricos y más cercanos al software científico que escribimos nosotros. Fruto de esta idea Javier se ha tomado la molestia de revisar nuestra biblioteca scikit-aero, y ha escrito una entrada en su blog sobre el proceso:

Como comenté en la entrada Auditorias de código o aprendamos juntos a ser mejores, una de las cosas que más me gusta hacer es analizar código de otras personas para aprender y también para aplicar la regla del buen boy scout e intentar contribuir a que ese código sea un poquito mejor.

El último proyecto que he analizado por el momento ha sido ha sido Scikit-Aero en Github del fenomenal Juan Luis Pibonacci que lleva el proyecto del blog Pybonacci (enlace) y en el que colaboro son una serie de entradas sobre TDD / Desarollo Dirigido por Pruebas.

En este artículo voy a contar brevemente cómo las cosas que he aprendido :)

Sobre scikit-aero

scikit-aero es una pequeña biblioteca Python que escribí mientras estudiaba flujos isentrópicos, ondas de choque, expansiones de Prandtl-Meyer y similares en una materia llamada «Aerothermodynamics». Es, por tanto, algo bastante específico. De ella me serví por ejemplo para producir este diagrama:

Diagrama de ondas de choque oblicuas
Diagrama de ondas de choque oblicuas

Podéis consultar el código en este notebook de ejemplo. Mi idea era además tratar de crear un código bien documentado, bien estructurado y bien probado.

Para los tests utilicé pytest, y para la cobertura el plugin pytest-cov.

Según el artículo de Javier no hice un mal trabajo, aunque quedaban unas cuantas cosas por mejorar en los tests del módulo isentropic.py.

Pruebas para una biblioteca científica: mi enfoque

scikit-aero consiste fundamentalmente en funciones que hacen cálculos matemáticos. No hay, por tanto, interacción con el sistema o con elementos del exterior, jerarquías de objetos complejas, tratamiento sofisticado de entradas del usuario... Mi enfoque para probar estas funciones fue acudir a referencias reconocidas y asegurar que producen el resultado numérico esperado. En este caso me basé en:

  • Tablas de informes técnicos de la NASA o la NACA, como por ejemplo NACA-TR-1135-1953 "Equations, tables, and charts for compressible flow" http://hdl.handle.net/2060/19930091059
  • Problemas resueltos de textos universitarios, como por ejemplo "Modern compressible flow" de J. D. Anderson.

Además, incluí en cada archivo y a veces en cada función de dónde están obtenidos los datos para que cualquier persona los pudiera comprobar. La ventaja de usar informes de la NACA es que los documentos que proceden del gobierno de Estados Unidos están bajo el dominio público y por tanto no tienen restricciones de copyright. Intenté usar este tipo de fuentes cuando fuese posible.

(Inciso: ¿Qué tal si los gobiernos de otros países toman nota?)

Puesto que estamos lidiando en la mayoría de los casos con resultados que tienen imprecisiones, para cada prueba decidí de manera más o menos arbitraria la precisión que iba a manejar para las comparaciones, usando las funciones assert_almost_equal y assert_array_almost_equal de numpy.testing. Si no conseguía un resultado correcto hasta 2 decimales, solía adimensionalizar las variables o directamente comprobar si algo estaba fallando.

Para scikit-aero he conseguido una precisión de al menos 2 decimales en la mayoría de las funciones, salvo en la parte de propiedades de la atmósfera estándar que está menos trabajada (se aceptan pull requests).

Lecciones aprendidas

Voy a comentar algunas cosas que aprendí revisando la pull request. Podéis leer los comentarios vosotros mismos en GitHub.

Dialogar con tus colaboradores

Cuando recibí las mejoras tardé varios días en reaccionar y solo cuando Javier me lo pidió: ¡mal! Como creador de una biblioteca, si quieres que sea atractivo para otros mejorar tu código es fundamental comentar qué te parece el suyo y entablar un diálogo con potenciales colaboradores. A veces habrá cosas que no te gusten y las tendrás que discutir, pero con eso no harás sino aprender más.

Probar todo el código

En primer lugar, vimos que había un test que se había quedado a medio escribir y faltaban algunas funciones por probar. Este es el fallo más evidente y más trivial: es importante que todo el código esté probado. La cobertura es el porcentaje de código probado frente al código total, y cuanta más alta sea más seguros podremos estar que que no hay casos especiales que pueden producir fallos inesperados. Lo ideal es que esté en el 100 % ;) (aunque por encima del 90 % está bien en general) y esto suele llevar a una proporción de líneas de test / líneas de programa mayor que 1.

Consistencia en las pruebas

Esto lo explica mucho mejor Javier:

Veo falta de consistencia en las pruebas. En algunas pruebas se usa un bucle for para verificar varios resultados esperados uno a uno, pero en otras se utiliza un assert_array (definida en NumPy)  que verifica todos los resultados son una sola llamada. El bucle for ambién aumenta la complejidad ciclomática y hace necesario escribir más código, con lo que hay más probabilidades de equivocarse, así que cambio todos los bucles for por llamadas a assert_array.

Me falló que las pruebas fuesen más homogéneas. Esto se ha corregido ya :)

Incluir lo justo y necesario

Es desable eliminar todo código superfluo de las pruebas, para que estas sean lo más simples posible. De esta forma será más fácil descubrir posibles fallos.

¡Complejidad ciclomática!

Estos palabros me espantaron un poco al principio, pero he aprendido que la complejidad ciclomática es una métrica muy utilizada y para valorar la complejidad de un programa. La de scikit-aero era bastante baja (eso es bueno) pero la de los tests debería haber sido un poco menor. En Python se puede medir con la biblioteca radon. La tendré muy en cuenta de ahora en adelante cuando escriba código nuevo.

Conclusiones

Los científicos e ingenieros que escribimos programas de ordenador mientras trabajamos (¿todos?) tenemos mucho, mucho que aprender de los informáticos. Ellos sabrán menos ecuaciones diferenciales y menos resistencia de materiales, pero en lo que respecta a software estamos en la Edad de Piedra en comparación. Seguimos cayendo en problemas superados desde hace años: aún nos enviamos correo en adjuntos de correo electrónico, aún no probamos ni documentamos el software y un largo etcétera. Creo que tenemos que establecer una relación más estrecha con los programadores profesionales para mejorar nuestros programas y hacer nuestra vida más fácil (¿a quién le gusta sufrir porque no se acuerda de qué versión de todas las que hay en la cadena de emails es la buena?).

Y vosotros, ¿escribís ya pruebas en vuestros programas? ¿Conocíais ya las herramientas que hemos mencionado para ello? En caso contrario, ¿crees que empezarás a tomártelo más en serio? ¡Cuéntanos en los comentarios!

Juan Luis Cano

Estudiante de ingeniería aeronáutica y con pasión por la programación y el software libre. Obsesionado con mejorar los pequeños detalles y con ganas de cambiar el mundo. Divulgando Python en español a través de Pybonacci y la asociación Python España.

More Posts - Website

Follow Me:
TwitterLinkedIn

10 thoughts on “Mi primera auditoría de código: revisando scikit-aero

  1. Juan Luis muchas gracias por tus buenos comentarios. La verdad es que me lo paso muy bien haciendo estas cosas y aún mejor compartiéndolas. A ver si podemos seguir colaborando un poquito más en el futuro.

    Me llamó un poco la atención la demora en responder al primer pull request, pero es porque en las pocas veces que lo he hecho, suelen contestar muy rápido. No tiene mayor importancia.

    Mientras, más me vale ir a por la siguiente entrega de la serie de TDD, a ver si la publicamos antes del verano 8D.

    1. Algo también interesante para ver en tu proyecto (y les dejo otro juguete) es la estimación en costos (es muy útil si se nos ocurre reescribir código).

      El modelo mas popular de estimación es cocomo http://es.wikipedia.org/wiki/COCOMO

      Para estimar tu proyecto con cocomo, linux trae una herramienta que se llama sloccount (apt-get install sloccount) http://www.dwheeler.com/sloccount/

      Pueden modificar varios parametros de costos para la estimación, como sueldos promedios y esas barbaridades empresariales.

      La salida del analisis de sckit-aero es esta http://dpaste.com/hold/1498293/

      1. Muchas gracias Juan, no conocía esta herramienta. Aunque me deja un poco de piedra que scikit-aero haya costado $ 17000 jaja xD

      2. hay q personalizar los salarios. además podes imaginar q son 17000 pasos chilenos (fíjate el cambio)

        moraleja: hay que saber interpretar esa salIda

    2. ¡Gracias a ti Javier! Lo mismo digo, espero que sigamos con este tipo de colaboraciones en el futuro :) ¡Estamos impacientes por leer el resto de la serie!

      ¡Un saludo!

  2. Interesante el post. Creo también que hace falta más comunicación en compartir “buenas prácticas” para ir aprendiendo unos de los otros. Si bien hay harta información sobre hacer tal o cual cosa y sobre una y otra herramienta, hace falta una mejor forma de entregar este tipo de información que permite anticiparse a los problemas analizados por Javier.

    Hace poco me aventuré a escribir código para formar una pequeña biblioteca de mi especialidad (acústica) y al poco andar me topé con que un colega de otra latitud estaba trabajando en lo mismo. Decidimos aunar esfuerzos para no reinventar la rueda y potenciar el objetivo final juntando ambos códigos. No ha sido fácil por las diferentes formas de trabajar y las diferentes herramientas que usamos. Hay muchas cosas sin pulir y por falta de experiencia se me hace difícil prever potenciales problemas con algunas decisiones. Por ejemplo, en los tests hay todo un tema porque ocupamos dos frameworks diferentes (unittest y pytest) por el momento y el plan obviamente es unificarlo, pero (me temo que por ignorancia en parte) ninguno de los dos se decanta por el framework que no utiliza (empecé usando pytest porque me pareció más fácil y ya está… XD).

    Que suerte contar con esa calidad de datos. Yo también trato de incorporar datos libres (y ya lo he hecho), pero es difícil encontrar para todo y solo me limita a un par de aplicaciones. En casos ni-tan-simples ni-tan-complejos termino por “re-programar” alguna función que ejecuta un cálculo para asegurarme de que esté bien. Me ha resultado para encontrar algunos errores, pero me temo que no es la mejor solución.

    Saludos

    1. Hola Felipe, gracias por el comentario. De entrada me parece loable que quisieras unir esfuerzos con este otro colega. De los tests no me precuparía tanto, pytest es más simple porque requiere menos código de inicialización y tiene plugins interesantes pero tampoco es un cambio grande. Alguno de los dos acabará cediendo :P

      ¡Un saludo!

  3. Magnífica entrada que ilustra un caso muy común en nuestra vida “científico-ingenieril”. Tal y como apuntas en las conclusiones, el trabajo multidisciplinar es clave. Creo que una de la gran ventaja de Python es precisamente que permite esa convergencia de diferentes profesionales bajo un mismo lenguaje.

    ¡Gracias Javier (@IWT2_Javier) por tus aportaciones!

    1. ¡Gracias Franz por el comentario! Ciertamente es lo que más me enamora de esta comunidad :) Espero que sigamos aprendiendo unos de otros. ¡Un saludo!

Leave a Reply