Otra manera de programar en Cocoa. Ruby y Python.

abril 28, 2010

Cuando pensamos en desarrollar aplicaciones para Mac, siempre nos viene a la mente Cocoa, y junto a este Framework aparece el lenguaje mas maquero, Objetive-C. Sin embargo, es posible hacer aplicaciones completas usando otros lenguajes, como puede ser Java, C o C++ y los que, desde mi punto de vista, son mas interesantes, estoy hablando de los lenguajes de Script de “moda” Ruby y Python.

Gracias a RubyCocoa y PyObjc, podemos invocar a métodos de clases desarrolladas en ObjetiveC, lo que quiere decir que vamos a ser capaces de utilizar las librerias de Cocoa, desde nuestro lenguaje de script favorito. Además, como ambos vienen instalados por defecto en nuestros OSX, no tendremos que instalar nada para empezar a desarrollar con ellos.

Aparte de la posibilidad de crear aplicaciones Cocoa únicamente usando estos lenguajes de script, se nos abre otra posibilidad bastante interesante. Gracias a la inclusión en OSX del Scripting Bridge y combinándolo con la posibilidad que comentabamos de usar Python o Ruby para usar librerías hechas en Objetive-C, podremos comunicarnos con las aplicaciones Cocoa usando estos lenguajes.

Antiguamente, si hablamos de lenguajes de script, esta posibilidad de comunicación entre aplicaciones estaba limitada al propio AppleScript.

Particularmente no tengo nada en contra de AppleScript, pero siempre me pareció un lenguaje un tanto complicado de aprender, sin embargo, Python o Ruby me parecen lenguajes bastante prácticos, por lo que el hecho de poder usarlos como medio de comunicación con otras aplicaciones Cocoa, me parece una idea tremenda 🙂

A partir de OSX 10.5 se introdujo en el sistema operativo lo que se conoce como Scripting Bridge. Este sistema permite comunicarse con los objetos en ejecución usando mensajes de Objetive-C. Anteriormente la única manera de comunicarse con las aplicaciones era usando comandos AppleScript, por eso, este era el único lenguaje con el que podíamos, por ejemplo, recuperar las canciones de nuestra lista de iTunes.

Como seguramente habreís podido deducir, gracias también al Scripting Bridge, podemos comunicarnos con dichas aplicaciones usando el omnipresente Objetive-C

La inclusión del Scripting bridge, de la misma manera, es cruicial para la parte “servidora”, es decir, el iTunes o el Safari, ya que ha simplificado en gran medida la forma de añadir soporte de Script a una aplicación, pese a que esta tarea sigue siendo no trivial.

Como siempre, la mejor forma de aprender algo es viendo algún ejemplo. Así que vamos a utilizar a Python para listar todas las canciones que tenemos en nuestra librería de iTunes.

El código para realizar esto es bastante sencillo como podemos ver:

import ScriptingBridge

iTunes = ScriptingBridge.SBApplication.applicationWithBundleIdentifier_("com.apple.iTunes")

for source in iTunes.sources():
	print source.name()
	for playlist in source.playlists():
		print "\\---", playlist.name()
		for track in playlist.tracks():
			print "\t\\---", track.name()

Si lo ejecutamos, veremos como se lanza “silenciosamente” iTunes, y momentos después se nos muestra la lista de canciones.

Es importante apreciar que con sólo 6 lineas, estamos salvando muchisimas dificultades que existían en el pasado, y sobre todo, estamos abriendo una posibilidad tremenda para crear nuevas aplicaciones.

Ahora bien, os podeis preguntar, ¿Cómo se que métodos tiene un objeto SBApplication determinado?, ya que por mucho que sea sencillo, si no tenemos documentación al respecto no podemos hacer nada.

Para averiguar que nos ofrece una aplicación existen varios métodos, el mas “visual” es usando la opción de “Abrir Diccionario” del “AppleScript Editor”. Como vemos en la captura.

El otro método “menos amigable” es usar los comandos sdef y sdp, que se encargan de “extraer” la definiciónes de script que posee una aplicación; como podemos ver en la captura de la terminal.

