Sie sind auf Seite 1von 43

Eclipse y Pydev

Andrs Marzal, 1 y 2 de junio de 2010


Eclipse es un entorno de desarrollo diseado originalmente para Java, pero con una arquitectura orientada a la extensin que ha hecho de l un entorno de desarrollo adaptable a diferentes lenguajes y plataformas. Hay extensiones de Eclipse para desarrollo con C/C++, Ruby, Python, Scala, IronPython, PHP, desarrollo de pginas web, etctera. Cuando Eclipse est adaptado al desarrollo con C/C++ suele llamarse Eclipse CDT; cuando est adaptado Java, Eclipse JDT; cuando a PHP, Eclipse PDT; y cuando a Python, Pydev. El desarrollo de Java ha estado controlado por Sun, empresa adquirida recientemente por Oracle. Sun desarrolla el entorno NetBeans, competidor directo de Eclipse. No obstante, Eclipse es la herramienta preferida por la mayor parte de desarrolladores Java: Eclipse es ms ligero en ejecucin y ms extensible (o al menos cuenta con ms extensiones). Pydev es una extensin de Eclipse para el desarrollo de aplicaciones con Python. Durante un tiempo la empresa Aptana ofreca una versin gratuita y otra, ms completa, de pago. Desde la versin 1.5 slo hay una versin y es gratuita. El desarrollador principal es Fabio Zadrozny. El objetivo de este documento es introducir al lector en el uso de Eclipse como plataforma de desarrollo, pero usando el entorno Pydev de desarrollo Python, y no el entorno por defecto para desarrollo con Java.

Instalacin de Eclipse y Pydev


Para instalar Eclipse y Pydev es necesario: Una mquina que corra Windows, Linux o Mac OS X. 300 Mb de disco. 500 Mb de memoria (cuanta ms, mejor). Java Development Kit (JDK) 1.4 o superior. Se puede descargar de http://java.sun.com/. La versin actual (y recomendada) es JDK 6 Update 20. Esta versin se puede bajar de http://java.sun.com/javase/downloads/widget/jdk6.jsp.

En Linux se puede instalar Eclipse con el sistema de control de paquetes propio de la versin que se est usando, pero lo cierto es que no es necesario instalarlo en ninguna de las plataformas: si descargamos el paquete y lo descomprimimos como carpeta (directorio), Eclipse es ejecutable directamente siempre que java (o java.exe) estn en la ruta (PATH) del shell. El paquete de instalacin de Eclipse se puede descargar directamente de la pgina web http://www.eclipse.org/downloads. La ltima versin es la 3.5.2 (la serie 3.5 tiene nombre clave Galileo). Ms que instalacin hablamos de descompresin de un directorio en el que est todo lo necesario para trabajar. Vamos a la pgina http://www.eclipse.org/downloads/ y pinchamos en Eclipse Classic 3.5.2. Llegamos as a una pgina de descarga que depende del operativo desde el que accedemos. Pinchamos en el enlace de descarga (flecha verde) y esperamos la descarga de 163 megabytes.

El fichero eclipse-SDK-3.5.2-win32.zip contiene una carpeta llamada eclipse. Basta con extraer la carpeta (unos 178 megabytes) y abrirla para empezar a usar eclipse. En Windows, la carpeta contiene esto:

Hacemos doble-clic en el icono eclipse.exe para arrancar el programa. Al arrancar nos pide un espacio de trabajo:

Ms adelante veremos qu es un espacio de trabajo. Por el momento, le decimos que s pulsando OK. La pantalla de Eclipse pasa a ser sta:

Cada crculo es un enlace. Los 4 de ms a la izquierda llevan a pginas de ayuda. El de la derecha nos lleva al banco de trabajo (que enseguida veremos qu es). Si pinchamos este ltimo enlace, la ventana principal pasa a tener este aspecto:

Vamos a configurar Eclipse aadiendo Pydev, la extensin que permite adaptar la herramienta al trabajo con Python. Antes habr que haber instalado Python 3.1, disponible en la pgina web oficial de Python: http://www.python.org. Si estamos en Windows, podemos instalarlo en el lugar por defecto: C:\Python31. La extensin de Eclipse en la que estamos interesados se denomina Pydev, su pgina principal est en http://www.pydev.org y el autor mantiene un blog donde informa de las novedades en http://pydev.blogspot.com. Instalamos la extensin desde dentro del mismo Eclipse. Seleccionamos la opcin HelpInstall New Software En el dilogo Install pulsamos el botn Add y aparece un nuevo formulario:

En el campo Name escribimos Pydev (o lo que queramos) y en el campo Location escribimos la URL
http://pydev.org/updates. Pulsamos OK y el dilogo Install pasa tener este aspecto:

Seleccionamos los dos elementos (PyDev y PyDev Mylin Integration) y pulsamos en el botn Next>:

Nuevamente pulsamos Next>, aceptamos la licencia de uso (es una licencia de cdigo abierto) y pulsamos ahora Finish. Eclipse empezar a descargar las extensiones que le hemos indicado y aquellas otras de las que dependen stas:

Eclipse puede advertir del riesgo de descargar software de la red y pedir confirmacin para seguir instalando. Y tambin puede que nos pida otorgar confianza a un certificado de la Eclipse Foundation Inc., cosa que haremos. Finalmente, Eclipse nos solicitar reiniciar el programa para seguir trabajando con mayor garanta de que no habr problemas:

Al reiniciar volver a preguntar si deseamos trabajar en C:\Users\usuario\workspace. Le decimos que s. Si antes marcamos la opcin de que recuerde que ese es el espacio de trabajo por defecto, no volver a preguntarlo la prxima ver que iniciemos Eclipse. Ya tenemos Eclipse listo para el resto de la sesin. Es hora de aprender algunos conceptos bsicos.

Conceptos bsicos
Eclipse ofrece una o ms ventanas en las que hay varios componentes dispuestos de un modeo determinado. Es el banco de trabajo (workbench). Qu componentes se muestran y cmo se distribuyen depende del entorno de desarrollo (si estamos desarrollando para Java, C/C++, Python) y de la accin que deseemos llevar a cabo (editar el proyecto, depurarlo, etctera). Una perspectiva (perspective) es una combinacin y disposicin determinada de vistas (view) en el banco de trabajo. Podemos elegir una perspectiva de dos modos: Desde el men WindowOpen PerspectiveOther Aparecer un dilogo modal similar a ste:

Desde la esquina superior derecha, lugar en el que podemos anclar las perspectivas que usamos habitualmente y seleccionarlas fcilmente:

El botn

conduce al cuadro de dilogo del punto anterior y permite seleccionar cualquier perspectiva.

Cuando abordamos un proyecto, como la construccin de una aplicacin, diseamos diferentes unidades interdependientes: libreras, ficheros con datos, imgenes, iconos, programas ejecutables, scripts Eclipse utiliza una nomenclatura propia que diferencia entre cada una de esas unidades, a cuya coleccin de ficheros de cdigo fuente y recursos denominamos proyecto (project), y la coleccin de proyectos relacionados entre s, a los que denominamos espacio de trabajo (workspace). Cuando instalamos Eclipse y lo ejecutamos por primera vez, nos solicitar crear un espacio de trabajo que, si lo deseamos, ser el espacio de trabajo por defecto. Al abrir nuevamente Eclipse y si hay un espacio de trabajo por defecto, Eclipse recuperar el estado en el que quedaron sus vistas la ltima que trabajamos con ese espacio de trabajo (editores de texto, explorador de paquetes, etctera). Es decir, Eclipse mantiene la sesin entre diferentes ejecuciones con un mismo espacio de trabajo. Dado que los usuarios suelen crear un espacio de trabajo por defecto, es frecuente encontrar usuarios noveles que crean todos sus proyectos en ese mismo espacio de trabajo. Es un error. Un espacio de trabajo que acabe conteniendo varias decenas de proyectos que realmente no estn relacionados entre s y que, posiblemente, usen tecnologas distintas (proyectos Java conviviendo con otros Python), har que Eclipse pierda eficiencia sin necesidad alguna, que aparezcan mensajes de error espurios o que se dificulte enormemente la distribucin de una base de cdigo. Es muy aconsejable crear tantos espacios de trabajo como sea conveniente. Cambiar de un espacio de trabajo a otro es muy sencillo con la opcin de men FileSwitch Workspace (pero ha de tenerse en cuenta que se reiniciar Eclipse cada vez que se cambie efectivamente de espacio de trabajo). Tambin es posible arrancar dos instancias de Eclipse, cada una con espacio de trabajo distinto, y trabajar as en dos o ms proyectos (en el sentido del usuario, no de Eclipse) simultneamente. Nuestro espacio de trabajo por defecto es workspace (tpicamente en el directorio de documentos de usuario).

Nuestro primer proyecto Python


Empezamos con el Hola, Mundo! de rigor. Hemos de crear un proyecto nuevo, pero antes conviene seleccionar la perspectiva de desarrollo con Pydev. Con la opcin de men WindowOpenPerspectiveOther se abre el cuadro que nos permite escoger Pydev. En la zona superior derecha aparecer Pydev seleccionado.

Seleccionamos ahora la opcin FileNewPydev Project y, adems de dar nombre al nuevo proyecto, seleccionamos la versin de la gramtica de Python y el intrprete que usaremos. El dilogo de creacin de proyecto Pydev tiene este aspecto:

Seleccionamos como gramtica la 3.0 (aunque tenemos Python 3.1, a efectos de gramtica es apropiado usar la 3.0) y pinchamos en el enlace Please configure an interpreter in the related preferences before proceeding.. El enlace lleva a un nuevo dilogo titulado Preferences (desde el que podramos fijar otras preferencias de Eclipse, no slo las de Pydev, pero no es el momento):

