Mostrando entradas con la etiqueta python. Mostrar todas las entradas
Mostrando entradas con la etiqueta python. Mostrar todas las entradas

Pruebas unitarias en Python: unittest

unittest, también conocido como PyUnit, pertenece a ese grupo de herramientas conocido como zUnit. Un conjunto de frameworks con base en SUnit para Smalltalk, de Kent Beck.
El uso de unittest es sencillo, cuando menos al tener práctica. Para cada uno de los grupos de pruebas, debemos de crear una clase que herede de unittest.TestCase, y desde ahí, ir creando métodos que comiencen con test. Cada uno de estos métodos será cada una de las pruebas que realizaremos. El resultado de cada una puede ser:
  • OK, prueba exitosa.
  • FAIL, la prueba ha fallado y se lanza una excepción AssertionError.
  • ERROR, la prueba ha fallado, pero se lanza una excepción distinta de AssertionError.
Aunque como en programación Python, se puede hacer uso de clausulas if, o emplear try/except, la clase TestCase tiene ya implementados una serie de métodos que facilitan las comprobaciones.

Métodos de comprobación

  • assertAlmostEqual(first, second, places=7, msg=None)
    Comprueba que los objetos pasados sean iguales hasta el decimal pasado como parámetro.
  • assertEqual(first, second, msg=None)
    Comprueba que los objetos pasados como parámetros sean iguales.
  • assertFalse(expr, msg=None)
    Comprueba que la expresión sea falsa.
  • assertNotAlmostEqual(first, second, places=7, msg=None)
    Comprueba que los objetos pasados como parámetros no sean iguales hasta el decimal pasado como parámetro.
  • assertNotEqual(first, second, msg=None)
    Comprueba que los objetos pasados como parámetros no sean iguales.
  • assertRaises(excClass, callableObj, *args, **kwargs)
    Comprueba que al llamar al objeto callableObj con los parámetros definidos en *args y **kwargs se lanza una excepción de tipo excClass.
  • assertTrue(expr, msg=None)
    Comprueba que la expresión sea cierta.
  • assert_(expr, msg=None)
    Comprueba que la expresión sea cierta.
  • fail(msg=None)
    Falla inmediatamente.
  • failIf(expr, msg=aquí ponemos lo que queramos desplegar)
    Falla si la expresión es cierta.
  • failIfAlmostEqual(first, second, places=7, msg=None)
    Falla si los objetos pasados como parámetros son iguales hasta el decimal pasado como parámetro.
  • failIfEqual(first, second, msg=None)
    Falla si los objetos pasados como parámetros son iguales.
  • failUnless(expr, msg=None)
    Falla a menos que la expresión sea cierta.
  • failUnlessAlmostEqual(first, second, places=7, msg=None)
    Falla a menos que los objetos pasados como parámetros sean iguales hasta el decimal pasado como parámetro.
  • failUnlessEqual(first, second, msg=None)
    Falla a menos que los objetos pasados como parámetros sean iguales.
  • failUnlessRaises(excClass, callableObj, *args, **kwargs)
    Falla a menos que al llamar al objeto callableObj con los parámetros definidos por *args y **kwargs se lance una excepción de tipo excClass.

Referencias

Leer más

If corto en python

Hoy es uno de esos días en los que estoy programando y me interesa simplificar el código, pero necesito igualmente condicionales. Desde la época de C, una de las formas de realizar asignaciones condicionales sencillas era con un 'if corto', o lo que es lo mismo, pasar de algo tal que esto,
...
country = ''
if(lang == 'es') {
    country = 'ES'
} else {
    country = 'EN'
}
...
A algo como esto,
...
country = (lang == 'es' ? 'ES' : 'EN')
...
Este tipo de sentencias se aplican en muchos, por no decir todo tipo de lenguajes de programación, no sólo C. Por ejemplo en PHP,
...
$value = ($userid == $user['userid'] ? 'yes' : null);
...
Sin embargo, cuando quiero aplicar esta misma fórmula en Python, no me funciona. Esto no significa que Python no tenga un 'if acortado', sino que lo implementó de otra forma,
...
host = {
   'name': (tmp['dns'] if tmp.has_key('dns') else tmp['name'])
}
...
Como podéis observar en vez de usar el tradicional (a==B ? x : y), lo que hace es la asignación y luego al comprobación: (x if a==b else y) y suprime a la vez los operadores reducidos ? y :
Tenerlo presente si vais a programar en Python ;-)
Leer más