Estos comandos nos sacaran un fichero .h que podemos utilizar para importarlo en nuestro proyecto de Objetive-C, y que desde el punto de vista de nuestros lenguajes de script, nos van a permitir saber que métodos nos expone la aplicación.


Introspección en Python

marzo 20, 2010

Lo primero que nos choca en el título es la palabra “Introspeccón”, no es un termino que estemos acostumbrados a utilizar en nuestro día a día, incluso nos cuesta deletrearlo. Segun la RAE, Instropección es “Observación interior de los propios actos o estados de ánimo o de conciencia.”. Esta explicación se acerca a su significado cuando nos referimos a los lenguajes de programación. Decimos que un lenguaje o plataforma tiene la capacidad de ser instrocpectivo cuando podemos, dinámicamente, mirar dentro del código que se está ejecutando, esto es, podemos consultar diversos aspectos del código en tiempo de ejecución.

Esta caracteristica es común en muchos de los lenguajes modernos como Java, C# y de la mayoría de lenguajes de script más utilizados como Ruby, Python o Javascript.

Utilizando reflexión podemos llevar a cabo tareas muy interesantes, como por ejemplo, construir herramientas de pruebas unitarias genéricas, o en el caso de desarrollar un IDE, estas capacidades son claves para poder brindar un sistema de sugerencia de código, como hace Eclipse o Netbeans.

Entrando en temas mas abstractos, gracias a la reflexión podemos diseñar un programa que sea adaptativo, esto es, que pueda crear métodos dinamicamente según las necesidades del cliente.

Usando Python, vamos a crear un script que llame a todos los métodos de todos los ficheros .py que se encuentren en el mismo directorio. Todos estos ficheros contendran funciones que admiten valores de entrada númericos.

Este programa puede ser útil de cara a crear entornos de pruebas unitarios.

Para ello, Python nos brinda varios mecanismos de instrospección.

El primero de todos es dir, esta función nos devuelve una lista de todos los elementos de un objeto, este objeto puede ser un módulo, un fichero con diversas funciones, o una clase de python.

Por ejemplo si tenemos un sencillo fichero llamado operaciones.py:

version = '1.0'

def suma(a, b):
	return a+b

def resta(a,b):
	return a-b

Si hacemos dir(operaciones) la lista de resultados, además de los elementos estandar como __name__, contendrá las funciones suma y resta y el atributo versión.

La siguiente función clave es getattr. Con esta función podremos obtener un elemento de un objeto, la ventaja es que podemos hacerlo en tiempo dinámico, por ejemplo si hacemos getattr(operaciones,’version’) se nos mostrará la cadena 1.0, esto sería equivalente a hacer operaciones.versión. Al igual que con los atributos podemos obtener también funciones e incluso llamarlas usando esta función. Por ejemplo lo equivalente a operaciones.suma(3,4) sería getattr(operaciones,’suma’)(3,4).

Ya casi tenemos todos los elementos que necesitamos para el proposito original, una de las cosas que aún necesitamos es saber si, teniendo un atributo, este es una función a la que podamos llamar. Para ello necesitamos la función callable. Esta función devolverá True cuando el elemento sea “llamable”. Por ejemplo, callable(getattr(operaciones, ‘suma’)) devolverá True.

Finalmente, lo único que nos falta por saber es el número de parámetros que la función tiene, para ello, usaremos la función getargspec, del módulo inspect. Esta fución nos devuelve una tupla con nombre, siendo uno de los elementos una lista con los parametros de la función. Así si llamamos inspect.getargsspec(operaciones.suma), el resultado será: ArgSpec(args=[‘a’, ‘b’], varargs=None, keywords=None, defaults=None).

Finalmente, el código de nuestro tester es el siguiente:

import os, inspect, random

for fileName in os.listdir('.'):
	if fileName.endswith('.py') & (fileName != 'tester.py'):
		moduleName = fileName[0:fileName.index('.py')]
		print '>> Trying to import ', fileName, ' moduleName: ', moduleName
		module = __import__(moduleName)
		for element in dir(module):
			moduleElement = getattr(module,element)
			if callable(moduleElement):
				params = []
				for param in inspect.getargspec(moduleElement).args:
					params.append(int(random.uniform(0,100)))
				print 'Calling ', moduleName,'->',element, '(', params, ')',
				try:
					print ' --- return Value: ', moduleElement(*params)
				except:
					print ' --- error!'