Como an no hemos dado de alta ningn intrprete, hemos de configurar la preferencias para que encuentre uno dndolo de alta previamente. Pulsamos en el botn New y eso nos lleva a un nuevo dilogo:

Nombremos al intrprete Python 3.1 (sin las comillas) y busquemos el fichero python.exe. En Windows lo encontraremos en la ruta C:\Python31\python.exe. El cuadro de dilogo queda as:

Pulsamos OK y, tras una breve espera, encuentra las libreras estndar:

Podemos pulsar directamente OK. En el dilogo Preferences aparecern ahora las libreras aadidas:

Tras pulsar nuevamente OK, hemos de esperar un poco. En el dilogo de creacin de proyecto podemos seleccionar como intrprete el que acabamos de crear, Python 3.1, o el intrprete por defecto (Default), que ahora es el mismo. Slo nos queda dar nombre al proyecto: HolaMundo. El banco de trabajo incluye ahora un elemento nuevo en el Pydev Package Explorer: una carpeta con una letra P y el texto HolaMundo. El explorador de paquetes ofrece una representacin en rbol del contenido de los proyectos en el espacio de trabajo actual. Expandamos un par de niveles del rbol:

Hay dos elementos: una carpeta con el dibujo de un paquete encima y con el texto src (abreviatura de source) y un elemento con el icono de Python y el nombre Python 3.1. Ese segundo elemento mantiene referencias a las libreras y documentacin que maneja Eclipse en este proyecto. Nuestro cdigo se ubicar en la carpeta src. Con el botn derecho del ratn sobre la carpeta src accederemos a un men contextual donde seleccionamos NewPydev Module. Un nuevo dilogo solicita el nombre del fichero (y el del package, pero eso lo veremos ms adelante).

Rellenamos slo el campo Name con el texto hola (sin comillas) y el campo Template lo dejamos como est (en <Empty>). En el explorador de paquetes se mostrar el fichero hola.py y en la zona central se abrir un editor de texto para el fichero hola.py, que ya tiene algn contenido (una cadena multilnea con la fecha de creacin y el autor en un formato de documentacin del que hablaremos un poco ms adelante):

10

Escribamos este texto debajo de la cadena de cabecera: print("Hola mundo") Ahora podemos ejecutar el programa de varios modos: Con la tecla F9. Con el men contextual que sale al pulsar con el botn derecho en hola.py, seleccionando la opcin Run AsPython Run Con el icono Run As que aparece en la barra sobre el editor de texto y que tiene este aspecto: pulsarlo aparecer un men desplegable. Elegimos Run AsPython Run. . Al

Al ejecutar el programa se mostrar la salida estndar en una nueva vista en la regin inferior:

Si deseamos ejecutar de nuevo nuestro programa, el icono Run As ofrecer directamente el fichero hola.py como ejecutable. Es posible controlar diferentes aspectos de un ejecutable Python. Desde Run AsRun Configurations, tanto en el men contextual sobre hola.py como desde el icono verde con la flecha, llegamos a un dilogo como ste:

11

La pestaa Main permite fijar el mdulo principal del proyecto (el ejecutable). La pestaa Arguments da la opcin de pasar argumentos de lnea de rdenes en la ejecucin del programa (til si queremos poner a prueba un script, por ejemplo). Ahora que hemos aprendido a crear, editar y ejecutar un proyecto sencillo, vamos a familiarizarnos con otros elementos del entorno.

Vistas
El banco de trabajo est dividido en varias regiones, cada una de las cuales alberga vistas (views). En la perspectiva Pydev, las vistas son: Explorador de paquetes (Pydev Package Explorer): en la zona izquierda. Editores de texto: en la zona central. Esquema (Outline): en la zona derecha. Problemas y Consola (Console): en la zona inferior.

Tener desplegadas todas las vistas puede limitar mucho la superficie disponible para la zona de trabajo del editor de texto, que es donde pasamos la mayor parte del tiempo. Podemos reducir las vistas al mnimo y ampliar la zona de edicin de texto con Ctrl-M. El banco de trabajo pasa a tener este aspecto:

12

Las regiones son ahora una barras de iconos verticales u horizontales cuyo primer icono tiene este aspecto: . Cada icono en la barra es una vista minimizada. Si hacemos clic en uno de esos iconos, la vista se despliega, pero se ocultar tan pronto hagamos clic en otra regin. Para restaurar el aspecto inicial del banco de trabajo basta con pulsar nuevamente Ctrl-M. Las vistas se pueden extraer de la ventana principal y residir en una ventana propia. Si arrastramos la pestaa fuera de la ventana, se crear una ventana nueva:

Arrastrando la pestaa (no la barra de la ventana!) podemos reubicar la vista en su lugar original.

Se habr observado que la vista se puede poner en cualquier lugar del banco de trabajo, y no slo en su lugar original. Si ubicamos la vista en una ventana determinada, podemos hacerlo creando una regin nueva o aadindola a una regin ya existente. En el segundo caso, la vista visible en la regin se seleccionar pinchando en la pestaa. Este grfico muestra el aspecto de la regin que contena inicialmente a Outline tras aadirle el explorador de paquetes:

En cada regin no minimizada hay dos iconos de control de apariencia: : minimizar la regin y mostrarla como una barra de iconos. : maximizar la regin para que ocupa la prctica totalidad del banco de trabajo a la vez que minimiza las dems regiones. , que restaura el aspecto del banco de

13

Si la regin est maximizada, el ltimo icono se sustituye por este otro, trabajo.

Hay ms vistas que las que vemos por defecto. Hay una vista, por ejemplo, para mostrar los resultados de una vista. Podemos activarla con el men WindowShow ViewSearch. Esta vista se muestra (inicialmente) en la regin inferior.

Ahora hablaremos brevemente de bsquedas en Eclipse.

Bsquedas y reemplazamientos
Eclipse distingue entre bsquedas de texto en el propio fichero y bsquedas en el proyecto. Las bsquedas en el propio fichero estn accesibles con el men EditFind/Replace o Ctrl-F y llevan a este dilogo:

No se trata de un dilogo modal: podemos mantener la ventana abierta mientras editamos. Si escribimos un texto en la caja etiquetada con Find: y pulsamos el botn Find, se selecciona el texto en cuestin en el editor de texto activo. (Ojo: hay que seleccionar la direccin de bsqueda apropiada.) Podemos ver que hay opciones para hacer la bsqueda sensible a la caja, con expresiones regulares, etctera. Tambin es posible reemplazar el texto encontrado. El botn Find permite ir saltando de aparicin del texto en aparicin del texto. Alternativamente podemos usar CtrlK. O para ir en direccin al inicio, Ctrl-Shift-K. Ntese que hay en el formulario dos campos de texto: el del texto que deseamos buscar y el del texto por el que deseamos hacer un reemplazamiento. Los reemplazamientos no atienden a la estructura sintctica del texto, sino slo a su realidad como cadenas de caracteres. Estos reemplazamientos deseables y ms inteligentes se estudiarn en este documento ms adelante, cuando hablemos de refactorizacin. El segundo tipo de bsqueda en Eclipse se refiere a texto en cualquier fichero de un espacio de trbajo. Si hemos hecho aparecer ya la vista Search no mostrar ms que un mensaje advirtiendo de que no hay nada encontrado porque nada se ha buscado y un enlace al dilogo de bsquedas. Pinchemos en el enlace y escribamos print (sin comillas) en la caja de texto buscado.

14

Tras pulsar el botn Search, se mostrarn en un rbol todos los ficheros que contienen ese texto. En nuestro caso, slo hay uno:

Si hacemos doble-clic en la lnea con la flecha amarilla de la vista Search, la que seala la ocurrencia del texto en una lnea de un fichero, el cursor saltar automticamente a la ubicacin de esa lnea en hola.py.

Preferencias
En Window>Preferences tenemos acceso a un dilogo para fijar nuestras preferencias, tanto de Eclipse en general como de sus extensiones. Vamos a cambiar el tipo de letra, que es uno de los elementos configurables. En Linux, Lucida Sans Typewriter es ms legible que Courier para editar cdigo. En Microsoft Windows, Consolas es la fuente tipogrfica preferible (descargable gratuitamente de Microsoft si no se tiene ya instalada en el sistema). En Apple Mac OS X, Menlo resulta apropiada para la edicin de programas. En el dilogo Preferences seleccionamos el formulario GeneralAppearanceColors and Fonts. En el rbol de opciones seleccionamos BasicText Font y pulsamos Edit Se abre un cuadro de seleccin de fuente y elegimos Consolas (o Menlo, o Lucida Sans Typewriter). Ya est: hemos cambiado el tipo de letra para las vistas del editor de texto o la Consola. Desde Preferences podemos controlar muchos aspectos del entorno. Vale la pena explorar las diferentes opciones. Muchas son fciles de entender por la simple descripcin que ofrecen sus nombres.

15

Problemas
La vista Problems informa al programador de los errores detectados en los ficheros. Si tratamos de importar un mdulo inexistente, Pydev lo detecta y advierte del error en Problems:

En Problems se notifican ahora dos problemas: un error (crculo rojo con aspa blanca) y un aviso (tringulo amarillo con signo de exclamacin). Si desplegamos el mensaje de cada uno de ellos tendremos ms informacin:

El error no slo es notificado en Problems. El margen izquierdo de la lnea problemtica contiene un icono especial, idntico al de error en Problems: . Si ponemos el cursor sobre el icono Eclipse muestra una pista (tooltip) con dos lneas de texto: Unused import: pepe Unresolved import: pepe Y en el margen derecho tenemos otro sistema de deteccin de problemas: la barra derecha es como un mapa del cdigo a escala. Sobreimpresas en ella aparecern marcas que indican la altura a la que se ha detectado un problema. El cuadro rojo arriba del todo seala que hay algn problema en el fichero. El rectngulo rosado advierte de un problema y, como hemos dicho, aparece a la altura proporcional del cdigo.

16