Montando un entorno virtual en Python

Desde hace una época hacia aquí, la gente que me conoce sabe que estoy programando en Python. Aunque no soy programador, es cierto que si hay que meterle mano al código, pues se le mete, tampoco pasa nada. Lo más importante es divertirse con lo que haces, tanto en el ámbito laboral como personal.
Bueno, dejándonos de historias, el detalle es que tras aprender cómo funciona Python, módulos y dependencias, rápidamente me di cuenta de que programar en él estaba ensuciando mucho mi sistema. Cada nuevo módulo que instalaba, realmente quedaba colocado en el sistema, con lo que había que mantenerlo. Puesto que me gusta tener mi escritorio Linux lo más "limpio" posible, trabajando en 4 o 5 cosas diferentes de Python terminaba por tener miles de dependencias instaladas que en realidad el sistema no necesitaba para nada.
La primera solución por la que opté, y que es perfectamente válida, es el empleo de máquinas virtuales. Un VirtualBox específico para cada proyecto. Con ello consigues no ensuciar el sistema y tener todo más o menos ordenado. Así que es viable.
Sin embargo, y de casualidad,  haciendo una búsqueda de módulos Python en Debian (apt-cache al rescate!) descubrí un nuevo paquete más que interesante: python-virtualenv.
python-virtualenv monta un pequeño entorno de desarrollo Python privado a una carpeta sobre el cual instalas los módulos que desees y con los que quieras trabajar, pero sin afectar al sistema. Con ello, se acabaron las instancias de máquinas virtuales para desarrollar en Python. Simplemente creo un nuevo entorno y trabajando con easy_install o pip, instalo todos los módulos que necesite para el proyecto en cuestión. Al salir del entorno virtual, esos módulos no están presentes en el sistema. Por decirlo de cierta forma, "crea un chroot local para cada instancia Python".
Vamos a ver cómo instalarlo y trabajar con él.
  • Instalación
    La instalación del nuevo módulo se torno realmente sencilla en sistemas Debian, ya que está en el repositorio.
    shell> apt-get install python-virtualenv
    
  • Creación del nuevo proyecto
    Es necesario crear una carpeta de trabajo, a nivel local y de usuario (no es necesario ser root) para tener la "copia" de Python y trabajar con él. Para ello,
    shell> cd ~
    shell> virtualenv prueba_python
    
  • Arrancando la instancia virtual
    Dentro de la nueva carpeta, si nos fijamos, podemos ver algo que se parece a una jaula chroot, con aquellas cosas que son necesarias para Python y poco más.
    shell> cd ~/prueba_python
    shell> ls
    bin  include  lib  local
    
    Para comenzar a trabajar con ella, simplemente la activamos.
    shell> source bin/activate
    
  • Trabajando en la instancia
    Si os fijáis, la shell ya ha cambiado y ahora nos muestra que estamos en un entorno Python controlado. Desde aquí tenemos acceso al resto del sistema, pero a nivel de Python, todo lo que se instale, así como las versiones de librerías, etc. estarán en dicha carpeta.
    Si deseamos instalar un nuevo módulo,
    (prueba_python)shell> pip install module
    
    Y a continuación, podemos ya trabajar y lanzar nuestros scripts Python para probarlos.
    (prueba_python)shell> python test.py
    
  • Desactivando la instancia virtual
    Una vez terminamos de trabajar, abandonamos la consola o desactivamos el entorno con,
    (prueba_python)shell> deactivate
    
Como podéis observar, resulta muy sencillo instalar los módulos que necesitemos de esta forma y además sin ensuciar el resto del sistema, ya que todo lo referente a Python quedará bajo dicha carpeta.
Por supuesto, puedes tener cuantas instancias desees, probando diferentes módulos e incluso versiones de Python, para ver cómo afecta una versión al resultado de tu programación.