Por cada fichero del directorio donde se encuentre tester.py, analizará si es un fichero de Python, y si lo es tratará de importarlo y llamar a todas sus funciones. Como curiosidad, destaca el uso del operador de desempaquetado (*), y lo útil que es para llamar a una función con parametros, a priori, desconocidos.

En nuestro ejemplo, en el directorio teníamos aparte del fichero de operaciones.py, uno que contenía algunas operaciones matemáticas, llamado mathsThings, con dos funciones, getFiboNumber que nos devuelve el elemento n de la serie de Fibonacci, y isPrime que nos devuelve si un número es primo.
La salida al ejecutar tester.py es:


Generadores en Python

marzo 11, 2010

Si algo tiene de bueno aprender un lenguaje de script es que descubres nuevas maneras para hacer las cosas que un lenguaje tradicional como C o Java habitualmente no te ofrece.

En el caso de Python, existen múltiples cosas a las que los programadores no estamos acostumbrados, en este post concretamente vamos a hablar de los generadores.

Desde un punto de vista muy básico, un generador es un método para extraer los resultados de una función de una forma diferente a lo que tenemos acostumbrado a usar. Es un concepto mas cercano a la programacion funcional de lisp o haskell que a la programación procedimental de C o Pascal.

Normalmente lo usaremos cuando los datos que una funcion devuelve es una lista de datos. El enfoque tradicional sería construir la lista en la función y devolverla tal cual al llamador, pero esta forma de funcionar tiene alguna limitación.
Por ejemplo, si el proceso de recolectar la lista es muy costoso, el llamador no obtendrá ningún resultado hasta el final del proceso, y si para mas añadido, el llamador puede no necesitar todos los elementos en función de algún elemento de la lista; usando la forma típica de procesar este comportamiento, el llamador no tiene manera de obtener parcialmente esos resultados.

Además desde el punto de vista de la eficiencia, si la lista es muy grande, la estamos iterando dos veces, una al generarla y otra al procesarla.

Aparte de este uso, es frecuente encontrarse con funciones en que queremos ir extrayendo poco a poco resultados, el funcionamiento típico se basa en llamar repetidas veces a la función con el “estado” de dicha función pasado por parámetros desde el llamador, por ejemplo, en las típicas funciones que te buscan caracteres en una cadena de forma progresiva, cada vez que quieres un nuevo resultado, necesitas pasarle la cadena recortada hasta la anterior ocurrencia del caracter que buscamos. Con los generadores podremos hacer esto de una forma mucho mas natural.

Realmente esta forma de funcionar, no es que no se pueda hacer con C, por ejemplo, usando callbacks podemos simular el mismo comportamiento, pero sin duda la forma de afrontarlo en Python, es mucho mas sencilla, ya que entre otras cosas, está integrado como mecanismo genérico del lenguaje.

Como siempre la mejor forma de aprender algo es viendo un ejemplo sobre ello. Usando los generadores vamos a desarrollar una función que pasado un número binario lo irá “desgranando” y devolviendo los valores decimales por cada bit.

Esto es, si le pasamos a la funcion 10011, la salida será 1+2+0+0+16=19.

Si esto lo hicieramos de la forma habitual, la función tendría que devolver una lista con los elementos, lista que luego deberiamos iterar de nuevo para imprimir el resultado.

### EJEMPLO SIN USAR GENERADORES
def binToDecClassic(val):
	n = 0
	res = []
	while val > 0:
		res += [(val%2) * (2**n)]
		val /= 10
		n+=1

	return res

sum = 0
out = binToDecClassic(10011)
for val in out:
	print "+", val,
	sum += val

print "=", sum

Si usamos generadores, el cógido sería:

### EJEMPLO USANDO GENERADORES
def binToDecUsingGenerators(val):
	n = 0
	while val > 0:
		yield (val % 2) * (2**n)
		val = val / 10
		n += 1