Si ponemos el cursor sobre el rectngulo rosado se muestra una pista y si pulsamos en l, saltamos automticamente a la lnea con el problema.

Tareas
Al editar cdigo es frecuente sealar las tareas pendientes con comentarios especiales. Por ejemplo, se usa TODO (por to do) para indicar que hay una tarea pendiente, o FIXME (por fix me) para sealar que hay un error que debera ser corregido. Eclipse puede mostrar un resumen con todos los comentarios especiales que usamos para sealar tareas pendientes. Abrimos en primer lugar la vista de tareas Tasks (men WindowShow ViewTasks). En el editor de texto escribimos un comentario como este: # TODO: Mejorar la cabecera. Salvamos a continuacin el fichero (Ctrl-S) y observamos que en Tasks aparece una tarea pendiente:

Si hacemos doble-clic en la tarea, el cursor se desplaza automticamente al fichero y lnea del comentario. Adems, en la barra izquierda aparece un icono que representa una tableta con una marca:

Por defecto hay dos marcas que se reconocen como tareas: TODO: y FIXME: (arrglame). Podemos aadir nuestras propias marcas en WindowPreferencesPydevTask Tags. Hay un sistema mucho ms potente para la gestin de tareas. Se denomina Mylin y se est convirtiendo en una herramienta estndar en Eclipse. Permite crear tareas que no son meros comentarios, asignarlas a programadores, almacenarlas en bases de datos que comparte un equipo de desarrollo, crear contextos que permiten centrar el entorno en la tarea que hay que resolver y se integra con sistemas de gestin de proyectos. Se trata de un sistema mucho ms complejo que el que hemos presentado, pero que se est implantando con fuerza. Se puede obtener ms informacin en http://www.eclipse.org/mylyn.

17

Consola interactiva
Una de las ventajas del desarrollo con Python es disponer de un entorno REPL (Read-Eval-Print-Loop) para explorar rpidamente la viabilidad de alguna idea o, sencillamente, para disponer siempre de una calculadora compleja. Eclipse permite disponer de varias consolas en la vista Console, una o ms de las cuales puede ser una consola interactiva. Para activarla, vamos al icono que parece una ventana con una cruz encima en la barra de la vista Console y desplegamos el men al que da acceso:

El men tiene una opcin Pydev console. Al seleccionarla se muestra este dilogo:

Al darle al botn OK se crea una consola nueva:

En ella podemos interactuar con un intrprete de Python:

Podemos crear varias consolas y seleccionar cada una de ellas con el icono que representa una pantalla de ordenador.

Ayuda en lnea
Al desarrollar es frecuente que necesitemos consultar manuales para saber, por ejemplo, qu argumentos necesita una funcin. En Pydev podemos obtener ayuda dejando el puntero del ratn sobre el identificador de la funcin en cuestin. La ayuda que ofrece Eclipse con Pydev no es mucha. Al posar el puntero sobre print en el editor de textos, aparece una pista con este texto:
print Found at: builtins print(value, ..., sep=' ', end='\n', file=sys.stdout) Prints the values to a stream, or to sys.stdout by default. Optional keyword arguments: file: a file-like object (stream); defaults to the current sys.stdout. sep: string inserted between values, default a space. end: string appended after the last value, default a newline.

18

No hay ms ayuda en lnea que esa. Si deseamos tener manuales al alcance de la mano, hemos de recurrir a Internet o al manual que se distribuye con Python. En Windows encontraremos un fichero en formato chm en C:\Python31\Doc\python311.chm (el nmero, que aqu es 311, depende de la versin exacta de Python, que en mi caso es la 3.1.1).

Mdulos, edicin asistida y el problema de la codificacin del fichero


Vamos a aadir mdulos al proyecto. En primer lugar, una clase que representa a una persona. La clase mantendr el nombre y el apellido de una persona y un mtodo nos permitir obtener el nombre completo. Vamos a FileNewPydev Module, dejamos el campo Package en blanco y ponemos en el campo Name el texto persona (sin comillas y, aunque se trata de un fichero Python, sin la extensin .py). Seleccionamos a continuacin la opcin Module: Class y pulsamos el botn OK. Se habr creado un nuevo fichero que podemos ver en el explorador de paquetes y una nueva pestaa con un editor de textos para ese fichero.

El fichero contendr ya texto que proviene de una plantilla (Module: Class). La plantilla contiene un texto similar a ste:
''' Created on 31/05/2010 @author: amarzal ''' class MyClass (object): ''' classdocs '''

def __init__(selfparams): ''' Constructor '''

Se puede navegar hacia adelante, por los elementos recuadrados, con la tecla Tab y hacia atrs con Shift-Tab. En cada cuadro podemos cambiar el contenido tecleando un texto nuevo. Editamos el contenido para que la clase pase a denominarse Persona. La herencia de object es innecesaria en Python 3.1. Eliminamos las cadenas de documentacin y definimos bien el constructor y un mtodo adicional. La clase pasa a quedar as:
class Persona: def __init__(self, nombre, apellido): self.nombre = nombre self.apellido = apellido def nombre_completo(self): return self.nombre + " " + self.apellido

19

Al editar el texto el editor nos habr ayudado de diferentes formas: Cuando empezamos a escribir una palabra, un men nos ofrece identificadores y palabras clave que podemos elegir para ahorrar tecleo. Si el men de compleciones no est disponible, podemos autocompletar cualquier identificador pulsando Ctrl-Space. Cuando dejamos el cursor en un identificador, se resaltan todas las apariciones de ese identificador en el mtodo en el que nos encontremos. Al escribir la cabecera de la definicin de un mtodo y teclear el parntesis abierto, el editor completa la lnea con el texto self): y deja el cursor entre self y el parntesis cerrado para que escribamos los parmetros. Al finalizar una lnea y pulsar el retorno de carro, el editor sita el cursor con un sangrado que suele ser el deseado. La tecla de borrad al principio de la lnea retrocede un tabulador completo (aunque en el texto no hay tabuladores, sino espacios en blanco).

