Etiqueta: análisis estático de código

Libro: JavaScript: The Good Parts de Douglas Crockford

A pesar de que Crockford tiene un estilo de comunicación bastante peculiar, demasiado agresivo, para mi gusto, tenía bastantes expectativas puestas en este libro, que al final no se cumplieron.

Para mi lo mejor del libro es que se puede leer muy rápido y los railroads syntax diagrams. No es desde luego un libro para aprender javascript sino más bien una guía de ciertas prácticas a evitar y una justificación de las reglas de jslint. La verdad es que al contrario que otros libros de javascript como el de Flanagan o centrados en los idioms de los lenguajes como Effective Java, no me veo volviendo a este de vez en cuando para repasar algún concepto.

El libro puede gustarte si eres de los que se leen la definición de reglas de eslint para definir tu propio subset.

Nota. Estas transparencias también son un buen resumen del libro e incluso tienen un «code playground» para practicar.

Emacs y análisis estático de código en python

Gracias a Cartolab he podido seguir dándole una vuelta al análisis de código estático en python. La idea era reducir los incómodos errores que se producen porque te equivocas al escribir el nombre de una variable o cosas parecidas de las que no te das cuenta has que ejecutas el código. Tratar de detectar estos errores de la forma en que lo veíamos en el artículo anterior sigue sin ser demasiado productivo y aquí es donde entra en juego la extensión para emacs Flymake.

Lo que hace Flymake es «pedirle» a emacs que esté continuamente realizando ciertos análisis sobre el código o tratando de compilarlo y/o ejecutarlo. En realidad flymake es un plugin bastante genérico que funciona del siguiente modo:

  1. Si al abrir un nuevo buffer está marcado como «a chequear», flymake programa un temporizador para el buffer. La forma de marcar buffers suele ser en función de la extensión del archivo que abramos
  2. Crea una copia temporal de los buffers abiertos cada cierto tiempo
  3. Ejecuta un programa externo sobre la copia del buffer. Este programa puede ser gcc, make u otra cosa como una herramienta de análisis estático de código.
  4. Parsea la salida que produce el programa externo y da una salida visual en el buffer actual para representar los posibles errores.

Activar flymake

Activar flymake junto a alguna de las herramientas que veíamos en el post anterior es bastante fácil. Flymake viene instalado por defecto, así que sólo tenemos que tener en el path los ejecutables a las herramientas de análisis, y añadir a nuestro .emacs las instrucciones para que se activen con los ficheros python.

flymake con pyflakes:

(when (load "flymake" t)
(defun flymake-pyflakes-init ()
(let* ((temp-file (flymake-init-create-temp-buffer-copy
'flymake-create-temp-inplace))
(local-file (file-relative-name
temp-file
(file-name-directory buffer-file-name))))
(list "pyflakes" (list local-file))))
(add-to-list 'flymake-allowed-file-name-masks'("\\.py\\'" flymake-pyflakes-init)))

flymake con pylint (el comando epylint es un modo especial de pylint para trabajar con emacs)

(when (load "flymake" t)
(defun flymake-pylint-init ()
(let* ((temp-file (flymake-init-create-temp-buffer-copy
'flymake-create-temp-inplace))
(local-file (file-relative-name
temp-file
(file-name-directory buffer-file-name))))
(list "epylint" (list local-file))))
(add-to-list 'flymake-allowed-file-name-masks'("\\.py\\'" flymake-pylint-init)))

flymake con pep8
(when (load "flymake" t)
(defun flymake-pylint-init ()
(let* ((temp-file (flymake-init-create-temp-buffer-copy
'flymake-create-temp-inplace))
(local-file (file-relative-name
temp-file
(file-name-directory buffer-file-name))))
(list "pep8.py" (list "--repeat" local-file))))
(add-to-list 'flymake-allowed-file-name-masks'("\\.py\\'" flymake-pylint-init)))

Por otro lado también podriamos configurar flymake para hacer que se pasarán las tres herramientas de forma automática pero yo lo veo innecesario dado que la información que proporcionan es en muchos casos redudante y estamos disminuyendo el rendimiento del ordenador.

Además de indicarle a flymake con que herramienta queremos trabajar, tenemos que activarlo para los buffers que nos interesen. Esto podemos hacerlo de varios modos:

  • Manual. Activando el modo menor de flymake. M-x flymake-mode
  • Que active el modo menor flymake cuando estemos en el modo python. Añadiendo al .emacs
    (add-hook 'python-mode-hook 'flymake-mode)
  • O que active el modo flymake en base a las extensiones de fichero que le hemos pasado anteriorme (flymake-allowed-file-name-masks)
    (add-hook 'find-file-hook 'flymake-find-file-hook)

Configurar flymake

Por defecto flymake ilumina (highlight) la línea de código donde se produce el error, empleando un color u otro en función de si es un error o un warning. Al dejar el ratón encima de la línea con el error (hover) nos muestra la descripción del error.

Para cambiar los colores que se emplean por defecto podemos emplear algo parecido a esto:

'(flymake-errline ((((class color)) (:background "LightPink" :foreground "black"))))
'(flymake-warnline ((((class color)) (:background "LightBlue2" :foreground "black"))))

Para que subraye en lugar de hacer highlight de la línea con el error:

'(flymake-errline ((((class color)) (:underline "red"))))
'(flymake-warnline ((((class color)) (:underline "yellow")))))

Para hacer que el fringe derecho se muestre un indicador de en que líneas hay errores podemos usar rfringe. Aunque yo no he sido capaz de hacerlo funcionar.

Mostrar la descripción del error en el minibuffer. Esto es imprescindible cuando se trabaja en modo -nw.

(defun my-flymake-show-help ()
(when (get-char-property (point) 'flymake-overlay)
(let ((help (get-char-property (point) 'help-echo)))
(if help (message "%s" help)))))
(add-hook 'post-command-hook 'my-flymake-show-help)

Aunque el código anterior a mi me funciona, para mostrar el error en el minibuffer se suele emplear el plugin flymake-cursor.

Moverse a través de los errores. Podemos emplear los comandos M-x flymake-goto-prev-error para ir al error anterior, o M-x flymake-goto-next-error para ir al siguiente. También podemos vincularlos a determinadas teclas:

(global-set-key [TECLA] 'flymake-goto-prev-error)
(global-set-key [TECLA] 'flymake-goto-next-error)

Links relacionados

Herramientas de análisis de código estático en Python

Programar es fácil, y puede hacerse con el bloc de notas. Escribir buen código es bastante más complicado, por ello existen un montón de herramientas que pueden ayudarnos. Un tipo de herramientas que he empezado a usar (en python) ultimamente son las de análisis estático de código. Estas herramientas examinan tu código (sin ejecutarlo) en busca de ciertos patrones, alertando de «code smells», incroguencias de estilo, posibles bugs, código repetido e incluso dando consejos sobre rendimiento o encapsulamiento en algunos casos.

En python tenemos disponibles distintos analizadores de código, y es habitual ver preguntas sobre cual es mejor. Las cuatro herramientas de este tipo para python están actualizadas a las últimas versiones en los repositorios de ubuntu. Para instalarlas:

sudo apt-get install pyflakes pep8 pychecker pylint

Pyflakes

Pyflakes parece la más sencilla de las cuatro herramientas que he usado. Hace pocas comprobaciones del tipo, imports no usados, variables asignadas y no empleadas, …
No chequea el estilo, ni advierte sobre posibles bugs, aunque dicen que es de las más rápidas, por lo que es la que la gente suele usar como «chequeador de código automático» en IDEs como PyDev o emacs.

pep8

pep8 valida el estilo de nuestro código (de forma bastante rigurosa) contra el estándar de estilo PEP8 de Python. Puede ayudar a detectar «code smells», pero no chequea «errores». Comprueba cosas como que las líneas no tengan más de 80 caracteres, los nombres de variables tengan un determinado formato, …

Para chequear un fichero llega con hacer:
pep8 nombre_de_ficheros.py

Aunque habitualmente se lanza con más opciones para obtener más información:
pep8 --show-source --show-pep8 nombre_de_ficheros.py

Cuando lo lanzamos con –show-pep8 proporciona bastante información sobre la regla de estilo que estamos rompiendo por lo que resulta útil para ir interiorizándolas.

Pychecker

Pychecker es la más antigua pero ahora está algo parado. La última versión 0.8.19 es de enero de 2011 y la anterior del 2008. PyChecker si que es bastante potente en cuanto a la detección de posibles bugs o errores como el de usar una variable antes de asignales un valor, llamar a un método que no existe, … En general detecta bastantes de esos errores que se cometen en python (cuando no se usa un IDE que ya detecte estas cosas)
Parámetros interesantes.
–blacklist=unittest Este módulo de python saca algún error con pychecker, así que para evitar ruido le decimos que no lo chequee.

Pychecker tiene, imho, un problema gordo, y es que ejecuta el código para chequearlo, no es realmente una herramienta de análisis estático, por lo que su uso es más bien desaconsejable.

Pylint

Pylint es una especie de mezcla entre pep8 y pychecker puesto que hace análisis tanto del estilo del código como de posibles bugs. Tiene un montón de características, es extensible mediante plugins, …

El informe que proporciona sobre el código es bastante extenso, clasifica los errores por su gravedad, … Como tiene muchas opciones es conveniente echarle un ojo al tutorial y al manual aunque no hace falta para ver su potencial.

Los parámetros más interesantes de pyling son:

  • –reports=n Para que sólo nos saque los posibles errores y no imprima las estadísticas e informes. Puede ser útil ver el informe de vez en cuando, pero si vamos a pasar el chequeo muchas veces sólo mete ruido.
  • –include-ids=y Por defecto no nos muestra el código de error completo. Con esto hacemos que nos lo muestre para poder obtener más información sobre él si no lo entendemos (Esto lo haríamos con pyling –help-msg=ERROR_CODE)
  • –disable=C0111 Esto hace que no se chequeen, los errores C0111, que indican que todos los métodos deberían tener un docstring. Me gusta eliminarlos porque soy de los que piensan que «los comentarios apestan»

Para que la línea de comandos no se vuelva muy complicada estás opciones pueden indicarse en un fichero de configuración para que sean usadas por defecto

Como y cuando usarlas

Lo que me gusta de estas cuatro herramientas es que no hay excusa para no usarlas. Son realmente sencillas, rápidas, y ayudan a hacer un código más legible y mantenible.

Por ahora, para acostumbrarme a ellas, lo que hago es al inicio de cada sesión de trabajo paso las cuatro herramientas en el siguiente orden:

  1. pep8 –show-source –show-pep8 *.py
  2. pylint –reports=n –include-ids=y –disable=C0111 *.py
  3. pychecker –blacklist=unittest *.py
  4. pyflakes *.py

Lo hago así porque me gusta la información sobre el estilo que proporciona pep8, y tras solucionar los errores de pylint ni pychecker ni pyflakes me están proporcionando ayuda adicional así que es probable que pronto deje de usarlos. De hecho cuando me acostumbre a la guía de estilo de pep8 es probable que sólo use pylint.

Como el proyecto en el que la estoy probando apenas son 6 o 7 clases de alrededor de 200 líneas, está forma de trabajar me resulta cómoda y me permite aprender a usarlas, pero está claro que en otros contextos puede no ser lo más adecuado.

En el próximo artículo de esta serie hablaremos sobre como integrar estas herramientas en emacs, al estilo de las sugerencias de eclipse u otros IDE, y otras aproximaciones un poco más sofisticadas de como integrarlas en nuestro flujo de trabajo.

¡Prueba y cuéntame!

Actualización 20/Julio: He añadido algún enlace y desaconsejado el uso de pychecker por las razones que ya están incluídas en el propio artículo.