sum = 0
for val in binToDecUsingGenerators(10011):
	print "+", val ,
	sum += val

print "=", sum

Como vemos, el código es muy similar, de hecho, la sintaxis puede llevar a confusión, ya que aunque vemos dos bucles, realmente sólo hay uno, en cada parada del for se corresponde con la instruccion “yield” dentro de la función. Esta palabra reservada, “yield” es la clave de los generadores. Cuando ponemos yield, devolvemos el control al llamador, con el resultado de la expresión que acompaña a dicha palabra, podemos decir que es como un return, pero que no finaliza definitivamente la función.

Este ejemplo, dada su sencillez para ver como funciona un generador, no aprovecha las ventajas que hemos comentado anteriormente, para ello, vamos a ver otro ejemplo algo más completo, para ello vamos a usar la funcion walk() del paquete estandar os. Esta función, dada un directorio raíz, va devolviendo poco a poco, usando “yield” los ficheros y subdirectorios de cada directorio. Por ejemplo, tenemos una función “searchInPath” que busca un fichero en un path:

import os

def searchInPath(path, filename):
    cont = 0
    for root, dirs, files in os.walk(path):
        if filename in files:
            return cont, True
        cont += 1
    return cont, False

[cont, found] = searchInPath('c:\\temp', 'prueba.py')
print cont,' steps - found: ', found

En cada directorio, se nos devolverá el control, de tal manera que si encontramos el fichero, no seguimos iterando por el árbol de directorios, si esto mismo lo haríamos de la forma tradicional, iterariamos por todo el árbol de directorios, devolveriamos la lista y luego deberíamos buscar en la lista de ficheros.


Incorporar Modelos ManyToMany al Backend en Django

diciembre 10, 2009

Uno de los beneficios de usar django para crear nuestra aplicación web es que como por arte de mágia, todo el backoffice de nuestra aplicación se generará ante nuestros ojos sin tirar ni una linea de código.

Parafraseando a Uderzo y Goscinny, ¿Ni una sola linea?, No, alguna, cual aldea Gala indestructible, habrá que tirar, pero ni mucho menos comparable a lo que sería hacerlo desde cero.

Hasta hace poco, si en nuestro modelo exitía una relación Muchos a Muchos, la edición desde la interfaz de administración no era todo lo intuitiva que esperaríamos, pero en la última versión de django este aspecto esta solucionado, proporcionandonos una flexibilidad absoluta.

Supongamos un modelo de un sencillo bloc de notas, inicialmente tendremos dos entidades, una que será las notas y otra que serán los tags que asociamos a dichas notas. La relación entre los tags y las notas es de Muchos a Muchos, el código de dichos modelos sería algo como:

class Note(models.Model):
  title = models.CharField(max_length=200)
  pub_date = models.DateTimeField('date created')
  body = models.TextField()
  tags = models.ManyToManyField('Tag', related_name='notes')

  def __unicode__(self):
    return self.title</div>

class Tag(models.Model):
  name = models.CharField(max_length=100)

  def __unicode__(self):
    return self.name

Por defecto, django nos genera una interfaz de administración que podemos ver si entramos a la aplicación “admin” una vez lanzado el servidor. Si usamos el servidor de desarrollo, la URL habitual es http://localhost:8000/admin/

Como deciamos anteriormente, en las versiones anteriores de django, solo podíamos editar (y personalizar usando Inlines por ejemplo) los elementos de la clase asociada donde se definía la relacion ManyToMany. En nuestro caso, solo podriamos asociar y añadir tags desde la gestión de notas, pero no viceversa. Al ser una relación bidireccional, como vemos esto no es muy útil.

Para personalizar las interfaces de administración debemos editar el fichero admin.py que esta dentro de nuestro proyecto.

Si queremos que la lista de objetos de la clase asociada aparezca de forma linear, podemos crear un objeto derivado de algun sabor de Inline, como por ejemplo TabularInline. La novedad en la nueva versión de django es que a la clase que modela la relación ManyToMany posee un atributo que representa la asociación.