Espero que tu sistema esté a partir de ahora más limpio ;-)
Leer más

Cómo crear un daemon en python

Una de los grandes problemas que suelen presentarse a nivel de programación de sistemas es la creación, de forma correcta, de un daemon o servicio que se ejecute en segundo plano y que haga el trabajo correctamente.
La forma más cutre que existe de hacer esto es ejecutando el comando y pasarlo luego a segundo plano, dejando así el software en cuestión trabajando, pero no en forma de daemon.
Hoy vamos a ver cómo programar un daemon en Python.
Para ello, lo primero que necesitamos es asegurarnos de que las librerías de Python necesarias están instaladas y en caso contrario, instalarlas. Una vez instaladas, ya podemos comenzar a programar el servicio de forma sencilla y eficiente.

shell> apt-get install python-daemon python-lockfile
En este caso, lo que vamos a realizar es un programa trivial y sin lógica, pero que nos servirá de ejemplo. A continuación os dejo el código.
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import logging
import time
from daemon import runner

class App():
   def __init__(self):
      self.stdin_path      = '/dev/null'
      self.stdout_path     = '/dev/tty'
      self.stderr_path     = '/dev/tty'
      self.pidfile_path    =  '/var/run/test.pid'
      self.pidfile_timeout = 5

   def run(self):
      i = 0
      while True:
         logger.info("message %s" %i++)
         i += 1
         time.sleep(1)

if __name__ == '__main__':
   app = App()
   logger = logging.getLogger("testlog")
   logger.setLevel(logging.INFO)
   formatter = logging.Formatter("%(asctime)s - %(name)s - %(message)s")
   handler = logging.FileHandler("/var/log/test.log")
   handler.setFormatter(formatter)
   logger.addHandler(handler)

   serv = runner.DaemonRunner(app)
   serv.daemon_context.files_preserve=[handler.stream]
   serv.do_action()
Como veis, el código es sencillo y con él ya creamos un pequeño servicio que se ejecutará en segundo plano y que se encargará de escribir cada segundo un mensaje en el log (fichero /var/log/test.log). A mayores también controla perfectamente que sólo haya una instancia del servicio ejecutándose al mismo tiempo (fichero de bloqueo /var/run/test.pid). Y como era de esperar, están incluidos por defecto aquellos métodos de arranque y parada de servicio (start, stop y restart). Para probar el funcionamiento del daemon, podemos hacerlo tal que así,
shell> python /usr/local/bin/test.py
usage: test.py start|stop|restart

shell> python /usr/local/bin/test.py start
started with pid 32547

shell> tail -f /var/log/test.log
2014-04-04 10:00:17,342 - testlog - Info message 3
2014-04-04 10:00:18,343 - testlog - Info message 4
2014-04-04 10:00:19,344 - testlog - Info message 5
2014-04-04 10:00:20,346 - testlog - Info message 6
2014-04-04 10:00:21,347 - testlog - Info message 7

shell> python /usr/local/bin/test.py stop
Terminating on signal 15
Por supuesto, para dejarlo listo y poder tenerlo instalado como un servicio en nuestro sistema, lo único que no falta es un fichero de arranque (/etc/init.d/test). Para ello, creamos uno muy sencillo.
#!/bin/bash

# /etc/init.d/test

### BEGIN INIT INFO
...
### END INIT INFO

case "$1" in
   start)
      echo "Starting server"
      python /usr/local/bin/test.py start 
      ;;

   stop)
      echo "Stopping server"
      python /usr/local/bin/test.py stop
      ;;

   restart)
      echo "Restarting server"
      python /usr/local/bin/test.py restart
      ;;

   *)
      echo "Usage: /etc/init.d/demonioprueba.sh {start|stop|restart}"
      exit 1
      ;;
esac
exit 0
Y ya podemos levantar nuestro servicio cuando sea necesario. Lógicamente, dentro del método run, de la clase App habrá que meter el código necesario para que el servicio tenga sentido, pero eso ya es cosa de cada uno. Aquí la idea era enseñar a levantar un servicio escrito en Python.
Leer más

Formulario de contacto

Nombre

Correo electrónico *

Mensaje *

Últimos comentarios