Hay muchas secuencias de teclado que conviene aprender para ser eficientes con el editor. Adems de las comunes (copiar con Ctrl-C, pegar con Ctrl-V, cortar con Ctr-X, deshacer con Ctrl-Z) podemos destacar: Ctrl-D para borrar una lnea entera. Ctrl-4 para meter un bloque de comentario (estilo #===).

Ctrl-Shift-4 para meter una lnea de separacin como comentario (estilo #---). Ctrl-Space para obtener asistencia. Ctrl-Left, Ctrl-Right para desplazarse palabra a palabra. Ctrl-Delete para eliminar una palabra en lugar deun carcter.

Hay muchas ms combinaciones de teclas interesantes. Se puede consultar el listado en HelpKey Assist o pulsando Ctrl-Shift-L. Sigamos con el proyecto. Vayamos al programa hola.py y modifiquemos el contenido para que quede as:
p = Persona("Pepe", "Lpez") print("Hola, " + p.nombre_completo() + "!")

Hay un problema: el editor de texto no sabe a qu corresponde el identificador Persona y lo marca como error. Podemos corregir el problema con ayuda de Pydev. Ponemos el cursor en Persona y pulsamos Ctrl-1. Aparece un men con dos posibles soluciones: Import Persona (persona), que significa que hay que completar el mdulo con la importacin de Persona del mdulo persona.py. @Undefined Variable, que significa que se aadir un comentario para que Pydev ignore el error y no lo marque en el editor de textos.

Seleccionamos la primera opcin y Pydev deja el texto as:


from persona import Persona p = Persona("Pepe", "Lpez") print("Hola, " + p.nombre_completo() + "!")

20

La funcionalidad que invocamos con Ctrl-1 para corregir un error recibe el nombre de QuickFix. Podemos usar Ctrl-1 tambin en zonas de cdigo sin errores y sugiere acciones que el editor puede ejecutar. Esta funcionalidad vinculada a la misma secuencia de teclas se denomina QuickAssist. Pongamos a prueba QuickAssist: vayamos a persona.py y borremos el cuerpo del constructor. La clase quedar as:
class Persona: def __init__(self, nombre, apellido): def nombre_completo(self): return self.nombre + " " + self.apellido

A continuacin, pongamos el cursor en la cabecera del constructor y pulsemos Ctrl-1. Aparece un men con dos opciones: Assign parameters to attributes Make docstring

Escojamos la primera. La clase quedar as:


class Persona: def __init__(self, nombre, apellido): self.nombre = nombre self.apellido = apellido def nombre_completo(self): return self.nombre + " " + self.apellido

La asignacin de parmetros a atributos es muy frecuente y QuickAssist nos ayuda. Ahora volvamos a poner el cursor en la cabecera del constructor, pulsemos Ctrl-1 y elijamos la segunda opcin. El texto pasa a ser:
class Persona: def __init__(self, nombre, apellido): ''' @param nombre: @param apellido: ''' self.nombre = nombre self.apellido = apellido def nombre_completo(self): return self.nombre + " " + self.apellido

Pydev ha preparado una cadena de documentacin para el constructor. Se puede obtener informacin sobre este formato en http://www.learningpython.com/2010/01/08/introducing-docstrings. Dejemos el texto como estaba antes de la cadena de documentacin. Imaginemos ahora que queremos ver la definicin del mtodo nombre_completo. Si pinchamos en el identificador con el ratn y pulsando a la vez la tecla Ctrl, iremos directamente a la definicin. (Con la tecla Ctrl los identificadores se convierten en hiperenlaces.) Si deseamos volver al punto en el que estbamos podemos pinchar en el icono de la flecha amarilla a la izquierda o teclear Alt-Left. Pydev memoriza los puntos por los que nos hemos desplazado y permite navegar por ellos con Alt-Left y Alt-Right. Tratemos de ejecutar el programa. Seleccionamos hola.py y pulsamos F9. Obtenemos un error en la consola:
File "C:\Users\amarzal\workspace\HolaMundo\src\hola.py", line 8 SyntaxError: Non-UTF-8 code starting with '\xf3' in file C:\Users\amarzal\workspace\HolaMundo\src\hola.py on line 8, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

21

Es un error debido a la codificacin del texto. Hemos usado una letra con tilde y la apertura de exclamacin, con lo que Python se queja de que esos caracteres no son aceptables en la codificacin por defecto. Al trabajar con Windows, el fichero se est codificando con Cp1252 y Python 3.1 asume una codificacin por defecto UTF-8. En Linux ocurrir probablemente algo parecido, pero con la codificacin Latin1 o ASCII. Tenemos dos soluciones: Poner un comentario en la primera lnea que indique que la codificacin es Cp1252: #coding: cp1252 Cambiar la codificacin del fichero por UTF-8. Para ellos seleccionamos EditSet Encoding y elegimos la opcin UTF-8. Al hacerlo el texto se convierte en esto:
''' Created on 31/05/2010 @author: amarzal ''' from persona import Persona p = Persona("Pepe", "Lpez")

print("Hola, " + p.nombre_completo() + "!")

con lo que hemos de sustituir los smbolos extraos por los correctos. Es recomendable escoger la segunda opcin. Para no tener que hacer esto manualmente con cada fichero que creemos, es recomendable fijar la codificacin por defecto para el proyecto entero. Con el men contextual en la carpeta del proyecto seleccionamos Properties. En la entrada Resource tenemos este cuadro:

Podemos fijar la codificacin a UTF-8 eligiendo Other y la entrada correspondiente en el men desplegable. Ahora que hemos corregido el problema, ejecutamos el programa hola.py con F9 y obtenemos esta salida por consola:
Hola, Pepe Lpez!

22

Acabamos esta seccin dedicada a la edicin con un par de observaciones. La primera, que el editor permite plegar y desplegar el cdigo. En la barra izquierda aparecen unos smbolos que al ser pulsados pliegan el cuerpo de una estructura sintctica: cadena de documentacin, definicin de clase, definicin de mtodo, etctera. La segunda es la ayuda a la navegacin que supone el explorador de paquetes, que cuando contiene definiciones de objetos elaborados, como clases, se puede desplegar ms all del fichero y mostrar las definiciones de objetos relevantes:

Ms adelante veremos cmo fijar una preferencia para que futuros proyectos creados con Eclipse adopten por defecto la codificacin UTF-8.

Refactorizacin de cdigo
En los ltimos aos se ha implantado el concepto de refactorizacin de cdigo, que es la reescritura de parte o de toda la aplicacin para aumentar su legibilidad. Acciones como renombrar una variable, por ejemplo, requieren de

ms conocimiento del dominio del que proporciona un reemplazador de texto convencional (como el que podemos invocar con EditFind/Replace o con SearchSearch). El renombrado inteligente de variables es una de tantas acciones de refactorizacin posibles. (Para saber ms de este tema puede consultarse el libro reseado en http://martinfowler.com/books.html#refactoring, de Martin Fowler.) Los entornos de desarrollo integrados, como Eclipse, ofrecen herramientas de refactorizacin. Vamos a usar una para renombrar los campos de la clase persona. El campo nombre pasar a denominare _nombre y el campo apellido, _apellido. Ponemos el cursor sobre la palabra nombre en una aparicin cualquiera de self.nombre y pulsamos el botn derecho del ratn. En el men contextual elegimos RefactoringRename Puede que nos solicite entonces guardar todos los cambios, a lo que respondemos que de acuerdo. En el dilogo aparece el identificador nombre y lo editamos para que se lea _nombre". Para estar seguros de que todo va bien, podemos pulsar en Preview> y el dilogo nos mostrar el antes y el despus de la refactorizacin.

23

Podemos pulsar en OK y los cambios se efectuarn. Repetimos el proceso con el otro campo y ya hemos completado la tarea. La refactorizacin permite transformar el cdigo con mayor seguridad de la que tendramos haciendo los cambios a mano. Practiquemos alguna ms. Definimos un mtodo que extrae las iniciales:
class Persona: def __init__(self, nombre, apellido): self._nombre = nombre self._apellido = apellido def nombre_completo(self): return self._nombre + " " + self._apellido def iniciales(self): return self._nombre[0] + ". " + self._apellido[0] + "."

No nos gusta la expresin con la que formamos la cadena y nos gustara separar la sentencia en otras que se apoyen en variables locales. Seleccionamos el texto self._nombre*0+ + . y en el men Refactoring escogemos Extract Local Variable Nos pide un nombre de variable. Introducimos ini1 y pulsamos OK. El texto del mtodo se transforma en

def iniciales(self): ini1 = self._nombre[0] + ". " return ini1 + self._apellido[0] + "."

Hacemos ahora esto mismo con _apellido*0+ + . y pasamos a tener


def iniciales(self): ini1 = self._nombre[0] + ". " ini2 = self._apellido[0] + "." return ini1 + ini2

Podemos efectuar la operacin contrario ahora. Con el cursor en la aparicin de ini1 en la expresin de la sentencia return seleccionamos RefactoringInline Local Variable, pulsamos OK y pasamos al texto
def iniciales(self): ini2 = self._apellido[0] + "." return self._nombre[0] + ". " + ini2

Hay ms opciones de refactorizacin, pero con estas bastan para hacernos una idea de las posibilidades. Los ejemplos empleados son excesivamente sencillos. El valor de la refactorizacin slo se aprecia bien cuando se trabaja en proyectos medianos o grandes, con decenas o centenares de ficheros interrelacionados. Hay que decir, de todos modos, que Python es un lenguaje difcil de refactorizar por sus caractersticas dinmicas. Lenguajes como Java ofrecen ms herramientas de refactorizacin y stas suelen ser ms fiables. La mayor verbosidad de Java hace que estas herramientas sean imprescindibles para el trabajo cotidiano.

Control de versiones
La programacin, tanto si es en equipo como individual, obliga a gestionar un conjunto de ficheros que evolucionan en el tiempo. Es importante ser capaz de recuperar versiones anteriores o comparar dos versiones de un mismo fichero para saber dnde se han producido cambios, ya que estos pueden ser responsables de la introduccin de un defecto en el programa. Eclipse tiene soporte integrado para CVS, un sistema de versiones que ha quedado un tanto obsoleto. Durante aos, Subversion (tambin conocido por SVN) fue ganando terreno a CVS y hasta hace poco era el estndar de facto en sistemas de control de versiones. Subversion parte de la idea de que hay un repositorio con el cdigo fuente del proyecto que es atacado por todos los programadores. Tpicamente, los programadores descargan la ltima versin del cdigo, introducen cambios y los dan de alta para que estn disponibles para el resto de programadores. Cada vez que se quiere dar de alta un conjunto de cambios, el programador est obligado a sincronizarse con el repositorio, es decir, a descargar los cambios que otros programadores pueden haber dado de alta mientras uno trabajaba aisladamente para modificar su versin. Si cada programador ha trabajado en ficheros distintos o en regiones diferentes del mismo fichero, SVN sabe fundir directamente los nuevos cambios con la versin actualizada del repositorio. Pero cuando eso no es posible, SVN avisa del conflicto y obliga al programador a fundir manualmente sus cambios con la ltima versin del repositorio y a sealar que ha resuelto el conflicto. Desde hace unos pocos aos SVN est vindose desplazado por sistemas de control de versiones que adoptan una filosofa distinta. Son los denominados sistemas de control de versiones distribuidos (DCVS, por Distributed Control Version System) y los dos ms extendidos son Git y Mercurial. Git, desarrollado inicialmente por Linus Torvalds, es algo ms complejo de usar que Mercurial, que est escrito en Python casi ntegramente. Git tambin es menos portable: depende de POSIX y eso dificulta la ejecucin en Microsoft Windows. No ocurre lo mismo con Mercurial. Muchas forjas de software libre ofrecen hospedaje de proyectos y control de versions con los sistemas Git o Mercurial.

24

Los sistemas DCVS no asumen la existencia de un repositorio central. La versin del proyecto que tiene cada usuario es una versin ntegra del proyecto y arrastra consigo toda la historia de cambios sufridos: es, pues, un repositorio completo. El programador puede hacer cambios y registrarlos localmente, es decir, slo en su copia del proyecto. Pero en algn momento tendr que compartir sus cambios con el resto del equipo. Para compartir los cambios con otros programadores puede adoptar diferentes soluciones: Generar un fichero de parches y entregarlo fsicamente a los compaeros del equipo (o enviarlo por correo). Poner su copia del proyecto al alcance de los dems arrancando temporalmente algn servidor local al que los dems pueden conectarse. Asumir que existe un repositorio central por convenio y registrar en l los cambios.

La flexibilidad que ofrecen los sistemas DCVS es la clave de su xito. Con SVN, un usuario sin conexin a red no poda dar de alta cambios, ya que stos slo se pueden registrar en el repositorio central. Con un sistema DCVS, puede ir registrando los cambios en su copia de trabajo y, cuando tenga conexin o considere que los cambios merecen ser compartidos, registrar los cambios en el repositorio central por convenio. Los sistemas DCVS tambin facilitan la divisin del grupo de trabajo en subgrupos que exploren posibilidades nuevas en el proyecto y que, si stas no llevan a ninguna parte, no acaban afectando en modo alguno a la base de cdigo compartida por todos. Nosotros vamos a usar ahora brevemente Mercurial, que se puede integrar fcilmente en Eclipse. Lo primero es instalar Mercurial. En Linux es seguro que hay un paquete con Mercurial. Para Windows, podemos ir a la pgina web http://mercurial.selenic.com/ y descargar el paquete de instalacin (la versin ms reciente es la 1.5.3). Si se desea usar Mercurial desde la lnea de rdenes, hay un tutorial fantstico en http://hginit.com/. Para trabajar con Eclipse no hace falta, pues hay una extensin de Eclipse que integra Mercurial y lleva sus propios binarios. La pgina web del proyecto MercurialEclipse, que es como se llama, est en la pgina web http://javaforge.com/project/HGE. Instalamos la extensin yendo a HelpInstall New Software, pinchando en Add y escribiendo Mercurial en Name y http://cbes.javaforge.com/update en Location. Basta con escoger MercurialEclipse de la lista de paquetes que se ofrecen. Tras la instalacin, conviene reiniciar Eclipse. Hemos de empezar por indicar que el proyecto va a estar sujeto a control de versiones con Mercurial. Con el men contextual de HolaMundo, vamos a TeamShare Project. Aparece un dilogo para elegir el sistema de control de versiones:

25

(Si hubisemos instalado la extensin para Subversion, tambin se mostrara en la lista de tipos de repositorio.)

Escogemos Mercurial y le damos a Next>. El dilogo ahora tiene este aspecto:

El enlace nos permite crear el repositorio en el mismo proyecto o en un lugar diferente. Lo haremos en el propio directorio. Pulsamos Finish y as todo est en el directorio C:\Users\usuario\workspace\HolaMundo. Si examinamos ese directorio veremos que hay un nuevo subdirectorio llamado .hg. Ese subdirectorio contiene todo lo necesario para el control de versiones. (Subversion creaba un subdirectorio propio en cada directorio del proyecto, lo que dificultaba ligeramente el mantenimiento.) Podemos ver que todo ha ido bien porque el explorador de paquetes muestra los iconos de carpeta y los ficheros presentas nuevos iconos.

26

Aunque hemos creado el repositorio, est vaco. Los iconos de hola.py y persona.py tienen un interrogante porque el repositorio no sabe nada de ellos. Las carpetas tienen un asterisco porque presentan cambios en su contenido con respecto a lo que el repositorio sabe (que no es nada por el momento). Y la carpeta principal tiene una marca [new]. Hemos de dar de alta esta primera versin del proyecto en nuestro repositorio. El men contextual de HolaMundo, en su entrada Team, tiene ahora un montn de opciones, ya que el proyecto est bajo control de versiones. Empezamos por seleccionar Commit Aparece un nuevo dilogo para seleccionar los ficheros que queremos someter a control y espacio para un comentario explicativo:

Marcamos todos los ficheros que queremos controlar (en este caso vamos a por todos), escribimos como mensaje de alta el texto Primera versin de nuestro Hola Mundo. y pulsamos OK. Y ya est. Los ficheros estn registrados y nuestro repositorio concuerda con nuestra copia de trabajo. Los iconos del explorador han cambiado:

27

Adems, HolaMundo tiene a su derecha una marca extraa. Mercurial asocia a cada versin un identificador ininteligible (y eso se hace as adrede). Hagamos un cambio en hola.py: Renombremos la variable p por q. Al salvar, el explorar detecta que ha habido cambios:

Podemos comparar la versin de trabajo de hola.py con la del repositorio. Vamos al men contextual de hola.py y elegimos Compare WithParent Changeset. En la zona central aparece una nueva pestaa con una utilidad de comparacin de versiones:

Si queremos dar por buenos los cambios, vamos al men contextual (del fichero, de la carpeta src o de HolaMundo) y seleccionamos TeamCommit Una vez hecho, podemos consultar la historia de cambios con TeamShow History. Aparecer una nueva vista en la que consultar los cambios.

28

La opcin Commit guarda siempre en el repositorio local. Para hacerlo en uno externo (de otro programador o el que se decida por convenio que es el central) hay que usar Push. No abundaremos ms en Mercurial, pues dominarlo requiere leer algn manual. Es muy recomendable empezar por http://hginit.com/ y leer despus, si se quiere saber ms, el libro gratuito Mercurial: The Definitive Guide, disponible en http://hgbook.red-bean.com/.

Libreras
Los espacios de trabajo agregan proyectos relacionados. El nuestro slo contiene uno. Vamos a aadir otro proyecto y a hacer que uno use a otro. Aadimos un proyecto Pydev al que denominamos Edades con FileNewPydev Project, sin olvidar fijar la gramtica a Python 3.0 y asegurarnos de que el intrprete es Default o Python 3.1. Ya puestos, vamos a fijar la

codificacin de los ficheros del proyecto a UTF-8: en el men contextual de Edades seleccionamos Properties y en text file encoding marcamos Other para seleccionar a continuacin UTF-8. Podemos ahorrarnos en el futuro tener que hacer esta operacin para cada proyecto si vamos a WindowPreferences y en Workspace fijamos el valor de Text file encoding a Other y seleccionamos UTF-8. Sigamos con la librera. En la carpeta src de Edades creamos un nuevo mdulo llamado edades (en un fichero edades.py) con este texto como contenido:
import datetime import time class FechaNacimiento: def __init__(self, dia, mes, ao): self._nacimiento = datetime.date(ao, mes, dia) def edad(self): n = time.localtime() now = datetime.date(n.tm_year, n.tm_mon, n.tm_mday) aos = now.year - self._nacimiento.year - 1 if now.month >= self._nacimiento.month and \ now.day >= self._nacimiento.day: aos += 1 return aos

Y ahora vamos a enriquecer la clase Persona de HolaMundo para que tenga tambin la fecha de nacimiento y se le pueda demandar la edad. Si vamos directamente a persona.py y escribimos
import edades

29

Eclipse detecta un error, pues no encuentra el mdulo edades. Hemos de indicar a HolaMundo que debe enlazar con Edades. Vamos Properties en el men contextual de HolaMundo y seleccionamos Project References. All aparece un listado de proyectos (en nuestro caso slo aparece Edades) a los que podemos hacer referencia desde HolaMundo. Marcamos Edades y pulsamos OK.

Volvemos a persona.py y comprobamos que ya no se marca error en la importacin de edades. Dejamos as el contenido del fichero:
import edades

class Persona: def __init__(self, nombre, apellido, dia, mes, ao): self._nombre = nombre self._apellido = apellido self._nacimiento = edades.FechaNacimiento(dia, mes, ao) def nombre_completo(self): return self._nombre + " " + self._apellido def iniciales(self): ini2 = self._apellido[0] + "." return self._nombre[0] + ". " + ini2 def edad(self): return self._nacimiento.edad()

Y ahora vamos a hola.py para crear una instancia con el constructor que hemos modificado y usar el mtodo edad:
from persona import Persona q = Persona("Pepe", "Lpez", 6, 12, 1980) print("Hola, " + q.nombre_completo() + "!") print("Tienes " + str(q.edad())+ " aos.")

Ejecutamos (F9) y obtenemos esta salida:


Hola, Pepe Lpez! Tienes 29 aos.

30
Depuracin
Para depurar un programa tenemos un modo de ejecucin especial que abre su propia perspectiva si es necesario. Introduzcamos un error deliberadamente y vamos cmo podemos explorar el entorno de ejecucin durante la depuracin. Vayamos a edades.py y cambiemos el orden ao, mes y da en la sentencia
self._nacimiento = datetime.date(ao, mes, dia)

que pasa a ser


self._nacimiento = datetime.date(dia, mes, ao)

Si ejecutamos sin ms (F9 en hola.py), el programa lanza una excepcin:


Traceback (most recent call last): File "C:\Users\amarzal\workspace\HolaMundo\src\hola.py", line 8, in <module> q = Persona("Pepe", "Lpez", 6, 12, 1980) File "C:\Users\amarzal\workspace\HolaMundo\src\persona.py", line 13, in __init__ self._nacimiento = edades.FechaNacimiento(dia, mes, ao) File "C:\Users\amarzal\workspace\Edades\src\edades.py", line 12, in __init__ self._nacimiento = datetime.date(dia, mes, ao) ValueError: day is out of range for month

Ntese que las referencias a lneas en la pila de llamadas son hiperenlaces que permiten ir directamente al fichero fuente. En cualquier caso, es demasiado tarde para explorar el entorno: la ejecucin ha finalizado. Vayamos paso a paso.

Empezamos por poner un punto de parada (breakpoint) en hola.py, donde se crea q. Para ello vamos al margen izquierdo de esa lnea y con el men contextual seleccionamos Add Breakpoint. Aparecer una marca verde en el margen:

Para ejecutar ahora en modo depuracin podemos: Pulsar F11. Ir a RunDebug. Abrir el men del icono y seleccionar el fichero en el que deseamos que se inicie la ejecucin.

Al empezar a depurar Pydev nos solicita un cambio de perspectiva para cuando se suspenda la ejecucin:

31
Le decimos que s (y memorizamos la decisin). El programa suspende la ejecucin en el punto de parada y la perspectiva pasa a ser Debug. La disposicin de las vistas cambia:

Arriba a la izquierda est la vista Debug con los mdulos activos. A la derecha tenemos dos vistas: Variables y Breakpoints. Los editores aparecen en la zona central, a mano izquierda. Aparece resaltada la lnea en la que se ha detenido la ejecucin. A esa altura, pero a la derecha, se muestra la vista Outline, con una representacin en rbol de los objetos creados en el mdulo. En la zona inferior tenemos la consola. Podemos ejecutar paso a paso con ayuda de atajos de teclado y de los iconos , que estn en Debug:

32

F5 (primer icono): da un paso al interior (step into). Ingresa en la primera funcin llamada y detiene la ejecucin. F6 (segundo icono): salta la llamada (step over). Pasa a la siguiente lnea y se detiene en ella, pero no en el cuerpo de las funciones posiblemente invocadas en la lnea actual. F7 (tercer icono): sale de la funcin (step return). Vuelve al punto en el que se invoc a la funcin o mtodo actualmente en ejecucin.

Avanzamos con F5 y entramos as en el constructor de Persona. Con F5 avanzamos y entramos finalmente en el constructor de FechaNacimiento. Para consultar el valor de una variable podemos seleccionar su identificador y, con el men contextual, seleccionar Display. Se abrir una ventana temporal con el valor de la variable. Si queremos vigilar constantemente su valor, conviene selecconar Watch. En ese caso se abrir una vista Expressions con el valor. Si hacemos eso con dia en la lnea
self._nacimiento = datetime.date(dia, mes, ao)

La vista Expressions contendr esto:

Podemos seguir ejecutando paso a paso hasta provocar nuevamente la excepcin. O hacerlo de un tirn con el icono de Debug. Y en cualquier momento podemos detener el proceso de depuracin pulsando en el cuadrado rojo (o pulsando Ctrl-F2). Al acabar la depuracin la perspectiva no vuelve a su estado anterior y queda como Debug. Para volver podemos seleccionar la perspectiva en la zona superior derecha del banco de trabajo o pulsar Ctrl-F8.

Actualizacin de las extensiones y del propio Eclipse


Cada cierto tiempo se van actualizando las extensiones que hemos instalado en sus lugares de origen. Para que nuestra copia de Eclipse se mantenga al da, cada cierto tiempo hay que ejecutar HelpCheck for Updates. La operacin puede ser relativamente lenta si hay muchas extensiones cargadas, por lo que conviene ejecutarla de tarde en tarde o cuando se sabe que hay una actualizacin importante.

Creacin y cambio de espacios de trabajo


Todo lo que hemos hecho ha ocurrido en un nico espacio de trabajo: workspace. Podemos trabajar con ms de uno. Para crear un espacio nuevo hacemos lo siguiente: Seleccionamos FileSwitch WorkspaceOther En el campo Workspace apuntamos a una carpeta vaca o inexistente. En el segundo caso, Eclipse nos la crear automticamente. Eclipse se reiniciar automticamente con el nuevo espacio de trabajo (que no tendr proyecto alguno).

33

A partir de ahora podremos alternar entre los dos espacios con FileSwitch Workspace o incluso arrancar dos instancias de Eclipse y hacer que cada una acceda a un espacio de trabajo diferente.

Pruebas unitarias
Tener garantas de que el software no contiene defectos es una prioridad absoluta en el desarrollo. Muchos programadores, de toda la vida, han acompaado el cdigo de sus libreras con pequeos programas de prueba que se aseguran de que el cdigo desarrollado proporciona ciertos valores ante determinadas entradas. En los ltimos aos el concepto ha derivado en una coleccin de utilidades y una serie de buenas prcticas que sistematizan el diseo de pruebas que se pueden ejecutar y validar automticamente. Las pruebas deben formar parte de la base de cdigo de toda librera o programa. En muchos equipos de desarrollo hay un acuerdo por el que un programador slo puede dar de alta en el repositorio una versin modificada del cdigo si ste supera exitosamente todas las pruebas. Es importante que las pruebas sean rpidas y ejecutables automticamente. De lo contrario, los programadores rehuirn su ejecucin. La automatizacin es importante para utilizacin por sistemas de integracin continua. Estos sistema comprueban (tpicamente cada noche) que el proyecto es compilable y supera todas las pruebas unitarias (y posiblemente otras). Si algo falla, un informe registra los problemas y un sistema de avisos informa a los programadores. Nos vamos a centrar ahora en las denominadas pruebas unitarias. Una prueba unitaria (unit test) es un trozo de cdigo (tpicamente un mtodo) que invoca a otro trozo de cdigo y comprueba la correccin de algunas asunciones acerca de los resultados que proporciona para cierta entrada. Si las asunciones no se observan, se dice que la unidad de prueba ha fallado.

Una unidad (unit) es, tpicamente, un simple mtodo o funcin. El sistema sometido a pruebas recibe el nombre de SUT (System Under Test). El SUT puede ser una clase cuyos mtodos estn siendo puestos a prueba (aunque en el caso de clases hay quien usa el trmio CUT, por Class Under Test). No hemos de confundir pruebas unitarias con otras pruebas que tienen objetivos distintos: prueba de aceptacin (el programa supera una demanda del cliente), prueba de integracin, prueba de regresin, prueba de prestaciones, prueba de carga, prueba de estrs Una prueba unitaria, para considerarse buena, ha de ser automatizable y repetible, fcil de implementar, permanecer en la base de cdigo una vez se ha escrito, ser ejecutable por cualquier desarrollador de un modo sencillo y de ejecucin rpida. Los marcos de trabajo (frameworks) de prueba unitaria simplifican el diseo de las pruebas, proporcionan un entorno para su ejecucin y generan informes con los resultados. El primer marco de trabajo es JUnit y su objetivo es la plataforma Java. El xito del sistema ha hecho que muchas otras plataformas copien buena parte de su funcionalidad y herramientas. As, hay NUnit (para C#), PyUnit (para Python), Test::Unit (para Ruby) EL conjunto de marcos de trabajo inspirados en JUnit recibe el nombre genrico de xUnit. El trabajo con unidades de testeo puede llegar a guiar el propio proceso de desarrollo de software. Hablamos de desarrollo dirigido por las pruebas (TDD, por Test Driven Development) para referirnos a una metodologa que codifica las pruebas unitarias antes que las propias pruebas. El desarrollo TDD es un ciclo iterativo que puede ilustrarse as:

Aadir prueba unitaria

Refactorizar

Ejecutar pruebas y obtener fallos

34

Ejecutar pruebas unitarias

Escribir cdigo

El punto de entrada al ciclo es la etapa Aadir prueba unitaria.

PyUnit
La librera PyUnit viene de serie con Python 3.1. Vamos a desarrollar un demo simplificada de desarrollo TDD e ilustraremos de paso el trabajo con paquetes (packages). Nuestro objetivo ser desarrollar una aplicacin para resolver Sudokus de 4x4, como ste:

Slo daremos ahora los primeros pasos: el diseo de un validador de codificaciones del Sudoku con listas. (Un material ms completo se puede encontrar en http://iparty.aditel.org/?page_id=140.)

La metodologa sugiere empezar por la recoleccin de las denominadas Historias de Usuario, pequeas fichas que describen funcionalidad que el cliente espera de nuestro software. El objetivo es concentrarse en una sola historia de usuario en cada instante. El ejemplo que presentamos no se cie al 100% a ese modelo, pero ilustra bien la metodologa. Empezamos con estas cuatro historias de usuario: 1. Dada una lista con 4 filas de nmeros, saber si describe un Sudoku de 4x4 2. Dada una cadena con 4 lneas de caracteres entre 1 y 4 y asteriscos en posicin libre, obtener la lista que lo describe 3. Resolver automticamente un Sudoku 4. Jugar partidas contra un jugador humano Empezamos por crear un proyecto Pydev al que denominamos Sudoku. En su carpeta src creamos un paquete (package) con FileNewPydev package. Al paquete le llamamos test. Podemos pensar en el paquete como un directorio del proyecto. Nos centramos en la primera historia de usuario. Para la historia hemos de disear ahora pruebas de aceptacin, esto es, pruebas cuya superacin nos permitira concluir que hemos implementado exitosamente la historia de usuario. Las pruebas de aceptacin para la primera historia de usuario se muestran aqu: 1. Dada una lista con 4 filas de nmeros, saber si describe un Sudoku de 4x4 1.1. Rechaza una no lista 1.2. Rechaza lista que no es de 4x4 1.3. Rechaza lista que no contiene 4x4 enteros 1.4. Se suministra una lista de 4x4 con nmeros menores que 0 o mayores que 4 y la rechaza 1.5. Rechaza lista con repetidos en fila 1.6. Rechaza lista con repetidos en columna 1.7. Rechaza lista con repetidos en regin 2x2 Vamos a crear una prueba unitaria para la primera prueba de aceptacin. En el paquete test creamos un mdulo Python del tipo Module: Unittest. Llamaremos al mdulo test_SudokuValidator. El prefijo test_ es un convenio para los mdulos con pruebas unitarias. La plantilla del mdulo es sta:
''' Created on 01/06/2010 @author: amarzal ''' import unittest

35

class Test(unittest.TestCase):

def testName(self): pass

if __name__ == "__main__": #import sys;sys.argv = ['', 'Test.testName'] unittest.main()

Se importa el mdulo estndar unittest y se define una clase que hereda de TestCase, una clase definida en unittest. La plantilla tambin contiene un mtodo que hemos de definir nosotros mismos. La ltima parte es un fragmento que ejecuta las pruebas unitarias si ejecutamos directamente el fichero (pero se suele ejecutar con ayuda de una herramienta, como veremos ms adelante).

Cambiamos el contenido para que quede as:


from unittest import TestCase class TestSudokuValidator(TestCase): def test_sudoku_isNotAList_isRejected(self): validator = SudokuValidator() sudoku = 0 self.assertFalse(validator.check(sudoku), "Acepta una no lista")

Hemos usado la clase SudokuValidator (el SUT) aun cuando no la hemos definido (y Pydev nos seala esto con error). Es lo normal: estamos desarrollando con la metodologa TDD. Los mtodos que son pruebas unitarias deben empezar por test. La ltima sentencia contiene un aserto que debe superarse para que la prueba unitaria se supere con xito. Los asertos estn definidos por herencia de TestCase. Es evidente que el SUT no puede pasar la prueba: ni siquiera existe. Vamos a ver cmo falla. En test_SudoluValidator.py accionamos el men contextual y seleccionamos Run AsPython unit-test. En la consola obtenemos este mensaje:
Finding files... ['/Users/amarzal/Documents/workspace/Sudoku/src/tests/test_SudokuValidator.py'] ... done Importing test modules ... done. test_sudoku_isNotAList_isRejected (test_SudokuValidator.TestSudokuValidator) ... ERROR ====================================================================== ERROR: test_sudoku_isNotAList_isRejected (test_SudokuValidator.TestSudokuValidator) ---------------------------------------------------------------------Traceback (most recent call last): File "/Users/amarzal/Documents/workspace/Sudoku/src/tests/test_SudokuValidator.py", line 7, in test_sudoku_isNotAList_isRejected validator = SudokuValidator() NameError: global name 'SudokuValidator' is not defined ---------------------------------------------------------------------Ran 1 test in 0.001s FAILED (errors=1)

36

El error est claro. El informe nos advierte de que ejecut una prueba en 0.001 segundos y que fall. Es hora de crear el SUT. Vamos a crearlo en un mdulo validator en un paquete llamado sudoku. Con el men contextual de src aadimos un paquete llamado sudoku y con el men contextual en sudoku creamos el mdulo validator. Su contenido es ste:
class SudokuValidator: def check(self, sudoku): if not isinstance(sudoku, list): return False

Ahora volvemos a ejecutar la prueba unitaria, pero el nuevo mensaje es ste:


Finding files... ['C:\\Users\\amarzal\\workspace_prueba\\kk\\src\\tests\\test_SudokuValidator.py'] ... done Importing test modules ... done. test_sudoku_isNotAList_isRejected (tests.test_SudokuValidator.TestSudokuValidator) ... ok ---------------------------------------------------------------------Ran 1 test in 0.000s OK

Todo ha ido bien. Es hora de aadir ms pruebas unitarias en test_SudolkuValidator:


from unittest import TestCase

from sudoku.validator import SudokuValidator class TestSudokuValidator(TestCase): def test_sudoku_isNotAList_isRejected(self): validator = SudokuValidator() sudoku = 0 self.assertFalse(validator.check(sudoku)) def test_sudoku_isNotA4x4List_isRejected (self): validator = SudokuValidator() sudoku = [] self.assertFalse(validator.check(sudoku))

Ntese que hemos suprimido el mensaje del aserto: si se da buenos nombres a los mtodos de las pruebas unitarias, los eventuales errores sern legibles por s solos. Un buen criterio para nombrar los mtodos es: Empezar por test Poner un sujeto Sealar una condicin que observa el sujeto Indicar el resultado esperado

Ejecutemos las nuevas pruebas unitarias, la segunda de las cuales est condenada a fallar:
Finding files... ['/Users/amarzal/Documents/workspace/Sudoku/src/tests/test_SudokuValidator.py'] ... done Importing test modules ... done. test_sudoku_isNotA4x4List_isRejected (test_SudokuValidator.TestSudokuValidator) ... FAIL test_sudoku_isNotAList_isRejected (test_SudokuValidator.TestSudokuValidator) ... ok ====================================================================== FAIL: test_sudoku_isNotA4x4List_isRejected (test_SudokuValidator.TestSudokuValidator) ---------------------------------------------------------------------Traceback (most recent call last): File "/Users/amarzal/Documents/workspace/Sudoku/src/tests/test_SudokuValidator.py", line 15, in test_sudoku_isNotA4x4List_isRejected self.assertFalse(validator.check(sudoku)) AssertionError: True is not False ---------------------------------------------------------------------Ran 2 tests in 0.001s

37

FAILED (failures=1) Se ha producido un fallo. Ahora se trata de un fallo y no de un error, porque ha fallado un aserto. Antes era un error porque fall la ejecucin de la misma prueba unitaria, ya que SudokuValidator no exista. Y ahora seguimos definiendo el validador para que supere la prueba:
class SudokuValidator: def check(self, sudoku): if not isinstance(sudoku, list): return False if len(sudoku) != 4: return False return True

Si ejecutamos ahora las pruebas unitarias:


Finding files... ['C:\\Users\\amarzal\\workspace_prueba\\kk\\src\\tests\\test_SudokuValidator.py'] ... done Importing test modules ... done.

test_sudoku_isNotA4x4List_isRejected (tests.test_SudokuValidator.TestSudokuValidator) ... ok test_sudoku_isNotAList_isRejected (tests.test_SudokuValidator.TestSudokuValidator) ... ok ---------------------------------------------------------------------Ran 2 tests in 0.000s OK

Ahora podemos ir iterando el proceso: disear prueba unitaria e implementar funcionalidad. Pero antes, refactoricemos las propias pruebas unitarias. Observemos que los dos mtodos test_* empiezan por la misma lnea: una que construye una instancia de SudokuValidator. La clase TestCase permite definir un mtodo que se invocar antes de ejecutar cada prueba unitaria:
from unittest import TestCase from sudoku.validator import SudokuValidator class TestSudokuValidator(TestCase): def setUp(self): self.validator = SudokuValidator() def test_sudoku_isNotAList_isRejected(self): sudoku = 0 self.assertFalse(self.validator.check(sudoku)) def test_sudoku_isNotA4x4List_isRejected (self): sudoku = [] self.assertFalse(self.validator.check(sudoku))

38

Tras iterar varias veces el proceso llegaramos a este cdigo para las pruebas unitarias:
import unittest from sudoku.validator import SudokuValidator class TestSudokuValidator(unittest.TestCase): def setUp(self): self.validator = SudokuValidator() def test_sudoku_isNotAList_isRejected(self): sudoku = 0 self.assertFalse(self.validator.check(sudoku)) def test_sudoku_isNotA4x4List_isRejected (self): sudoku = [] self.assertFalse(self.validator.check(sudoku)) def test_sudoku_withNotIntegers_isRejected (self): sudoku = [[0.0, 0, 0, 0], [0]*4, [0]*4, [0]*4] self.assertFalse(self.validator.check(sudoku)) def test_sudoku_withOutOfRangeNumbers_isRejected (self): sudoku = [[-1, 2, 3, 4], [0]*4, [0]*4, [0]*4] self.assertFalse(self.validator.check(sudoku)) def test_sudoku_withRepeatedNumbersInRow_isRejected (self): sudoku = [[0, 2, 2, 4], [0]*4, [0]*4, [0]*4] self.assertFalse(self.validator.check(sudoku)) def test_sudoku_withRepeatedNumbersInColumn_isRejected (self): sudoku = [[0, 2, 1, 4], [0, 2, 0, 0], [0]*4, [0]*4] self.assertFalse(self.validator.check(sudoku))

def test_sudoku_withRepeatedNumbersInSector_isRejected (self): sudoku = [[0, 2, 1, 4], [2, 0, 0, 0], [0]*4, [0]*4] self.assertFalse(self.validator.check(sudoku)) if __name__ == "__main__": #import sys;sys.argv = ['', 'Test.testName'] unittest.main()

Y este otro para el validador:


class SudokuValidator: def __init__(self): rows = tuple(tuple((i, j) for j in range(4)) for i in range(4)) columns = tuple(tuple((i, j) for i in range(4)) for j in range(4)) sectors = (((0,0), (0,1), (1,0), (1,1)), ((0,2), (0,3), (1,2), (1,3)), ((2,0), (2,1), (3,0), (3,1)), ((2,2), (2,3), (3,2), (3,3))) self.regions = rows + columns + sectors def check(self, sudoku): if not isinstance(sudoku, list) or len(sudoku) != 4: return False for row in sudoku: if not isinstance(sudoku, list) or len(row) != 4: return False for number in row: if not (isinstance(number, int) and 0 <= number <= 4): return False for region in self.regions: numbers = set() for (i, j) in region: n = sudoku[i][j] if n != 0 and n in numbers: return False numbers.add(n) return True def complete(self, sudoku): for row in sudoku: for number in row: if number == 0: return False all = set(range(1,5)) for region in self.regions: numbers = set() for (i, j) in region: numbers.add(sudoku[i][j]) if numbers != all: return False return True

39

Una ventaja de las pruebas unitarias es la vigilancia que ejercen sobre el cdigo se decidimos refactorizarlo. Tras cada cambio podemos ejecutar las pruebas unitarias y asegurarnos de que supera todas las que tenan xito antes. No tendremos nunca la garanta al 100% de que todo est bien, pero s una gran seguridad. Al refactorizar una base de cdigo mediana o grande es fundamental trabajar con red.

Hay una reflexin pertinente: el desarrollo TDD mejora la calidad del diseo del cdigo generado. El programador se centra en cada instante en fragmentos cortos de funcionalidad y acaba diseando clases ms finas, ms orientadas a responsabilidades concretas. Esto hace que se aleje el peligro de crear clases Dios, esto es, clases que crecen y crecen asumiendo ms y ms funcionalidad, hasta que el cdigo se hace inmanejable. Por otra parte, se sigue un buen principio de diseo: YAGNI (Youre Not Goint to Need It), es decir, evita la sobreingeniera.

Impostores
Las pruebas unitarias pretenden someter a prueba a unidades de cdigo pequeas, tpicamente clases o funciones. Frecuentemente esas unidades dependen de otras clases complejas o de agentes que no se pueden emplear en las pruebas. En el primer caso, es posible que esa dependencia de otras clases haga difcil establecer, en caso de fallo, quin es el culpable del mismo, lo que da al traste con el objetivo esencial de la prueba unitaria. En el segundo caso, ese agente puede consumir recursos costosos o crticos (escritura en bases de datos, acceso a dispositivos externos que consumen fungibles, etctera), requerir mucho tiempo de ejecucin (acceso a bases de datos, comunicacin por red, etctera) o, sencillamente, no estar disponibles en tiempo de ejecucin. En estas situaciones resulta conveniente trabajar con impostores (mockers), objetos que suplantan a los que no podemos usar en la fase de pruebas (como un humano que interacta con un mdulo). Imaginemos una funcin que calcula la solucin de un problema combinatorio con un mtodo pesado. Un impostor podra haber memorizado la solucin de una serie de problemas para las entradas que suministra una prueba unitaria y devolverlas directamente cuando proceda. De detectare un fallo en pruebas unitarias sobre un SUT que usa al impostor, estara claro que el responsable es el SUT, y no el mtodo de resolucin del problema combinatorio. Disear impostores para funciones es sencillo, pero cmo hacerlo eficientemente para objetos de mayor complejidad? Las libreras de mocking simplifican esta tarea. La impostura (!) es un campo relativamente reciente y no hay marcos de trabajo claramente dominantes. Hay varios tipos de impostor. Martin Fowler distingue entre: Dummy objects (objetos tontos?): objetos que se pasan en listas de parmetros, pero nunca se usan. Fake objects (objetos falsos?): objetos que realmente funcionan, pero de algn modo que los hacen inapropiados para produccin (como una base de datos en memoria). Stubs (colillas?): proporcionan respuestas enlatadas a llamadas concretas hechas durante las pruebas. Los stubs pueden almacenar algunos datos sobre las veces que ha hecho cierta accin para ser consultados luego. Mocks (impostores propiamente dichos): objetos pre-programados con expectativas que configuran una especificacin de las llamadas que se espera que reciban.

40

Vamos a presentar brevemente una librera de mocking para Python: Mockito for Python. Es un marco de trabajo inspirado en Mockito, uno exitoso en el mundo Java. La pgina web de Mockito (para Java) est en http://code.google.com/mockito y la de Python en http://code.google.com/p/mockito/wiki/MockitoForPython. Supongamos que el programa de Sudoku est completo y que el usuario interacta fijando coordenadas y valor que desea poner en la casilla de esas coordenadas, usando el valor 0 para indicar el borrado de la casilla. En principio, el jugador se representara con una clase Python con mtodos para comunicarse con el humano mediante entrada/salida. Esta implementacin puede tomarse como tpica:
class SudokuPlayer(): def get_coordinates_and_number(self): while True: print("Play i j n:", end="") line = input().strip() words = line.split()

if len(words) == 3: try: i = int(words[0]) j = int(words[1]) n = int(words[2]) if 1 <= i <= 4 and 1 <= j <= 4 and 0 <= n <= 9: return (i-1, j-1, n) else: raise ValueError() except ValueError: print("Invalid input")

Esta implementacin no es muy buena si pensamos en la impostura. No lo es porque presenta pocas costuras (seams), es decir, puntos en los que podemos intervernir para que el cdigo se pueda impostar sin depender de los recursos costosos (en este caso, el humano). Lo primero que haremos es intervenir para impostar la propia entrada/salida, que pasar a usarse indirectamente a travs de funciones:
class SudokuPlayer(): def __init__(self, prompt=lambda: print("Play i j n:", end=""), notify_error = lambda: print("Invalid input"), get_input=input): self.prompt = prompt self.notify_error = notify_error self.get_input = get_input def get_coordinates_and_number (self): while True: self.prompt() line = self.get_input().strip() words = line.split() if len(words) == 3: try: i = int(words[0]) j = int(words[1]) n = int(words[2]) if 1 <= i <= 4 and 1 <= j <= 4 and 0 <= n <= 9: return (i-1, j-1, n) else: raise ValueError() except ValueError: self.notify_error()

41

Las funciones prompt, notify_error y get_input son las encargadas de la entrada/salida y toman por defecto los valores que tena la versin anterior. Ahora podemos reemplazarlas en la fase de pruebas por impostores. Veamos una primera prueba unitaria:
class TestSudokuPlayer(unittest.TestCase): def test_getCoordinatesAndNumber_readsGoodUserValues_returnsProperValues (self): ioMock = Mock() when(ioMock).get_input().thenReturn( "1 1 1") output = StringIO() player = SudokuPlayer(prompt=lambda: None, notify_error=lambda: None, get_input=ioMock.get_input) (i, j, n) = player.get_coordinates_and_number() verify(ioMock, times=1).get_input() self.assertEquals((i,j,n), (0,0,1)) output.close()

El objeto ioMock es un impostor para la entrada salida. Le fijamos expectativas con esta sentencia:

when(ioMock).get_input().thenReturn("1 1 1")

Se lee: cuando ioMock reciba una llamada al mtodo get_input, debe devolver la cadena 1 1 1. El mtodo get_input no existe hasta el mismo momento en que le decimos que se podra invocar ese mtodo! Cuando construirmos SudokuPlayer, le pasamos como parmetro get_input el mtodo ioMock.get_input, cuyo comportamiento acabamos de definir. Tras ejecutar la llamada al SUT (la llamada al mtodo get_coordinates_and_number de player), podemos verificar que ioMock se ha comportado como se esperaba con la sentencia:
verify(ioMock, times=1).get_input()

Esa sentencia se lee como cercirate de que ioMock ha recibido exactamente una llamada a get_input. Si esa comprobacin fallar, la prueba unitaria fracasara. Podemos impostar al propio objeto player. Esta prueba unitaria lo hace:
def test_game_withValidSudoku_plasyOK (self): player = Mock() when(player).get_coordinates_and_number().thenReturn((0,0,1)) \ .thenReturn((1,3,2)) \ .thenReturn((2,3,3)) \ .thenReturn((3,0,4)) game = SudokuGame(sudoku_chooser=lambda x: "*234\n341*\n214*\n*321") self.assertTrue(game.start(player))

La segunda sentencia del cuerpo de la prueba prepara al impostor para que responda a una serie de llamadas a un mtodo. Se lee llamarn varias veces a get_coordinates_and_number sobre player, y la primera vez debes devolver la tupla (0,0,1); la segunda, la tupla (1,3,2); la tercera, la tupla (2,3,3); y la cuarta, la tupla (3,0,4). No hace falta usar la artillera pesada siempre. Podemos impostar ciertas funciones con simples stubs gracias a las costuras. En este ejemplo impostamos una funcin que debera escoger un Sudoku de una base de datos:
def test_game_withInvalidSudokuString_raisesException (self): game = SudokuGame(sudoku_chooser=lambda x: "") self.assertRaises(ValueError, game.start, None) def test_game_withIncompleteSudoku_raisesException (self): game = SudokuGame(sudoku_chooser=lambda x: "234\n341*\n214*\n*321") self.assertRaises(ValueError, game.start, None) def test_game_withImpossibleSudoku_raisesException (self): game = SudokuGame(sudoku_chooser=lambda x: "2234\n341*\n214*\n*321") self.assertRaises(ValueError, game.start, None)

42

Ntese que lo nico importante es haber creado previamente las costuras. Acabamos de hablar de impostores con un comentario: los impostores no slo ayudan a probar el cdigo; tambin ayudan a disear mejor cdigo. Crear las costuras apropiadas supone dotar al cdigo de mayor flexibilidad, lo que har ms fcil su adaptacin a diferentes contexto. Las costuras introducidas en el jugador hacen que ahora resulta sencillo adaptar una aplicacin pensada para interaccin con consola (pantalla y teclado) a contexto como el de servicios web.

Cobertura de cdigo
Un problema de los lenguajes muy dinmicos, como Python, es que muchos problemas de tipos no se manifiestan hasta la ejecucin de las sentencias problemticas. Si una lnea de cdigo no se ha ejercitado nunca en una prueba

unitaria, no sabremos si es problemtica hasta la fase de explotacin del programa. Necesitamos herramientas que nos digan qu lneas se han ejecutado y cules no durante la fase pruebas. Las herramientas de cobertura de cdigo nos facilitan esa labor. Para Python, la herramienta de cobertura por antonomasia es coverage.py, construida por Ned Batchelder y disponible en http://nedbatchelder.com/code/coverage. Slo hay un problema: a fecha de hoy, la versin de Python 3.1 no se integra con Eclipse va Pydev (aunque se puede usar maualmente). Por eso no entraremos en detalles sobre su uso. Slo mostrar un ejemplo de salida de coverage.py: <unittest.TestResult run=52 errors=0 failures=0> Name Stmts Exec Cover Missing --------------------------------------------------------sudoku/__init__ 1 1 100% sudoku/game 35 30 85% 40-42, 47-48 sudoku/parser 16 16 100% sudoku/player 20 20 100% sudoku/presenter 12 12 100% sudoku/solver 36 36 100% sudoku/validator 36 34 94% 17, 44 test/__init__ 1 1 100% test/test_SudokuGame 21 20 95% 33 test/test_SudokuParser 19 18 94% 38 test/test_SudokuPlayer 34 33 97% 49 test/test_SudokuPresenter 11 10 90% 27 test/test_SudokuSolver 30 29 96% 41 test/test_SudokuValidator 28 27 96% 39 --------------------------------------------------------TOTAL 300 287 95% El informe indica que la ejecucin de una serie de pruebas unitarias ha cubierto el 95% de las lneas. Adems, nos indica qu lneas estn por cubrir. Nuestra misin es disear nuevas pruebas unitarias para asegurarnos de que esas lneas se ejecutan en la fase de pruebas.

43

Python 3.x
Python 3.x es un cambio importante en la serie Python en tanto que rompe la compatibilidad hacia atrs, pero porque implementa un relacin de cambios solicitados a lo largo del tiempo en aspectos que se consideran muy importantes. La serie 3.x se viene desarrollando en paralelo a la 2.x. La versin 3.0 altera la sintaxis del lenguaje y cambia elementos de las libreras. Algunos de los cambios (no los sintcticos) de la versin 3.1 y la futura 3.2 se han ido incorporando a la versin 2.6 y a la inminente versin 2.7. Los cambios de la serie 2.x se introducen para facilitar la migracin a la serie 3.x. En este momento la serie 3.x es completamente utilizable, pero muchas libreras estn en fase de transicin. An se tardar un par de aos en encontrar para 3.x todo lo que se encuentra y usa generalmente en la serie 2.x. Los cambios ms importantes en la serie 3.x se resumen excelentemente en el documento http://ptgmedia.pearsoncmg.com/imprint_downloads/informit/promotions/python/python2python3.pdf.

Das könnte Ihnen auch gefallen