Este atributo se conoce como through, en versiones anteriores, ese parámetro nos servía para indicar un modelo extendido asociado a la relación muchos a muchos, a la hora de declarar la propia relación en el modelo.

Así si usamos las siguientes clases:

class TagNoteInline(admin.TabularInline):
  model = Note.tags.through

class TagAdmin(admin.ModelAdmin):
  inlines = [
    TagNoteInline,
  ]

class NoteAdmin(admin.ModelAdmin):
  inlines = [
    TagNoteInline,
  ]
  exlude = ('tags',)

admin.site.register(Note, NoteAdmin)
admin.site.register(Tag, TagAdmin)

Podremos editar en ambas interfaces la clase correspondiente asociada.

Para finalizar, si no has entendido nada 😉 quizás te interese el maravilloso tutorial que esta en la propia web del django y concretamente la relacionada con la parte de administración: http://docs.djangoproject.com/en/dev/intro/tutorial02/#intro-tutorial02

Django y las Bases de Datos

junio 24, 2009
No se vosotros, pero si algo me gusta poco programar es el típico código para unir nuestras clases del modelo con la base de datos. Una de las grandes desventajas de escribir este código a mano es que es sumamente repetitivo, lo que lleva a muchos errores de copy&paste.
Este aspecto desde luego no es nuevo en el desarrollo de aplicaciones, los requerimientos de persistencia son constantes en una aplicación.
Una primera aproximacion fué las bases de datos orientadas a objetos, recuerdo que en alguna clase de la universidad vimos FastObjects o la extension de Oracle para Objectos, sin embargo esta aproximación no acabo de imponerse. La opción que mas se utilizaba fué usar APIs para traducir los objetos tradicionales a Bases de Datos Relacionales típicas.
Posiblemente esta opción fue la mas extendida por la gran difusión y optimizacion de dichos motores de bases de datos.

django y las bases de datos

No se vosotros, pero si algo me gusta poco programar es el típico código para unir nuestras clases del modelo con la base de datos. Una de las grandes desventajas de escribir este código a mano es que es sumamente repetitivo, lo que lleva a muchos errores de copy&paste.

Este aspecto desde luego no es nuevo en el desarrollo de aplicaciones, los requerimientos de persistencia son constantes en una aplicación.

Una primera aproximacion fué las bases de datos orientadas a objetos, recuerdo que en alguna clase de la universidad vimos FastObjects o la extension de Oracle para Objectos, sin embargo esta aproximación no acabó de imponerse. La opción que mas se utilizaba fué usar APIs para traducir los objetos tradicionales a Bases de Datos Relacionales típicas.

Posiblemente esta opción fue la mas extendida por la gran difusión y optimización de dichos motores de bases de datos.

Hablando de mi experiencia y en el ámbito de los lenguajes “mayores”, es decir Java o C#, empecé experimentando con Hibernate en lo que se refiere a Java y con los DataSource tipados en lo respectivo a .NET.

Mas tarde se publicó finalmente EJB 3.0 y surgió JPA. Lo cual añadio a Java una api bastante decente y además oficial que iba a favorecer bastante la programación de estas labores. Quizás en otro post hablaremos de JPA 😉

Cada evolución y versión de las APIs mejoraban un poco mas lo que teniamos anteriormente, y nos permitia quitarnos cada vez mas codigo de “pegamento” entra la BBDD y nuestras clases.

Según la aplicación, actualmente es posible ahorrarse mucho código de este tipo, lo que sin duda es una gran ventaja.

Todo no son paños calientes y muchas veces no nos quedará mas remedio que seguir programando algunos fragmentos de este tipo de código ya que las APIs no siempre se adaptan a las necesidades que suelen surgir cuando se plasman los requisitos del cliente, sin embargo estos son los casos especiales.

Hablando ya de lo que en esta serie nos preocupa, django nos va a ayudar bastante a la hora de autogenerar este código. De hecho a priori no tendremos que escribir ni una sola sentencia SQL para persistir los datos.

En el anterior post, dejamos el servidor funcionando, ahora vamos a crear una aplicación dentro de ese servidor para poder meter nuestro código. Para ello hay que ejecutar

$ python manage.py startapp bookstore

Y el script manage.py nos creará el directorio bookstore con los ficheros iniciales.

De los ficheros iniciales que nos ha creado el script anterior, vamos a centrarnos en el fichero models.py, por ejemplo digamos que queremos escribir una aplicación para manejar libros, empezaremos creando nuestras clases de modelo,

Para ello editamos el fichero models.py y añadimos la clase que representa a un libro:

from django.db import models
class Book(models.Model):
        author = models.CharField(max_length=100)
        title = models.CharField(max_length=100)
        creationDate = models.DateTimeField('date created')

        def __unicode__(self):
                return self.author + ": " + self.title

La definición de la clase ya nos da muchas pistas de como funciona internamente django. Primero podemos ver que nuestras clases de modelo heredan de models.Model, lo que nos va a proporcionar toda la funcionalidad de búsquedas, creación, etc.

El módulo models nos proporciona métodos para crear las variables de nuestras clases módelo, en lugar de crear tipos básicos de Python. En el ejemplo vemos como crear un campo de Texto o un campo de tipo Fecha.

Una cosa que echamos en falta en el ejemplo es como representar las relaciones entre clases, para ello vamos a modificar ligeramente el ejemplo, extrayendo el campo autor, a una clase nueva, por lo que quedaria algo como:

from django.db import models
class Author(models.Model):
        name = models.CharField(max_length=100)
        birthDate = models.DateTimeField('Birth date')
        def __unicode__(self):
                return self.name

class Book(models.Model):
        author = models.ForeignKey(Author)
        title = models.CharField(max_length=100)
        creationDate = models.DateTimeField('date created')
        def __unicode__(self):
                return self.author + ": " + self.title

Como vemos, usamos la clase ForeignKey para representar la asociación entre libro y autor, el resto de las relaciones también podemos implementarlas sin problemas, usando la clases ManyToMany o OneToOne.

Como siempre, nos gusta ver las cosas funcionando, para ello vamos a usar el comando manage.py para obtener diversas cosas, sin embargo, antes de nada es necesario incluir nuestra aplicación dentro de la lista de aplicaciones instaladas en nuestro proyecto. Para ello editamos el fichero settings.py y añadimos una linea con nuestra aplicación, algo como:

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'pruebadjango.bookstore',
)

Siendo “pruebadjango” el nombre de nuestro proyecto

Otro paso necesario es configurar los datos de acceso a la base de datos, para este ejemplo vamos a usar sqlite, una base de datos muy sencilla, que puede residir en un fichero de disco y que viene dentro de la distribución de python, lo que nos va a simplificar mucho las labores de configuración.

Para ello debemos editar de nuevo el fichero settings.py y modificar las lineas:

DATABASE_ENGINE = 'sqlite3'           
DATABASE_NAME = '/home/roberto/Projects/django/bbdd'

Siendo el DATABASE_NAME la ruta al fichero que va a guardar la base de datos.

Una vez añadida la aplicación a la lista ya podemos empezar a ver cositas

El script manage.py tiene diversas utilidades que nos van a servir para ver como django transforma nuestras clases Python a sql. Así por ejemplo si ejecutamos:

$ python manage.py sqlall bookstore

Obtendremos las sentencias SQL generadas para nuestras clases, para nuestro ejemplo la salida es:

BEGIN;
CREATE TABLE "bookstore_author" (
    "id" integer NOT NULL PRIMARY KEY,
    "name" varchar(100) NOT NULL,
    "birthDate" datetime NOT NULL
)
;
CREATE TABLE "bookstore_book" (
    "id" integer NOT NULL PRIMARY KEY,
    "author_id" integer NOT NULL REFERENCES "bookstore_author" ("id"),
    "title" varchar(100) NOT NULL,
    "creationDate" datetime NOT NULL
)
;
CREATE INDEX "bookstore_book_author_id" ON "bookstore_book" ("author_id");
COMMIT;

O también podemos ejecutar:

$ python manage.py validate

Para validar que nuestros modelos estan bien definidos.

Finalmente si ejecutamos:

$ python manage.py syncdb

Se crearan las tablas para nuestros modelos en la base de datos.