Gracias Gracias:  1
Página 2 de 2 PrimerPrimer 12
Resultados 16 al 22 de 22

Tema: Python para todos - por Raul Gonzalez Duque

  1. #16
    Administrador y fundador. Avatar de 3dpoder
    Fecha de ingreso
    Apr 2002
    Mensajes
    15,459

    Post Interactuar con Webs - Python para todos - por Raúl González Duque

    Interactuar con webs


    Existen dos módulos principales para leer datos de URLs en Python: urllib y urllib2. En esta lección aprenderemos a utilizar urllib2 ya que es mucho más completo, aunque urllib tiene funcionalidades propias que no se pueden encontrar en urllib2, por lo que también lo tocaremos de pasada.

    urllib2 puede leer datos de una URL usando varios protocolos como HTTP, HTTPS, FTP, o Gopher.

    Se utiliza una función urlopen para crear un objeto parecido a un fichero con el que leer de la URL. Este objeto cuenta con métodos como read, readline, readlines y close, los cuales funcionan exactamente igual que en los objetos file, aunque en realidad estamos trabajando con un wrapper que nos abstrae de un socket que se utiliza por debajo.

    El método read, como recordareis, sirve para leer el “archivo” completo o el número de bytes especificado como parámetro, readline para leer una línea, y readlines para leer todas las líneas y devolver una lista con ellas.

    También contamos con un par de métodos geturl, para obtener la URL de la que estamos leyendo (que puede ser útil para comprobar si ha habido una redirección) e información que nos devuelve un objeto con las cabeceras de respuesta del servidor (a las que también se puede acceder mediante el atributo headers).

    import urllib2
    try:
    f = urllib2.urlopen(“http://www.python.org”)
    print f.read()
    f.close()
    except HTTPError, e:
    print “Ocurrió un error”
    print e.code
    except URLError, e:
    print “Ocurrió un error”
    print e.reason
    Al trabajar con urllib2 nos podemos encontrar, como vemos, con errores de tipo URLError. Si trabajamos con HTTP podemos encontrarnos también con errores de la subclase de URLError HTTPError, que se lanzan cuando el servidor devuelve un código de error HTTP, como el error 404 cuando no se encuentra el recurso. También podríamos encontrarnos con errores lanzados por la librería que urllib2 utiliza por debajo para las transferencias HTTP: httplib; o con excepciones lanzadas por el propio módulo socket.

    La función urlopen cuenta con un parámetro opcional data con el que poder enviar información a direcciones HTTP (y solo HTTP) usando POST (los parámetros se envían en la propia petición), por ejemplo para responder a un formulario. Este parámetro es una cadena codificada adecuadamente, siguiendo el formato utilizado en las URLs:

    ‘password=contrase%A4a&usuario=manuel’
    Lo más sencillo para codificar la cadena es utilizar el método urlencode de urllib, que acepta un diccionario o una lista de tuplas (clave, valor) y genera la cadena codificada correspondiente:

    import urllib, urllib2
    params = urllib.urlencode({“usuario”: “manuel”,
    “password”: “contraseña”})
    f = urllib2.urlopen(“http://ejemplo.com/login”, params)
    Si lo único que queremos hacer es descargar el contenido de una URL a un archivo local, podemos utilizar la función urlretrieve de urllib en lugar de leer de un objeto creado con urlopen y escribir los datos leídos.

    La función urlretrieve toma como parámetros la URL a descargar
    y, opcionalmente, un parámetro filename con la ruta local en la que guardar el archivo, un parámetro data similar al de urlopen y un parámetro reporthooque con una función que utilizar para informar del progreso.

    A excepción de las ocasiones en las que se utiliza el parámetro data las conexiones siempre se realizan utilizando GET (los parámetros se envían en la URL). Para enviar datos usando GET basta con concatenar la cadena resultante de urlencode con la URL a la que nos vamos a conectar mediante el símbolo? .

    params = urllib.urlencode({“usuario”: “manuel”,
    “password”: “contraseña”})
    f = urllib2.urlopen(“http://ejemplo.com/login” +
    “?” + params)
    En urllib también se utiliza una función urlopen para crear nuestros pseudo-archivos, pero a diferencia de la versión de urllib, la función urlopen de urllib2 también puede tomar como parámetro un objeto Request, en lugar de la URL y los datos a enviar.

    La clase Request define objetos que encapsulan toda la información relativa a una petición. A través de este objeto podemos realizar peticiones más complejas, añadiendo nuestras propias cabeceras, como el User-Agent.

    El constructor más sencillo para el objeto Request no toma más que una cadena indicando la URL a la que conectarse, por lo que utilizar este objeto como parámetro de urlopen sería equivalente a utilizar una cadena con la URL directamente.

    Sin embargo el constructor de Request también tiene como parámetros opcionales una cadena data para mandar datos por POST y un diccionario headers con las cabeceras (además de un par de campos origin_req_host y unverifiable, que quedan fuera del propósito del capítulo por ser de raro uso).

    Veamos cómo añadir nuestras propias cabeceras utilizando como ejemplo la cabecera User-Agent. El User-Agent es una cabecera que sirve para identificar el navegador y sistema operativo que estamos utilizando para conectarnos a esa URL. Por defecto urllib2 se identifica
    como “Python-urllib/2.5”; si quisiéramos identificarnos como un Linux corriendo Konqueror por ejemplo, usaríamos un código similar al siguiente:

    ua = “Mozilla/5.0 (compatible; Konqueror/3.5.8; Linux)”
    h = {“User-Agent”: ua}
    r = urllib2.Request(“http://www.python.org”, headers=h)
    f = urllib2.urlopen(r)
    print f.read()
    Para personalizar la forma en que trabaja urllib2 podemos instalar un grupo de manejadores (handlers) agrupados en un objeto de la clase OpenerDirector (opener o abridor), que será el que se utilice a partir de ese momento al llamar a urlopen.

    Para construir un opener se utiliza la función build_opener a la que se le pasa los manejadores que formaran parte del opener. El opener se encargara de encadenar la ejecución de los distintos manejadores en el orden dado. También se puede usar el constructor de OpenerDirector, y añadir los manejadores usando su método add_handler.

    Para instalar el opener una vez creado se utiliza la función install_
    opener, que toma como parámetro el opener a instalar. También se podría, si sólo queremos abrir la URL con ese opener una sola vez, utilizar el método open del opener.

    urllib2 cuenta con handlers que se encargan de manejar los es quemas disponibles (HTTP, HTTPS, FTP), manejar la autenticación, manejar las redirecciones, etc.

    Para añadir autenticación tendríamos que instalar un opener que incluyera como manejador HTTPBasicAuthHandler, ProxyBasicAuthHandler, HTTPDigestAuthHandler y/o ProxyDigestAuthHandler.

    Para utilizar autenticación HTTP básica, por ejemplo, usaríamos

    HTTPBasicAuthHandler:
    aut_h = urllib2.HTTPBasicAuthHandler()
    aut_h.add_password(“realm”, “host”, “usuario”, “password”)
    opener = urllib2.build_opener(aut_h)
    urllib2.install_opener(opener)
    f = urllib2.urlopen(“http://www.python.org”)
    Si quisiéramos especificar un proxy en el código tendríamos que utilizar un opener que contubiera el manejador ProxyHandler. El manejador por defecto incluye una instacia de ProxyHandler construido llamando al inicializador sin parámetros, con lo que se lee la lista de proxies a utilizar de la variable de entorno adecuada. Sin embargo también podemos construir un ProxyHandler pasando como parámetro al inicializador un diccionario cuyas claves son los protocolos y los valores, la URL del proxy a utilizar para dicho protocolo.

    proxy_h = urllib2.ProxyHandler({“http” : “http://miproxy.net:123”})
    opener = urllib2.build_opener(proxy_h)
    urllib2.install_opener(opener)
    f = urllib2.urlopen(“http://www.python.org”)
    Para que se guarden las cookies que manda HTTP utilizamos el manejador HTTPCookieProcessor.

    cookie_h = urllib2.HTTPCookieProcessor()
    opener = urllib2.build_opener(cookie_h)
    urllib2.install_opener(opener)
    f = urllib2.urlopen(“http://www.python.org”)
    Si queremos acceder a estas cookies o poder mandar nuestras propias cookies, podemos pasarle como parámetro al inicializador de HTTPCookieProcessor un objeto de tipo CookieJar del módulo cookielib.

    Para leer las cookies mandadas basta crear un objeto iterable a partir del CookieJar (también podríamos buscar las cabeceras correspondientes, pero este sistema es más claro y sencillo):

    import urllib2, cookielib
    cookie_j = cookielib.CookieJar()
    cookie_h = urllib2.HTTPCookieProcessor(cookie_j)
    opener = urllib2.build_opener(cookie_h)
    opener.open(“http://www.python.org”)
    for num, cookie in enumerate(cookie_j):
    print num, cookie.name
    print cookie.value
    print
    En el improbable caso de que necesitaramos añadir una cookie antes
    de realizar la conexión, en lugar de conectarnos para que el sitio la mande, podríamos utilizar el método set_cookie de CookieJar, al que le pasamos un objeto de tipo Cookie. El constructor de Cookie, no obstante, es bastante complicado.

  2. #17
    Administrador y fundador. Avatar de 3dpoder
    Fecha de ingreso
    Apr 2002
    Mensajes
    15,459

    Post Threads - Python para todos - por Raúl González Duque

    Threads


    ¿Qué son los procesos y los threadsí
    Las computadoras serían mucho menos útiles si no pudiéramos hacer más de una cosa a la vez. Si no pudiéramos, por ejemplo, escuchar música en nuestro reproductor de audio favorito mientras leemos un tutorial de Python en Mundo Geek.

    Pero, ¿cómo se conseguía esto en computadoras antiguas con un solo núcleo / una sola CPU? Lo que ocurría, y lo que ocurre ahora, es que en realidad no estamos ejecutando varios procesos a la vez (se llama proceso a un programa en ejecución), sino que los procesos se van turnando y, dada la velocidad a la que ejecutan las instrucciones, nosotros tenemos la impresión de que las tareas se ejecutan de forma paralela como si tuviéramos multitarea real.

    Cada vez que un proceso distinto pasa a ejecutarse es necesario realizar lo que se llama un cambio de contexto, durante el cual se salva el estado del programa que se estaba ejecutando a memoria y se carga el estado del programa que va a entrar a ejecutarse.

    En Python podemos crear nuevos procesos mediante la función os.fork, que ejecuta la llamada al sistema fork, o mediante otras funciones más avanzadas como popen2.popen2, de forma que nuestro programa pueda realizar varias tareas de forma paralela.

    Sin embargo el cambio de contexto puede ser relativamente lento, y los recursos necesarios para mantener el estado demasiados, por lo que a menudo es mucho más eficaz utilizar lo que se conoce como threads, hilos de ejecución, o procesos ligeros.

    Los threads son un concepto similar a los procesos: también se trata de código en ejecución. Sin embargo los threads se ejecutan dentro de un proceso, y los threads del proceso comparten recursos entre si, como la memoria, por ejemplo.

    El sistema operativo necesita menos recursos para crear y gestionar los threads, y al compartir recursos, el cambio de contexto es más rápido. Además, dado que los threads comparten el mismo espacio de memoria global, es sencillo compartir información entre ellos: cualquier variable global que tengamos en nuestro programa es vista por todos los threads.

    El GIL
    La ejecución de los threads en Python esta controlada por el GIL (Global Interpreter Lock) de forma que sólo un thread puede ejecutarse a la vez, independientemente del número de procesadores con el que cuente la máquina. Esto posibilita que el escribir extensiones en C para Python sea mucho más sencillo, pero tiene la desventaja de limitar mucho el rendimiento, por lo que a pesar de todo, en Python, en ocasiones nos puede interesar más utilizar procesos que threads, que no sufren de esta limitación.

    Cada cierto número de instrucciones de bytecode la máquina virtual para la ejecución del thread y elige otro de entre los que estaban esperando.

    Por defecto el cambio de thread se realiza cada 10 instrucciones de bytecode, aunque se puede modificar mediante la función sys.setcheckinterval. También se cambia de thread cuando el hilo se pone a dormir con time.sleep o cuando comienza una operación de entrada/salida, las cuales pueden tardar mucho en finalizar, y por lo tanto, de no realizar el cambio, tendríamos a la CPU demasiado tiempo sin trabajar esperando a que la operación de E/S terminara.

    Para minimizar un poco el efecto del GIL en el rendimiento de nuestra
    aplicación es conveniente llamar al intérprete con el flag -O, lo que hará que se genere un bytecode optimizado con menos instrucciones, y, por lo tanto, menos cambios de contexto. También podemos plantearnos el utilizar procesos en lugar de threads, como ya comentamos, utilizando por ejemplo el módulo processing; escribir el código en el que el rendimiento sea crítico en una extensión en C o utilizar IronPython o Jython, que carecen de GIL.

    Threads en Python
    El trabajo con threads se lleva a cabo en Python mediante el módulo thread. Este módulo es opcional y dependiente de la plataforma, y puede ser necesario, aunque no es común, recompilar el intérprete para añadir el soporte de threads.

    Además de thread, también contamos con el módulo threading que se apoya en el primero para proporcionarnos una API de más alto nivel, más completa, y orientada a objetos. El módulo threading se basa ligeramente en el modelo de threads de Java.

    El módulo threading contiene una clase Thread que debemos extender para crear nuestros propios hilos de ejecución. El método run contendra el código que queremos que ejecute el thread. Si queremos especificar nuestro propio constructor, este debera llamar a threading.Thread.__init__(self) para inicializar el objeto correctamente.

    import threading
    class MiThread(threading.Thread):
    def __init__(self, num):
    threading.Thread.__init__(self)
    self.num = num
    def run(self):
    print “Soy el hilo”, self.num
    Para que el thread comience a ejecutar su código basta con crear una instancia de la clase que acabamos de definir y llamar a su método start. El código del hilo principal y el del que acabamos de crear se ejecutaran de forma concurrente.

    print “Soy el hilo principal”
    for i in range(0, 10):
    t = MiThread(i)
    t.start()
    t.join()
    El método join se utiliza para que el hilo que ejecuta la llamada se bloquee hasta que finalice el thread sobre el que se llama. En este caso se utiliza para que el hilo principal no termine su ejecución antes que los hijos, lo cual podría resultar en algunas plataformas en la terminación de los hijos antes de finalizar su ejecución. El método join puede tomar como parámetro un número en coma flotante indicando el número máximo de segundos a esperar.

    Si se intenta llamar al método start para una instancia que ya se esta ejecutando, obtendremos una excepción.

    La forma recomendada de crear nuevos hilos de ejecución consiste en extender la clase Thread, como hemos visto, aunque también es posible crear una instancia de Thread directamente, e indicar como parámetros del constructor una clase ejecutable (una clase con el método especial __call__) o una función a ejecutar, y los argumentos en una tupla (parámetro args) o un diccionario (parámetro kwargs).

    import threading
    def imprime(num):
    print “Soy el hilo”, num
    print “Soy el hilo principal”
    for i in range(0, 10):
    t = threading.Thread(target=imprime, args=(i, ))
    t.start()
    Además de los parámetros target, args y kwargs también podemos pasar al constructor un parámetro de tipo cadena name con el nombre que queremos que tome el thread (el thread tendrá un nombre predeterminado aunque no lo especifiquemos); un parámetro de tipo booleano verbose para indicar al módulo que imprima mensajes sobre el estado de los threads para la depuración y un parámetro group, que por ahora no admite ningún valor pero que en el futuro se utilizara para crear grupos de threads y poder trabajar a nivel de grupos.

    Para comprobar si un thread sigue ejecutandose, se puede utilizar el método isAlive. También podemos asignar un nombre al hilo y consultar su nombre con los métodos setName y getName, respectivamente.

    Mediante la función threading.enumerate obtendremos una lista de los objetos Thread que se están ejecutando, incluyendo el hilo principal (podemos comparar el objeto Thread con la variable main_thread para comprobar si se trata del hilo principal) y con threading.activeCount podemos consultar el número de threads ejecutandose.

    Los objetos Thread también cuentan con un método setDaemon que toma un valor booleano indicando si se trata de un demonio. La utilidad de esto es que si solo quedan threads de tipo demonio ejecutandose, la aplicación terminara automáticamente, terminando estos threads de forma segura.

    Por último tenemos en el módulo threading una clase Timer que hereda de Thread y cuya utilidad es la de ejecutar el código de su método run después de un período de tiempo indicado como parámetro en su constructor. También incluye un método cancel mediante el que cancelar la ejecución antes de que termine el período de espera.


    Sincronización

    Uno de los mayores problemas a los que tenemos que enfrentarnos al utilizar threads es la necesidad de sincronizar el acceso a ciertos recursos por parte de los threads. Entre los mecanismos de sincronización que tenemos disponibles en el módulo threading se encuentran los locks, locks reentrantes, semaforos, condiciones y eventos.

    Los locks, también llamados mutex (de mutual exclusion), cierres de exclusión mutua, cierres o candados, son objetos con dos estados posibles: adquirido o libre. Cuando un thread adquiere el candado, los demás threads que lleguen a ese punto posteriormente y pidan adquirirlo se bloquearan hasta que el thread que lo ha adquirido libere el candado, momento en el cual podrá entrar otro thread.

    El candado se representa mediante la clase Lock. Para adquirir el candado se utiliza el método acquire del objeto, al que se le puede pasar un booleano para indicar si queremos esperar a que se libere (True) o no (False). Si indicamos que no queremos esperar, el método devolvera True o False dependiendo de si se adquirió o no el candado, respectivamente. Por defecto, si no se indica nada, el hilo se bloquea indefinidamente.

    Para liberar el candado una vez hemos terminado de ejecutar el bloque de código en el que pudiera producirse un problema de concurrencia, se utiliza el método release.

    lista = []
    lok = threading.Lock()
    def anyadir(obj):
    lock.acquire()
    lista.append(obj)
    lock.release()
    def obtener():
    lock.acquire()
    obj = lista.pop()
    lock.release()
    return obj
    La clase RLok funciona de forma similar a Lock, pero en este caso el candado puede ser adquirido por el mismo thread varias veces, y no quedará liberado hasta que el thread llame a release tantas veces como llamó a acquire. Como en Lock, y como en todas las primitivas de sincronización que veremos a continuación, es posible indicar a acquire si queremos que se bloquee o no.

    Los semaforos son otra clase de candados. La clase correspondiente, Semaphore, también cuenta con métodos acquire y release, pero se diferencia de un Lok normal en que el constructor de Semaphore puede tomar como parámetro opcional un entero value indicando el número máximo de threads que pueden acceder a la vez a la sección de código crítico. Si no se indica nada permite el acceso a un solo thread.

    Cuando un thread llama a acquire, la variable que indica el número de threads que pueden adquirir el semaforo disminuye en 1, porque hemos permitido entrar en la sección de código crítico a un hilo más. Cuando un hilo llama a release, la variable aumenta en 1.

    No es hasta que esta variable del semaforo es 0, que llamar a acquire producira un bloqueo en el thread que realizó la petición, a la espera de que algún otro thread llame a release para liberar su plaza.

    Es importante destacar que el valor inicial de la variable tal como lo pasamos en el constructor, no es un límite máximo, sino que múltiples llamadas a release pueden hacer que el valor de la variable sea mayor que su valor original. Si no es esto lo que queremos, podemos utilizar la clase BoundedSemaphore en cuyo caso, ahora si, se consideraría un error llamar a release demasiadas veces, y se lanzaría una excepción de tipo ValueError de superarse el valor inicial.

    Podríamos utilizar los semaforos, por ejemplo, en un pequeño programa en el que múltiples threads descargaran datos de una URL, de forma que pudieramos limitar el número de conexiones a realizar al sitio web para no bombardear el sitio con cientos de peticiones concurrentes.

    semaforo = threading.Semaphore(4)
    def descargar(url):
    semaforo.acquire()
    urllib.urlretrieve(url)
    semaforo.release()
    Las condiciones (clase Condition) son de utilidad para hacer que los threads sólo puedan entrar en la sección crítica de darse una cierta condición o evento. Para esto utilizan un Lok pasado como parámetro, o crean un objeto RLok automáticamente si no se pasa ningún parámetro al constructor.

    Son especialmente adecuadas para el clasico problema de productor-consumidor. La clase cuenta con métodos acquire y release, que llamaran a los métodos correspondientes del candado asociado. También tenemos métodos wait, notify y notifyAll.

    El método wait debe llamarse después de haber adquirido el candado con acquire. Este método libera el candado y bloquea al thread hasta que una llamada a notify o notifyAll en otro thread le indican que se ha cumplido la condición por la que esperaba. El thread que informa a los demás de que se ha producido la condición, también debe llamar a acquire antes de llamar a notify o notifyAll.

    Al llamar a notify, se informa del evento a un solo thread, y por tanto se despierta un solo thread. Al llamar a notifyAll se despiertan todos los threads que esperaban a la condición.

    Tanto el thread que notifica como los que son notificados tienen que terminar liberando el lok con release.

    lista = []
    cond = threading.Condition()
    def consumir():
    cond.acquire()
    cond.wait()
    obj = lista.pop()
    cond.release()
    return obj
    def producir(obj):
    cond.acquire()
    lista.append(obj)
    cond.notify()
    cond.release()
    Los eventos, implementados mediante al clase Event, son un wrapper
    por encima de Condition y sirven principalmente para coordinar threads mediante señales que indican que se ha producido un evento. Los eventos nos abstraen del hecho de que estemos utilizando un Lok por debajo, por lo que carecen de métodos acquire y release.

    El thread que debe esperar el evento llama al método wait y se bloquea, opcionalmente pasando como parámetro un número en coma flotante indicando el número máximo de segundos a esperar. Otro hilo, cuando ocurre el evento, manda la señal a los threads bloqueados a la espera de dicho evento utilizando el método set. Los threads que estaban esperando se desbloquean una vez recibida la señal. El flag que determina si se ha producido el evento se puede volver a establecer a falos usando clear.

    Como vemos los eventos son muy similares a las condiciones, a excepción de que se desbloquean todos los threads que esperaban el evento y que no tenemos que llamar a acquire y release.

    import threading, time
    class MiThread(threading.Thread):
    def __init__(self, evento):
    threading.Thread.__init__(self)
    self.evento = evento
    def run(self):
    print self.getName(), “esperando al evento”
    self.evento.wait()
    print self.getName(), “termina la espera”
    evento = threading.Event()
    t1 = MiThread(evento)
    t1.start()
    t2 = MiThread(evento)
    t2.start()
    # Esperamos un poco
    time.sleep(5)
    evento.set()
    Por último, un pequeño extra. Si sois usuarios de Java sin duda estaréis echando en falta una palabra clave syncronized para hacer que sólo un thread pueda acceder al método sobre el que se utiliza a la vez. Una construcción común es el uso de un decorador para implementar esta funcionalidad usando un Lock. Sería algo así:

    def synchronized(lock):
    def dec(f):
    def func_dec(*args, **kwargs):
    lock.acquire()
    try:
    return f(*args, **kwargs)
    finally:
    lock.release()
    return func_dec
    return dec
    class MyThread(threading.Thread):
    @synchronized(mi_lock)
    def run(self):
    print “método sincronizado”

    Datos globales independientes

    Como ya hemos comentado los threads comparten las variables globales. Sin embargo pueden existir situaciones en las que queramos utilizar variables globales pero que estas variables se comporten como si fueran locales a un solo thread. Es decir, que cada uno de los threads tengan valores distintos independientes, y que los cambios de un determinado thread sobre el valor no se vean reflejados en las copias de los demás threads.

    Para lograr este comportamiento se puede utilizar la clase threading.local, que crea un almacén de datos locales. Primero debemos crear una instancia de la clase, o de una subclase, para después almacenar y obtener los valores a través de parámetros de la clase.

    datos_locales = threading.local()
    datos_locales.mi_var = “hola”
    print datos_locales.mi_var
    Fijémonos en el siguiente código, por ejemplo. Para el hilo principal el objeto local tiene un atributo var, y por lo tanto el print imprime su valor sin problemas. Sin embargo para el hilo te ese atributo no existe, y por lo tanto lanza una excepción.

    local = threading.local()
    def f():
    print local.var
    local.var = “hola”
    t = threading.Thread(target=f)
    print local.var
    t.start()
    t.join()
    Compartir información
    Para compartir información entre los threads de forma sencilla podemos utilizar la clase Queue.Queue, que implementa una cola (una estructura de datos de tipo FIFO) con soporte multihilo. Esta clase utiliza las primitivas de threading para ahorrarnos tener que sincronizar el acceso a los datos nosotros mismos.

    El constructor de Queue toma un parámetro opcional indicando el tamaño máximo de la cola. Si no se indica ningún valor no hay límite de tamaño.

    Para añadir un elemento a la cola se utiliza el método put(item); para obtener el siguiente elemento, get(). Ambos métodos tienen un parámetro booleano opcional blok indica si queremos que se espere hasta que haya algún elemento en la cola para poder devolverlo o hasta que la cola deje de estar llena para poder introducirlo.

    También existe un parámetro opcional timeout que indica, en segundos, el tiempo máximo a esperar. Si el timeout acaba sin poder haber realizado la operación debido a que la cola estaba llena o vacía, o bien si blok era False, se lanzara una excepción de tipo Queue.Full o Queue.Empty, respectivamente.

    Con qsize obtenemos el tamaño de la cola y con empty() y full() podemos comprobar si esta vacía o llena.

    que = Queue.Queue()
    class MiThread(threading.Thread):
    def __init__(self, q):
    self.que = q
    threading.Thread.__init__(self)
    def run(self):
    while True:
    try:
    obj = q.get(False)
    except Queue.Empty:
    print “Fin”
    break
    print obj
    for i in range(10):
    q.put(i)
    t = MiThread(q)
    t.start()
    t.join()

  3. #18
    Administrador y fundador. Avatar de 3dpoder
    Fecha de ingreso
    Apr 2002
    Mensajes
    15,459

    Post Serialización de objetivos - Python para todos - por Raúl González Duque

    Serialización de objetos


    Algunas veces tenemos la necesidad de guardar un objeto a disco para poder recuperarlo más tarde, o puede que nos sea necesario mandar un objeto a través de la red, a otro programa en Python ejecutandose en otra máquina.

    Al proceso de transformar el estado de un objeto en un formato que se pueda almacenar, recuperar y transportar se le conoce con el nombre de serialización o marshalling.

    En Python tenemos varios módulos que nos facilitan esta tarea, como marshal, pickle, cPickle y shelve.

    El módulo marshal es el más básico y el más primitivo de los tres, y es que, de hecho, su propósito principal y su razón de ser no es el de serializar objetos, sino trabajar con bytecode Python (archivos .pyc).

    marshal sólo permite serializar objetos simples (la mayoría de los tipos incluidos por defecto en Python), y no proporciona ningún tipo de mecanismo de seguridad ni comprobaciones frente a datos corruptos o mal formateados. Es más, el formato utilizado para guardar el bytecode (y por tanto el formato utilizado para guardar los objetos con marshal) puede cambiar entre versiones, por lo que no es adecuado para almacenar datos de larga duración.

    pickle, por su parte, permite serializar casi cualquier objeto (objetos de tipos definidos por el usuario, colecciones que contienen colecciones, etc) y cuenta con algunos mecanismos de seguridad básicos. Sin embargo, al ser más complejo que marshal, y, sobre todo, al estar escrito en Python en lugar de en C, como marshal, también es mucho más lento.

    La solución, si la velocidad de la serialización es importante para nuestra aplicación, es utilizar cPickle, que no es más que es una implementación en C de pickle. cPickle es hasta 1000 veces más rápido que pickle, y prácticamente igual de rápido que marshal.

    Si intentamos importar cPickle y se produce un error por algún motivo, se lanzara una excepción de tipo ImportError. Para utilizar cPickle si esta disponible y pickle en caso contrario, podríamos usar un código similar al siguiente:

    try:
    import cPickle as pickle
    except ImportError:
    import pickle
    as en un import sirve para importar el elemento seleccionado utilizando otro nombre indicado, en lugar de su nombre.

    La forma más sencilla de serializar un objeto usando pickle es mediante una llamada a la función dump pasando como argumento el objeto a serializar y un objeto archivo en el que guardarlo (o cualquier otro tipo de objeto similar a un archivo, siempre que ofrezca métodos read, realine y write).

    try:
    import cPickle as pickle
    except ImportError:
    import pickle
    fichero = file(“datos.dat”, “w”)
    animales = [“piton”, “mono”, “camello”]
    pickle.dump(animales, fichero)
    fichero.close()
    La función dump también tiene un parámetro opcional protocol que indica el protocolo a utilizar al guardar. Por defecto su valor es 0, que utiliza formato texto y es el menos eficiente. El protocolo 1 es más eficiente que el 0, pero menos que el 2. Tanto el protocolo 1 como el 2 utilizan un formato binario para guardar los datos.

    try:
    import cPickle as pickle
    except ImportError:
    import pickle
    fichero = file(“datos.dat”, “w”)
    animales = [“piton”, “mono”, “camello”]
    pickle.dump(animales, fichero, 2)
    fichero.close()
    Para volver a cargar un objeto serializado se utiliza la función load, a la que se le pasa el archivo en el que se guardó.

    try:
    import cPickle as pickle
    except ImportError:
    import pickle
    fichero = file(“datos.dat”, “w”)
    animales = [“piton”, “mono”, “camello”]
    pickle.dump(animales, fichero)
    fichero.close()
    fichero = file(“datos.dat”)
    animales2 = pickle.load(fichero)
    print animales2
    Supongamos ahora que queremos almacenar un par de listas en un fichero. Esto sería tan sencillo como llamar una vez a dump por cada lista, y llamar después una vez a load por cada lista.

    fichero = file(“datos.dat”, “w”)
    animales = [“piton”, “mono”, “camello”]
    lenguajes = [“python”, “mono”, “perl”]
    pickle.dump(animales, fichero)
    pickle.dump(lenguajes, fichero)
    fichero = file(“datos.dat”)
    animales2 = pickle.load(fichero)
    lenguajes2 = pickle.load(fichero)
    print animales2
    print lenguajes2
    Pero, ¿y si hubiéramos guardado 30 objetos y quisiéramos acceder al último de ellos? ¿o si no recordaramos en qué posición lo habíamos guardado? El módulo shelve extiende pickle / cPickle para proporcionar una forma de realizar la serialización más clara y sencilla, en la que podemos acceder a la versión serializada de un objeto mediante una cadena asociada, a través de una estructura parecida a un diccionario.

    La única función que necesitamos conocer del módulo shelve es open, que cuenta con un parámetro filename mediante el que indicar la ruta a un archivo en el que guardar los objetos (en realidad se puede crear más de un archivo, con nombres basados en filename, pero esto es transparente al usuario).

    La función open también cuenta con un parámetro opcional protocol, con el que especificar el protocolo que queremos que utilice pickle por debajo.

    Como resultado de la llamada a open obtenemos un objeto Shelf, con el que podemos trabajar como si de un diccionario normal se tratase (a excepción de que las claves sólo pueden ser cadenas) para almacenar y recuperar nuestros objetos.

    Como un diccionario cualquiera la clase Shelf cuenta con métodos get, has_key, items, keys, values, …

    Una vez hayamos terminado de trabajar con el objeto Shelf, lo cerraremos utilizando el método close.

    import shelve
    animales = [“piton”, “mono”, “camello”]
    lenguajes = [“python”, “mono”, “perl”]
    shelf = shelve.open(“datos.dat”)
    shelf[“primera”] = animales
    shelf[“segunda”] = lenguajes
    print shelf[“segunda”]
    shelf.close()

  4. #19
    Administrador y fundador. Avatar de 3dpoder
    Fecha de ingreso
    Apr 2002
    Mensajes
    15,459

    Post Bases de datos - Python para todos - por Raúl González Duque

    Bases de Datos


    Existen problemas para los que guardar nuestros datos en ficheros de texto plano, en archivos XML, o mediante serialización con pickle o shelve pueden ser soluciones poco convenientes. En ocasiones no queda más remedio que recurrir a las bases de datos, ya sea por cuestiones de escalabilidad, de interoperabilidad, de coherencia, de seguridad, de confidencialidad, etc.

    A lo largo de este capítulo aprenderemos a trabajar con bases de datos en Python. Sin embargo se asumen una serie de conocimientos básicos, como puede ser el manejo elemental de SQL. Si este no es el caso, existen miles de recursos a disposición del lector en Internet para introducirse en el manejo de bases de datos.

    DB API
    Existen cientos de bases de datos en el mercado, tanto comerciales como gratuitas. También existen decenas de módulos distintos para trabajar con dichas bases de datos en Python, lo que significa decenas de APIs distintas por aprender.

    En Python, como en otros lenguajes como Java con JDBC, existe una propuesta de API estándar para el manejo de bases de datos, de forma que el código sea prácticamente igual independientemente de la base de datos que estemos utilizando por debajo. Esta especificación recibe el nombre de Python Database API o DB-API y se recoge en el PEP 249 (http://www.python.org/dev/peps/pep-0249/).

    DB-API se encuentra en estos momentos en su versión 2.0, y existen implementaciones para las bases de datos relacionales más conocidas, así como para algunas bases de datos no relacionales.

    A lo largo de este capítulo utilizaremos la base de datos SQLite para los ejemplos, ya que no se necesita instalar y ejecutar un proceso servidor independiente con el que se comunique el programa, sino que se trata de una pequeña librería en C que se integra con la aplicación y que viene incluida con Python por defecto desde la versión 2.5. Desde la misma versión Python también incorpora un módulo compatible con esta base de datos que sigue la especificación de DB API 2.0: sqlite3, por lo que no necesitaremos ningún tipo de configuración extra.

    Nada impide al lector, no obstante, instalar y utilizar cualquier otra base de datos, como MySQL, con la cual podemos trabajar a través del driver compatible con DB API 2.0 MySQLdb (http://mysql-python.sourceforge.net/).

    Variables globales
    Antes de comenzar a trabajar con sqlite3, vamos a consultar algunos datos interesantes sobre el módulo. Todos los drivers compatibles con DB-API 2.0 deben tener 3 variables globales que los describen. A saber:


    • apilevel : una cadena con la versión de DB API que utiliza. Actualmente sólo puede tomar como valor “1.0” o “2.0”. Si la variable no existe se asume que es 1.0.
    • threadsafety : se trata de un entero de 0 a 3 que describe lo seguro que es el módulo para el uso con threads. Si es 0 no se puede compartir el módulo entre threads sin utilizar algún tipo de mecanismo de sincronización; si es 1, pueden compartir el módulo pero no las conexiones; si es 2, módulos y conexiones pero no cursores y, por último, si es 3, es totalmente thread-safe.
    • paramstyle: informa sobre la sintaxis a utilizar para insertar valores en la consulta SQL de forma dinamica.


    qmarkɣɣ: interrogaciones.
    sql = “select all from te where valor=?”

    numericɣɣ: un número indicando la posición.
    sql = “select all from te where valor=:1”

    namedɣɣ: el nombre del valor.

    formatɣɣ: especificadores de formato similares a los del printf de C.

    sql = “select all from te where valor=%s”

    pyformatɣɣ: similar al anterior, pero con las extensiones de Python.

    sql = “select all from te where valor=%(valor)”

    Veamos los valores correspondientes a sqlite3:

    >>> import sqlite3 as dbapi
    >>> print dbapi.apilevel
    2.0
    >>> print dbapi.threadsafety
    1
    >>> print dbapi.paramstyle
    qmark
    Excepciones
    A continuación podéis encontrar la jerarquía de excepciones que deben proporcionar los módulos, junto con una pequeña descripción de cada excepción, a modo de referencia.

    --StandardError
    --|__Warning
    --|__Error
    ----|__InterfaceError
    ----|__DatabaseError
    ------|__DataError
    ------|__OperationalError
    ------|__IntegrityError
    ------|__InternalError
    ------|__ProgrammingError
    ------|__NotSupportedError

    StandardError• : Super clase para todas las excepciones de DB API.

    Warning• : Excepción que se lanza para avisos importantes.

    Error• : Super clase de los errores.

    InterfaceError• : Errores relacionados con la interfaz de la base de datos, y no con la base de datos en sí.

    DatabaseError• : Errores relacionados con la base de datos.

    DataError• : Errores relacionados con los datos, como una división entre cero.

    OperationalError• : Errores relacionados con el funcionamiento de la base de datos, como una desconexión inesperada.

    IntegrityError• : Errores relacionados con la integridad referencial.

    InternalError• : Error interno de la base de datos.

    ProgrammingError• : Errores de programación, como errores en el código SQL.

    NotSupportedError• : Excepción que se lanza cuando se solicita un método que no esta soportado por la base de datos.

    Uso básico de DB-API
    Pasemos ahora a ver cómo trabajar con nuestra base de datos a través de DB-API.

    Lo primero que tendremos que hacer es realizar una conexión con el servidor de la base de datos. Esto se hace mediante la función connect, cuyos parámetros no están estandarizados y dependen de la base de datos a la que estemos conectandonos.

    En el caso de sqlite3 sólo necesitamos pasar como parámetro una cadena con la ruta al archivo en el que guardar los datos de la base de datos, o bien la cadena “:memory:” para utilizar la memoria RAM en lugar de un fichero en disco.

    Por otro lado, en el caso de MySQLdb, connect toma como parámetros la máquina en la que corre el servidor (host), el puerto (port), nombre de usuario con el que autenticarse (user), contraseña (password) y base de datos a la que conectarnos de entre las que se encuentran en nuestro SGBD (db).

    La función connect devuelve un objeto de tipo Connection que representa la conexión con el servidor.

    >>> bbdd = dbapi.connect(“bbdd.dat”)
    >>> print bbdd
    <sqlite3.Connection object at 0x00A71DA0>
    Las distintas operaciones que podemos realizar con la base de datos se realizan a través de un objeto Cursor. Para crear este objeto se utiliza el método cursor() del objeto Connection:

    c = bbdd.cursor()
    Las operaciones se ejecutan a través del método execute de Cursor, pasando como parámetro una cadena con el código SQL a ejecutar.

    Como ejemplo creemos una nueva tabla empleados en la base de datos:

    c.execute(“””create table empleados (dni text,
    nombre text,
    departamento text)”””)
    y a continuación, insertemos una tupla en nuestra nueva tabla:

    c.execute(“””insert into empleados values (‘12345678-A’, ‘Manuel Gil’, ‘Contabilidad’)”””)
    Si nuestra base de datos soporta transacciones, si estas están activadas, y si la característica de auto-commit esta desactivada, será necesario llamar al método commit de la conexión para que se lleven a cabo las operaciones definidas en la transacción.

    Si en estas circunstancias utilizaramos una herramienta externa para comprobar el contenido de nuestra base de datos sin hacer primero el commit nos encontraríamos entonces con una base de datos vacía.

    Si comprobaramos el contenido de la base de datos desde Python, sin cerrar el cursor ni la conexión, recibiríamos el resultado del contexto de la transacción, por lo que parecería que se han llevado a cabo los cambios, aunque no es así, y los cambios sólo se aplican, como comentamos, al llamar a commit.

    Para bases de datos que no soporten transacciones el estándar dicta
    que debe proporcionarse un método commit con implementación vacía, por lo que no es mala idea llamar siempre a commit aunque no sea necesario para poder cambiar de sistema de base de datos con solo modificar la línea del import.

    Si nuestra base de datos soporta la característica de rollbak también podemos cancelar la transacción actual con:

    bbdd.rollback()
    Si la base de datos no soporta rollbak llamar a este método producira una excepción.

    Veamos ahora un ejemplo completo de uso:

    import sqlite3 as dbapi
    bbdd = dbapi.connect(“bbdd.dat”)
    cursor = bbdd.cursor()
    cursor.execute(“””create table empleados (dni text,
    nombre text,
    departamento text)”””)
    cursor.execute(“””insert into empleados
    values (‘12345678-A’, ‘Manuel Gil’, ‘Contabilidad’)”””)
    bbdd.commit()
    cursor.execute(“””select * from empleados
    where departamento=’Contabilidad’”””)
    for tupla in cursor.fetchall():
    print tupla
    Como vemos, para realizar consultas a la base de datos también se utiliza execute. Para consultar las tuplas resultantes de la sentencia SQL se puede llamar a los métodos de Cursor fetchone, fetchmany o fetchall o usar el objeto Cursor como un iterador.

    cursor.execute(“””select * from empleados
    where departamento=’Contabilidad’”””)
    for resultado in cursor:
    print tupla
    El método fetchone devuelve la siguiente tupla del conjunto resultado o None cuando no existen más tuplas, fetchmany devuelve el número de tuplas indicado por el entero pasado como parámetro o bien el número indicado por el atributo Cursor.arraysize si no se pasa ningún parámetro (Cursor.arraysize vale 1 por defecto) y fetchall devuelve un objeto iterable con todas las tuplas.

    A la hora de trabajar con selects u otros tipos de sentencias SQL es importante tener en cuenta que no deberían usarse los métodos de cadena habituales para construir las sentencias, dado que esto nos haría vulnerables a ataques de inyección SQL, sino que en su lugar debe usarse la característica de sustitución de parámetros de DB API.

    Supongamos que estamos desarrollando una aplicación web con Python para un banco y que se pudiera consultar una lista de sucursales del banco en una ciudad determinada con una URL de la forma http://www.mibanco.com/sucursalesíciudad=Madrid

    Podríamos tener una consulta como esta:

    cursor.execute(“””select * from sucursales
    where ciudad=’” + ciudad + “’”””)
    A primera vista podría parecer que no existe ningún problema: no hacemos más que obtener las sucursales que se encuentren en la ciudad indicada por la variable ciudad. Pero, ¿qué ocurriría si un usuario malintencionado accediera a una URL como “http://www.mibanco.com/sucursalesíciudad=Madrid’;SELECT * FROM contrasenyas”?

    Como no se realiza ninguna validación sobre los valores que puede contener la variable ciudad, sería sencillo que alguien pudiera hacerse con el control total de la aplicación.

    Lo correcto sería, como decíamos, utilizar la característica de sustitución de parámetros de DB API. El valor de paramstyle para el módulo sqlite3 era qmark. Esto significa que debemos escribir un signo de interrogación en el lugar en el que queramos insertar el valor, y basta pasar un segundo parámetro a execute en forma de secuencia o mapping con los valores a utilizar para que el módulo cree la sentencia por nosotros.

    cursor.execute(“””select * from sucursales
    where ciudad=?”””, (ciudad,))
    Por último, al final del programa se debe cerrar el cursor y la conexion:
    cursor.close()
    bbdd.close()

    Tipos SQL

    En ocasiones podemos necesitar trabajar con tipos de SQL, y almacenar, por ejemplo, fechas u horas usando Date y Time y no con cadenas. La API de bases de datos de Python incluye una serie de constructores a utilizar para crear estos tipos. Estos son:

    Date(year, month, day) : Para almacenar fechas.

    Time(hour, minute, second) : Para almacenar horas.

    Timestamp(year, month, day, hour, minute, second) : Para almacenar timestamps (una fecha con su hora).

    DateFromTicks(ticks) : Para crear una fecha a partir de un número con los segundos transcurridos desde el epoch (el 1 de Enero de 1970 a las 00:00:00 GMT).

    TimeFromTicks(ticks) : Similar al anterior, para horas en lugar de fechas.

    TimestampFromTicks(ticks)• : Similar al anterior, para timestamps.
    Binary(string) : Valor binario.

    Otras opciones
    Por supuesto no estamos obligados a utilizar DB-API, ni bases de datos relacionales. En Python existen módulos para trabajar con bases de datos orientadas a objetos, como ZODB (Zope Object Database) y motores para mapeo objeto-relacional (ORM) como SQLAlchemy, SQLObject o Storm.

    Además, si utilizamos IronPython en lugar de CPython tenemos la posibilidad de utilizar las conexiones a bases de datos de .NET, y si utilizamos Jython, las de Java.

  5. #20
    Administrador y fundador. Avatar de 3dpoder
    Fecha de ingreso
    Apr 2002
    Mensajes
    15,459

    Post Documentación - Python para todos - por Raúl González Duque

    Documentación


    Docstrings

    En capítulos anteriores ya comentamos en varias ocasiones que todos los objetos cuentan con una variable especial __doc__ mediante la que indicar el propósito y uso del objeto. Estos son los llamados docstrings o cadenas de documentación.

    A estos atributos se les puede asociar el texto correspondiente explícitamente, asignandolo al literal cadena correspondiente, como con cualquier otra variable. Sin embargo, por conveniencia, Python ofrece un mecanismo mucho más sencillo y es que si el primer estamento de la definición del objeto es una cadena, esta se asocia a la variable __doc__ automáticamente.

    def haz_algo(arg):
    “””Este es el docstring de la funcion.”””
    print arg
    print haz_algo.__doc__
    haz_algo.__doc__ = “””Este es un nuevo docstring.”””
    print haz_algo.__doc__
    Como vemos lo interesante de estas cadenas es que, a diferencia de los comentarios normales de Python y de los comentarios de otros lenguajes, las cadenas de documentación no se eliminan del bytecode, por lo que se pueden consultar en tiempo de ejecución, usando, por ejemplo, la función help del lenguaje, o utilizando la sentencia print como en el ejemplo anterior.

    >>> help(haz_algo)
    Help on function haz_algo in module __main__:
    haz_algo(arg)
    Este es un nuevo docstring.

    Pydoc
    La función help, que comentamos brevemente con anterioridad, utiliza el módulo pydoc para generar la documentación de un objeto a partir de su docstring y los docstrings de sus miembros. Este módulo, incluido por defecto con Python desde la versión 2.1, se puede importar en nuestro código Python y utilizarse programaticamente, o bien se puede utilizar como una herramienta de línea de comandos que sería el equivalente a la aplicación Javadoc del mundo Java.

    pydoc puede mostrar la información como texto en la consola, tal como lo utiliza help, pero también puede generar archivos HTML como javadoc o facilitar la información a través de un pequeño servidor web incluido con el módulo.

    Pydoc es muy sencillo de utilizar. Con

    pydoc.py nombre1 [nombre2 ...]
    se muestra la documentación del tema, módulo, clase, paquete, función o palabra clave indicada de forma similar a la función help. Si el nombre es keywords, topics o modules se listaran las distintas palabras claves, temas y módulos respectivamente.

    Si se pasa el flag -w, el script guardará la documentación en uno o varios archivos html en lugar de mostrarla por pantalla.

    pydoc.py -w nombre1 [nombre2 ...]
    El flag -que sirve para buscar una determinada palabra en las sinopsis de todos los módulos disponibles. La sinopsis es la primera línea de la cadena de documentación.

    pydoc.py -que xml
    Con -p podemos iniciar el servidor HTTP en el puerto indicado.

    pydoc.py -p puerto
    Una vez hecho esto podemos acceder a la documentación de todos los módulos disponibles abriendo la página http://localhost:puerto en nuestro navegador.

    Por último, mediante el flag -g podemos lanzar una interfaz gráfica para buscar documentación que utiliza el servidor HTTP para mostrar los resultados.

    Epydoc y reStructuredText

    El problema de pydoc es que es muy simple, y no permite añadir semántica o modificar estilos de la documentación. No podemos, por ejemplo, indicar que en una línea en concreto de entre las líneas de documentación de la función describe un parámetro de la función o mostrar un cierto término en cursiva.

    Existen proyectos para generar documentación con funcionalidades más avanzadas como Docutils, Epydoc o Sphinx, aunque es necesario aprender sintaxis especiales.

    Docutils es un proyecto desarrollado por David Goodger que incluye
    distintas herramientas para generar documentación utilizando el formato reStructuredText, un formato de texto plano creado por el mismo autor, y que es el formato más utilizado en la comunidad Python. reStructuredText se utiliza, entre otros, para la creación de los PEPs (Python Enhancement Proposals).

    Sin embargo, actualmente Docutils es más indicado para generar documentos a partir de archivos de texto, y no a partir de docstrings extraídos de código fuente Python, ya que el parser encargado de este trabajo dista mucho de estar terminado.

    EpyDoc es una de las herramientas de generación de documentación para Python más utilizadas. Además de texto plano y de su propio formato, llamado epytext, soporta reStructuredText y sintaxis Javadoc, cosa que los programadores Java agradeceran.

    A lo largo del resto del capítulo utilizaremos reStructuredText como lenguaje de marcado y EpyDoc para generar los documentos finales.

    Epydoc se puede descargar desde su página web en forma de instalador exe para Windows, paquete RPM para Fedora o similares, o en archivos zip y tar.gz que incluyen scripts de instalación: http://epydoc.sourceforge.net/. También se encuentra en los repositorios de varias distribuciones Linux.

    Una vez hayamos instalado Epydoc siguiendo el método adecuado para nuestro sistema operativo tendremos acceso a su funcionalidad a través de dos interfaces de usuario distintas: el script epydoc, que consiste en una aplicación de línea de comandos, y el script epydocgui (epydoc.pyw en Windows), que ofrece una interfaz gráfica. Además también podemos acceder a la funcionalidad de epydoc programaticamente, como en el caso de pydoc.

    Vamos a crear un pequeño módulo con un par de clases para ver primero el resultado de utilizar epydoc con docstrings de texto plano, sin ningún tipo de marcado especial.

    “””Modulo para ejemplificar el uso de epydoc.”””
    class Persona:
    “””Mi clase de ejemplo.”””
    def __init__(self, nombre):
    “””Inicializador de la clase Persona.”””
    self.nombre = nombre
    self.mostrar_nombre()
    def mostrar_nombre(self):
    “””Imprime el nombre de la persona”””
    print “Esta es la persona %s” % self.nombre
    class Empleado(Persona):
    “””Subclase de Persona.”””
    pass
    if __name__ == “__main__”:
    raul = Persona(“Raul”)
    El formato de salida por defecto de epydoc es HTML. Por lo tanto para generar la documentación en forma de documentos HTML bastaría escribir algo así:

    epydoc ejemplo.py
    o bien

    epydoc --html ejemplo.py
    Para generar un archivo PDF, utilizando LaTeX, se utilizaría el flag --pdf:

    epydoc --pdf ejemplo.py
    Si LaTeX no esta instalado o epydoc no encuentra el ejecutable no será posible generar el PDF.

    También podemos indicar el nombre del proyecto y la URL mediante las opciones --name y --url:

    epydoc --name Ejemplo --url http://mundogeek.net ejemplo.py
    E incluso añadir diagramas mostrando la clase base y subclases (--graph classtree), las llamadas entre funciones y métodos (--graph callgraph), clases y subclases usando notación UML (--graph umlclasstree) o todos ellos (--graph all).

    epydoc --graph all ejemplo.py
    Para generar el grafo de llamadas, no obstante, es necesario generar un archivo con la información necesaria utilizando el módulo profile o el módulo hotshot e indicar el archivo resultante utilizando el flag --pstat:

    epydoc --graph all --pstat profile.out ejemplo.py
    Veamos ahora algunas funcionalidades básicas de marcado en reStructuredText.

    Para poner un texto en italica se rodea el texto con asteriscos:
    *italica* -> italica
    Para ponerlo en negrita, se utilizan dos asteriscos:

    **negrita** -> negrita
    Para mostrar el texto como monoespacio, por ejemplo para mostrar código inline, se utiliza “.

    “monoespacio” -> monoespacio
    Si necesitamos utilizar cualquiera de estos caracteres especiales, se pueden escapar utilizando la barra invertida.

    \* es un carácter especial -> * es un carácter especial
    Los títulos se crean añadiendo una línea de caracteres no alfanuméricos por debajo del texto, o por encima y por debajo del texto. Para crear un subtítulo basta utilizar una nueva combinación.
    Título
    ======


    Subtitulo
    —————————
    Título

    Subtitulo
    Para crear una lista no ordenada se empieza cada línea con el carácter ‘*’, ‘-’ o ‘+’:

    * Python
    * C
    * Java
    Python•
    C•
    Java•

    Para crear una lista numerada se empieza la línea con el número se guido de un punto, o bien con el símbolo ‘#’ para que se introduzca el número automáticamente.

    1. Python
    2. C
    3. Java

    1. Python
    2. C
    3. Java

    Para describir propiedades de los elementos que estamos documentando se utilizan los campos o fields. En reStructuredText los campos comienzan con ‘:’, le sigue el nombre del campo y opcionalmente sus argumentos, y se cierra de nuevo con ‘:’, para terminar con el cuerpo del campo.

    Estos son algunos de los campos que soporta Epydoc:




    Para que Epydoc sepa que utilizamos reStructuredText es necesario indicarlo mediante una variable __docformat__ en el código, o bien mediante la opción --docformat de línea de comandos. Las opciones posibles son epytext, plaintext, restructuredtext o javadoc.

    Veamos un ejemplo con campos:

    “””Modulo para ejemplificar el uso de *epydoc*.
    :author: Raul Gonzalez
    :versión: 0.1”””
    __docformat__ = “restructuredtext”
    class Persona:
    “””Modela una persona.”””
    def __init__(self, nombre, edad):
    “””Inicializador de la clase `Persona`.
    :param nombre: Nombre de la persona.
    :param edad: Edad de la persona”””
    self.nombre = nombre
    self.edad = edad
    self.mostrar_nombre()
    def mostrar_nombre(self):
    “””Imprime el nombre de la persona”””
    print “Esta es la persona %s” % self.nombre
    class Empleado(Persona):
    “””Subclase de `Persona` correspondiente a las personas
    que trabajan para la organizacion.
    :todo: Escribir implementacion.”””
    pass
    if __name__ == “__main__”:
    juan = Persona(“Juan”, 26)
    reStructuredText también soporta un segundo tipo de campos en el que el cuerpo del campo es una lista. De esta forma podemos, por ejemplo, describir todos los parámetros de una función o método con un solo campo :Parameters:, en lugar de con un campo :param: para cada parámetro.

    class Persona:
    “””Modela una persona.”””
    def __init__(self, nombre, edad):
    “””Inicializador de la clase `Persona`.
    :Parameters:
    - `nombre`: Nombre de la persona.
    - `edad`: Edad de la persona.
    “””
    self.nombre = nombre
    self.edad = edad
    self.mostrar_nombre()
    Otros campos que admiten listas son :Exceptions: para indicar varias excepciones (:except, :Variables: para comentar varias variables (:var o :Ivariables: para comentar varias instancias (:ivar.




    Miniaturas adjuntadas Miniaturas adjuntadas Python para todos - por Raul Gonzalez Duque-variables.jpg   Python para todos - por Raul Gonzalez Duque-variables2.jpg  

  6. #21
    Administrador y fundador. Avatar de 3dpoder
    Fecha de ingreso
    Apr 2002
    Mensajes
    15,459

    Post Pruebas - Python para todos - por Raúl González Duque

    Pruebas


    Para asegurar en la medida de lo posible el correcto funcionamiento y la calidad del software se suelen utilizar distintos tipos de pruebas, como pueden ser las pruebas unitarias, las pruebas de integración, o las pruebas de regresión.

    A lo largo de este capítulo nos centraremos en las pruebas unitarias, mediante las que se comprueba el correcto funcionamiento de las unidades lógicas en las que se divide el programa, sin tener en cuenta la interrelación con otras unidades.

    La solución más extendida para las pruebas unitarias en el mundo Python es unittest, a menudo combinado con doctest para pruebas más sencillas. Ambos módulos están incluidos en la librería estándar de Python.

    Doctest
    Como es de suponer por el nombre del módulo, doctest permite combinar las pruebas con la documentación. Esta idea de utilizar las pruebas unitarias para probar el código y también a modo de documentación permite realizar pruebas de forma muy sencilla, propicia el que las pruebas se mantengan actualizadas, y sirve a modo de ejemplo de uso del código y como ayuda para entender su propósito.

    Cuando doctest encuentra una línea en la documentación que comienza con ‘>>>’ se asume que lo que le sigue es código Python a ejecutar, y que la respuesta esperada se encuentra en la línea o líneas siguientes, sin >>>. El texto de la prueba termina cuando se encuentra una línea en blanco, o cuando se llega al final de la cadena de documentación.

    Tomemos como ejemplo la siguiente función, que devuelve una lista con los cuadrados de todos los números que componen la lista pasada como parámetro:

    def cuadrados(lista):
    “””Calcula el cuadrado de los números de una lista”””
    return [n ** 2 for n in lista]
    Podríamos crear una prueba como la siguiente, en la que comprobamos que el resultado al pasar la lista [0, 1, 2, 3] es el que esperabamos:

    def cuadrados(lista):
    “””Calcula el cuadrado de los números de una lista
    >>> l = [0, 1, 2, 3]
    >>> cuadrados(l)
    [0, 1, 4, 9]
    “””
    return [n ** 2 for n in lista]
    Lo que hacemos en este ejemplo es indicar a doctest que cree un lista l con valor [0, 1, 2, 3], que llame a continuación a la función cuadrados con l como argumento, y que compruebe que el resultado devuelto sea igual a [0, 1, 4, 9].

    Para ejecutar las pruebas se utiliza la función testmod del módulo, a la que se le puede pasar opcionalmente el nombre de un módulo a evaluar (parámetro name). En el caso de que no se indique ningún argumento, como en este caso, se evalúa el módulo actual:

    def cuadrados(lista):
    “””Calcula el cuadrado de los números de una lista
    >>> l = [0, 1, 2, 3]
    >>> cuadrados(l)
    [0, 1, 4, 9]
    “””
    return [n ** 2 for n in lista]
    def _test():
    import doctest
    doctest.testmod()
    if __name__ == “__main__”:
    _test()
    En el caso de que el código no pase alguna de las pruebas que hemos definido, doctest mostrara el resultado obtenido y el resultado esperado. En caso contrario, si todo es correcto, no se mostrara ningún mensaje, a menos que añadamos la opción -v al llamar al script o el parámetro verbose=True a la función tesmod, en cuyo caso se mostraran todas las pruebas ejecutadas, independientemente de si se ejecutaron con éxito.

    Este sería el aspecto de la salida de doctest utilizando el parámetro -v:

    Trying:
    l = [0, 1, 2, 3]
    Expecting nothing
    ok
    Trying:
    cuadrados(l)
    Expecting:
    [0, 1, 4, 9]
    ok
    2 items had no tests:
    __main__
    __main__._test
    1 items passed all tests:
    2 tests in __main__.cuadrados
    2 tests in 3 items.
    2 passed and 0 failed.
    Test passed.
    Ahora vamos a introducir un error en el código de la función para ver el aspecto de un mensaje de error de doctest. Supongamos, por ejemplo, que hubieramos escrito un operador de multiplicación (‘*’) en lugar de uno de exponenciación (‘**’):

    def cuadrados(lista):
    “””Calcula el cuadrado de los números de una lista
    >>> l = [0, 1, 2, 3]
    >>> cuadrados(l)
    [0, 1, 4, 9]
    “””
    return [n * 2 for n in lista]
    def _test():
    import doctest
    doctest.testmod()
    if __name__ == “__main__”:
    _test()
    Obtendríamos algo parecido a esto:

    ************************************************** *******
    File “ejemplo.py”, line 5, in __main__.cuadrados
    Failed example:
    cuadrados(l)
    Expected:
    [0, 1, 4, 9]
    Got:
    [0, 2, 4, 6]
    ************************************************** *******
    1 items had failures:
    1 of 2 in __main__.cuadrados
    ***Test Failed*** 1 failures.
    Como vemos, el mensaje nos indica que ha fallado la prueba de la línea 5, al llamar a cuadrados(l), cuyo resultado debería ser [0, 1, 4, 9], y sin embargo obtuvimos [0, 2, 4, 6].

    Veamos por último cómo utilizar sentencias anidadas para hacer cosas un poco más complicadas con doctest. En el ejemplo siguiente nuestra función calcula el cuadrado de un único número pasado como parámetro, y diseñamos una prueba que compruebe que el resultado es el adecuado para varias llamadas con distintos valores. Las sentencias anidadas comienzan con “...” en lugar de “>>>”:

    def cuadrado(num):
    “””Calcula el cuadrado de un número.
    >>> l = [0, 1, 2, 3]
    >>> for n in l:
    ... cuadrado(n)
    [0, 1, 4, 9]
    “””
    return num ** 2
    def _test():
    import doctest
    doctest.testmod()
    if __name__ == “__main__”:
    _test()
    unittest / PyUnit
    unittest, también llamado PyUnit, forma parte de una familia de herramientas conocida colectivamente como xUnit, un conjunto de frameworks basados en el software SUnit para Smalltalk, creado por Kent Beck, uno de los padres de la eXtreme Programming. Otros ejemplos de herramientas que forman parte de esta familia son JUnit para Java, creada por el propio Kent Bek junto a Erich Gamma, o NUnit, para .NET.

    El uso de unittest es muy sencillo. Para cada grupo de pruebas tenemos que crear una clase que herede de unittest.TestCase, y añadir una serie de métodos que comiencen con test, que serán cada una de las pruebas que queremos ejecutar dentro de esa batería de pruebas.

    Para ejecutar las pruebas, basta llamar a la función main() del módulo, con lo que se ejecutaran todos los métodos cuyo nombre comience con test, en orden alfanumérico. Al ejecutar cada una de las pruebas el resultado puede ser:

    OK : La prueba ha pasado con éxito.

    FAIL : La prueba no ha pasado con éxito. Se lanza una excepción AssertionError para indicarlo.

    ERROR : Al ejecutar la prueba se lanzó una excepción distinta de AssertionError.

    En el siguiente ejemplo, dado que el método que modela nuestra prueba no lanza ninguna excepción, la prueba pasaría con éxito.

    import unittest
    class EjemploPruebas(unittest.TestCase):
    def test(self):
    pass
    if __name__ == “__main__”:
    unittest.main()
    En este otro, sin embargo, fallaría:

    import unittest
    class EjemploPruebas(unittest.TestCase):
    def test(self):
    raise AssertionError()
    if __name__ == “__main__”:
    unittest.main()
    Nada nos impide utilizar clausulas if para evaluar las condiciones que nos interesen y lanzar una excepción de tipo AssertionError cuando no sea así, pero la clase TestCase cuenta con varios métodos que nos pueden facilitar la tarea de realizar comprobaciones sencillas. Son los siguientes:

    assertAlmostEqual(first, second, places=7, msg=None)• : Comprueba que los objetos pasados como parámetros sean iguales hasta el séptimo decimal (o el número de decimales indicado por places).

    assertEqual(first, second, msg=None)• : Comprueba que los objetos pasados como parámetros sean iguales.
    assertFalse(expr, msg=None)• : Comprueba que la expresión sea falsa.

    assertNotAlmostEqual(first, second, places=7, msg=None)• : Comprueba que los objetos pasados como parámetros no sean iguales hasta el séptimo decimal (o hasta el número de decimales indicado por places).

    assertNotEqual(first, second, msg=None)• : Comprueba que los objetos pasados como parámetros no sean iguales.

    assertRaises(excClass, callableObj, *args, **kwargs)• : Comprueba que al llamar al objeto callableObj con los parámetros
    definidos por *args y **kwargs se lanza una excepción de tipo excClass.
    assertTrue(expr, msg=None)• : Comprueba que la expresión sea cierta.

    assert_(expr, msg=None)• : Comprueba que la expresión sea cierta.
    fail(msg=None)• : Falla inmediatamente.

    failIf(expr, msg=None)• : Falla si la expresión es cierta.
    failIfAlmostEqual(first, second, places=7, msg=None)• : Falla si los objetos pasados como parámetros son iguales hasta el séptimo
    decimal (o hasta el número de decimales indicado por places).

    failIfEqual(first, second, msg=None)• : Falla si los objetos pasados
    como parámetros son iguales.

    failUnless(expr, msg=None)• : Falla a menos que la expresión sea cierta.

    failUnlessAlmostEqual(first, second, places=7, msg=None)• : Falla a menos que los objetos pasados como parámetros sean iguales hasta el séptimo decimal (o hasta el número de decimales indicado por places).

    failUnlessEqual(first, second, msg=None)• : Falla a menos que los objetos pasados como parámetros sean iguales.

    failUnlessRaises(excClass, callableObj, *args, **kwargs)• : Falla a menos que al llamar al objeto callableObj con los parámetros
    definidos por *args y **kwargs se lance una excepción de tipo excClass.

    Como vemos todos los métodos cuentan con un parámetro opcional msg con un mensaje a mostrar cuando dicha comprobación falle.

    Retomemos nuestra pequeña función para calcular el cuadrado de un número. Para probar el funcionamiento de la función podríamos hacer, por ejemplo, algo así:

    import unittest
    def cuadrado(num):
    “””Calcula el cuadrado de un número.”””
    return num ** 2
    class EjemploPruebas(unittest.TestCase):
    def test(self):
    l = [0, 1, 2, 3]
    r = [cuadrado(n) for n in l]
    self.assertEqual(r, [0, 1, 4, 9])
    if __name__ == “__main__”:
    unittest.main()
    Preparación del contexto
    En ocasiones es necesario preparar el entorno en el que queremos que se ejecuten las pruebas. Por ejemplo, puede ser necesario introducir unos valores por defecto en una base de datos, crear una conexión con una máquina, crear algún archivo, etc. Esto es lo que se conoce en el mundo de xUnit como test fixture.

    La clase TestCase proporciona un par de métodos que podemos sobreescribir para construir y desconstruir el entorno y que se ejecutan antes y después de las pruebas definidas en esa clase. Estos métodos son setUp() y tearDown().

    class EjemploFixture(unittest.TestCase):
    def setUp(self):
    print “Preparando contexto”
    self.lista = [0, 1, 2, 3]
    def test(self):
    print “Ejecutando prueba”
    r = [cuadrado(n) for n in self.lista]
    self.assertEqual(r, [0, 1, 4, 9])
    def tearDown(self):
    print “Desconstruyendo contexto”
    del self.lista

  7. #22
    Administrador y fundador. Avatar de 3dpoder
    Fecha de ingreso
    Apr 2002
    Mensajes
    15,459

    Post Distribuir Aplicaciones Python - Python para todos - por Raúl González Duque

    Distribuir aplicaciones Python


    Una vez terminemos con el desarrollo de nuestra nueva aplicación es conveniente empaquetarla de forma que sea sencillo para los usuarios instalarla, y para nosotros distribuirla.

    En Python existen dos módulos principales para este cometido: distutils,
    que es parte de la librería estándar y era el método más utilizado hasta hace poco, y setuptools, que extiende la funcionalidad de distutils
    y es cada vez más popular.

    En este capítulo veremos el funcionamiento de ambas herramientas, y terminaremos explicando cómo crear ejecutables .exe para Windows a partir de nuestro programa en Python.

    distutils
    Todo programa distribuido con distutils contiene un script llamado
    por convención setup.py, que se encarga de instalar la aplicación llamando a la función setup de distutils.core. Esta función tiene montónes de argumentos, que controlan, entre otras cosas, cómo instalar la aplicación.

    Destinados a describir la aplicación tenemos los siguientes argumentos:


    • name : El nombre del paquete.
    • versión : El número de versión.
    • description : Una línea describiendo el paquete.
    • long_description : Descripción completa del paquete.
    • author : Nombre del autor de la aplicación.
    • author_email : Correo electrónico del autor.
    • maintainer : Nombre de la persona encargada de mantener el paquete, si difiere del autor.
    • maintainer_email : Correo de la persona encargada de mantener el paquete, si difiere del autor.
    • url : Web de la aplicación.
    • download_url : Url de la que descargar la aplicación.
    • license : Licencia de la aplicación

    También tenemos argumentos que controlan los archivos y directorios que deben instalarse, como son packages, py_modules, scripts y ext_modules.

    El parámetro scripts, que es una lista de cadenas, indica el nombre del módulo o módulos principales, es decir, los que ejecuta el usuario. Si nuestra aplicación consistiera, por ejemplo, en un solo script ejemplo.
    py, el código de setup.py podría tener un aspecto similar al siguiente:

    from distutils.core import setup
    setup(name=”aplicación de ejemplo”,
    versión=”0.1”,
    description=”Ejemplo del funcionamiento de distutils”,
    author=”Raul Gonzalez”,
    author_email=”zootropo en gmail”,
    url=”http://mundogeek.net/tutorial-python/”,
    license=”GPL”,
    scripts=[“ejemplo.py”]
    )
    Si hemos escrito otros módulos para ser utilizados por el script principal, estos se indican mediante el parámetro py_modules. Por ejemplo, supongamos que la aplicación consiste en un script principal ejemplo.py, y un módulo de apoyo apoyo.py:

    from distutils.core import setup
    setup(name=”aplicación de ejemplo”,
    versión=”0.1”,
    description=”Ejemplo del funcionamiento de distutils”,
    author=”Raul Gonzalez”,
    author_email=”zootropo en gmail”,
    url=”http://mundogeek.net/tutorial-python/”,
    license=”GPL”,
    scripts=[“ejemplo.py”],
    py_modules=[“apoyo”]
    )
    Para instalar paquetes Python (directorios que contienen varios módulos y un archivo __init__.py) usaríamos el parámetro packages. Si además del módulo ejemplo.py quisiéramos instalar los paquetes gui y bbdd, por ejemplo, haríamos algo así:

    from distutils.core import setup
    setup(name=”aplicación de ejemplo”,
    versión=”0.1”,
    description=”Ejemplo del funcionamiento de distutils”,
    author=”Raul Gonzalez”,
    author_email=”zootropo en gmail”,
    url=”http://mundogeek.net/tutorial-python/”,
    license=”GPL”,
    scripts=[“ejemplo.py”],
    packages=[“gui”, “bbdd”]
    )
    ext_modules, por último, sirve para incluir extensiones que utilice el programa, en C, C++, Fortran, …

    Veamos ahora cómo se utilizaría el archivo setup.py una vez creado.

    Al ejecutar el comando

    python setup.py install
    los módulos y paquetes especificados por py_modules y packages se instalan en el directorio Lib de Python. Los programas indicados en scripts, se copian al directorio Scripts de Python.

    Una vez hemos comprobado que la aplicación se instala correctamente, procedemos a crear archivos mediante los que distribuir la aplicación a los usuarios. Para crear archivos con el código fuente se utiliza la opción sdist de setup.py, que crea por defecto un archivo tar.gz en Unix y un zip en Windows.
    python setup.py sdist
    Sin embargo se puede utilizar --formats para especificar el formato o formatos que queramos generar



    Para crear un archivo tar.bz2, un tar.gz y un zip, por ejemplo, se utilizaría la siguiente orden:

    python setup.py sdist --formats=bztar,gztar,zip
    Para generar un archivo de distribución binaria, se usa la opción bdist:

    python setup.py bdist
    Los formatos que soporta bdist son los siguientes:



    Para crear un archivo rpm y un instalador de Windows, por ejemplo, escribiríamos:

    python setup.py bdist --formats=wininst,rpm
    También es posible crear otros tipos de archivos de distribución utilizando scripts que extienden distutils, como es el caso de los paquetes deb mediante el script stdeb (http://stdeb.python-hosting.com/)

    setuptools
    setuptools extediende distutils añadiendo una serie de funcionalidades muy interesantes: introduce un nuevo formato de archivo para distribución de aplicaciones Python llamado egg, se encarga de buscar todos los paquetes que deben instalarse y añadir las posibles dependencias, permite instalar paquetes de PyPI con un solo comando, etc.

    Además, como setuptools se basa en distutils, un script de instalación básico utilizando setuptools es prácticamente igual a su equivalente con distutils. Tan sólo cambiaría la sentencia de importación.

    from setuptools import setup
    setup(name=”aplicación de ejemplo”,
    versión=”0.1”,
    description=”Ejemplo del funcionamiento de distutils”,
    author=”Raul Gonzalez”,
    author_email=”zootropo en gmail”,
    url=”http://mundogeek.net/tutorial-python/”,
    license=”GPL”,
    scripts=[“ejemplo.py”],
    )
    El único inconveniente que podríamos encontrar al uso de setuptools es que no esta incluido por defecto en Python 2.5, aunque es probable que esto cambie en próximas versiones debido a su gran uso. Pero los desarrolladores de setuptools han pensado en todo, e incluso esto no debería suponer ningún problema, ya que con un mínimo esfuerzo por nuestra parte podemos hacer que setuptools se descargue e instale automáticamente en la máquina del usuario si este no se encuentra ya en el sistema. Basta distribuir con nuestro paquete un pequeño módulo extra ez_setup.py que viene incluido por defecto con setuptools (http://peak.telecommunity.com/dist/ez_setup.py) y llamar a la función use_setuptools del módulo al inicio de setup.py:

    from ez_setup import use_setuptools
    use_setuptools()
    from setuptools import setup
    setup(name=”aplicación de ejemplo”,
    versión=”0.1”,
    description=”Ejemplo del funcionamiento de distutils”,
    author=”Raul Gonzalez”,
    author_email=”zootropo en gmail”,
    url=”http://mundogeek.net/tutorial-python/”,
    license=”GPL”,
    scripts=[“ejemplo.py”],
    )
    Veamos ahora con más detenimiento algunos de los cambios y novedades que introduce setuptools.

    Integración con PyPI
    Al estilo de CPAN en Perl setuptools permite instalar de forma fácil y sencilla los paquetes pertenecientes a PyPI, el Índice de Paquetes Python (http://pypi.python.org/pypi), así como subir nuestros propios paquetes.

    PyPI cuenta en el momento de escribir estas líneas con 4782 paquetes, por lo que poder instalar los paquetes de este repositorio con un simple comando supone una ayuda muy a tener en cuenta.

    Instalar un paquete de PyPI es tan sencillo como pasar al comando easy_install el nombre del paquete a instalar

    easy_install docutils
    Searching for docutils
    Reading http://pypi.python.org/simple/docutils/
    Reading http://docutils.sourceforge.net/
    Best match: docutils 0.5
    Downloading http://prdownloads.sourceforge.net/d...ar.gz?download
    Processing docutils-0.5.tar.gz
    Running docutils-0.5/setup.py -que bdist_egg --dist-dir /tmp/easy_install-wUAyUZ/docutils-0.5/egg-dist-tmp-kWkkkv
    “optparse” module already present; ignoring extras/optparse.py.
    “textwrap” module already present; ignoring extras/textwrap.py.
    zip_safe flag not set; analyzing archive contents…
    docutils.writers.newlatex2e.__init__: module references __file__
    docutils.writers.pep_html.__init__: module references __file__
    docutils.writers.html4css1.__init__: module references __file__
    docutils.writers.s5_html.__init__: module references __file__
    docutils.parsers.rst.directives.misc: module references __file__
    Adding docutils 0.5 to easy-install.pth file
    Installing rst2pseudoxml.py script to /usr/bin
    Installing rst2html.py script to /usr/bin
    Installing rst2latex.py script to /usr/bin
    Installing rst2s5.py script to /usr/bin
    Installing rst2newlatex.py script to /usr/bin
    Installing rstpep2html.py script to /usr/bin
    Installing rst2xml.py script to /usr/bin
    Installed /usr/lib/python2.5/site-packages/docutils-0.5-py2.5.egg
    Processing dependencies for docutils
    Finished processing dependencies for docutils

    Poder subir nuestros paquetes a PyPI requiere de un proceso un poco más laborioso. Primero registramos los detalles de nuestra aplicación en PyPI mediante la opción register del script setup.py, el cual nos preguntara por nuestro nombre de usuario, contraseña y correo electrónico si no tenemos cuenta en PyPI, o nombre de usuario y contraseña si nos registramos anteriormente:

    python setup.py register
    running register
    running egg_info
    creating Aplicacion_de_ejemplo.egg-info
    writing Aplicacion_de_ejemplo.egg-info/PKG-INFO
    writing top-level names to Aplicacion_de_ejemplo.egg-info/top_level.txt
    writing dependency_links to Aplicacion_de_ejemplo.egg-info/dependency_links.txt
    writing manifest file ‘Aplicacion_de_ejemplo.egg-info/SOURCES.
    txt’
    reading manifest file ‘Aplicacion_de_ejemplo.egg-info/SOURCES.
    txt’
    writing manifest file ‘Aplicacion_de_ejemplo.egg-info/SOURCES.
    txt’
    We need to know who you are, so please choose either:
    1. use your existing login,
    2. register as a new user,
    3. have the server generate a new password for you (and email it to you), or

    4. quit
    Your selection [default 1]: 1
    Username: zootropo
    Password:
    Server response (200): OK
    I can store your PyPI login so future submissions will be faster.
    (the login will be stored in /home/zootropo/.pypirc)
    Save your login (y/N)?y

    Para crear y subir una distribución con el código fuente de nuestra aplicación se utiliza la opción sdist upload:

    python setup.py sdist upload
    También podríamos crear y subir un egg (un formato de archivo para distribuir aplicaciones Python que veremos en la próxima sección) utilizando la opción bdist_egg upload:

    python setup.py bdist_egg upload
    O combinar los tres pasos en un solo comando:

    python setup.py register sdist bdist_egg upload
    Una vez subido el paquete cualquier persona podría instalarlo en su sistema utilizando easy_install, de la misma forma que cualquier otro paquete de PyPI:

    easy_install mi-paquete
    Eggs
    Los eggs (huevo en inglés) son archivos de extensión .egg mediante los que distribuir aplicaciones en Python. Serían algo así como el equivalente a los archivos .jar del mundo Java. Son multiplataforma, permiten manejar dependencias, y permiten instalar distintas versiones del mismo paquete.

    La forma más sencilla de instalar aplicaciones distribuidas como archivos egg es mediante el comando easy_install, el cual comentamos brevemente en el punto anterior al hablar sobre su uso para instalar paquetes de PyPI. Para instalar un archivo egg no tenemos más que pasarle el nombre del archivo al comando easy_install:

    easy_install mi-aplicacion.egg
    o bien podemos pasarle la URL de la que descargar el egg:

    Para construir nuestros propios eggs podemos utilizar el comando bdist_egg de setup.py, de forma similar a la manera en que construíamos paquetes RPM o instaladores para Windows con distutils:

    python setup.py bdist_egg
    Otros cambios destacables
    Uno de los cambios más interesantes es la incorporación de un nuevo
    argumento para la función setup llamado install_requires, que consiste en una cadena o lista de cadenas que indica los paquetes de los que depende la aplicación. Si nuestra aplicación necesitara tener instalado el paquete apoyo para poder ejecutarse, por ejemplo, escribiríamos lo siguiente:

    install_requires = [“apoyo”]
    Y de esta forma, easy_install se encargaría de buscar e instalar el paquete si fuera necesario, bien en PyPI, o en cualquier otro repositorio indicado por el parámetro dependency_links.

    Además podemos especificar que se necesita una versión concreta del paquete requerido, que sea mayor o menor que una cierta versión, o que no se trate de una versión determinada utilizando operadores relacionales (==, !=, <, <=, >, >=):

    install_requires = [“apoyo >= 1.0 < 2.0”]
    También existen argumentos similares para declarar paquetes que deben instalarse para poder ejecutar el script de instalación (setup_requires), para poder ejecutar las posibles pruebas incluídas con el paquete (tests_require) y para conseguir funcionalidades adicionales (extras_require, que consiste en este caso en un diccionario).

    setuptools incluye también atajos útiles, como la función find_packages() que nos evita tener que listar todos y cada uno de los paquetes que utiliza nuestro script en el parámetro packages, como era el caso de distutils:

    from ez_setup import use_setuptools
    use_setuptools()
    from setuptools import setup, find_packages
    setup(name=”aplicación de ejemplo”,
    versión=”0.1”,
    description=”Ejemplo del funcionamiento de distutils”,
    author=”Raul Gonzalez”,
    author_email=”zootropo en gmail”,
    url=”http://mundogeek.net/tutorial-python/”,
    license=”GPL”,
    scripts=[“ejemplo.py”],
    packages = find_packages()
    )
    Crear ejecutables .exe
    Tanto en Mac OS como en la mayor parte de las distribuciones Linux el intérprete de Python esta instalado por defecto, por lo que los usuarios de estos sistemas no tienen mayor complicación a la hora de instalar y ejecutar aplicaciones escritas en Python.

    En el caso de Windows, esto no es así, por lo que sería interesante que los usuarios de este sistema operativo no tuvieran que instalar el intérprete de Python. También sería interesante que nuestro programa consistiera en un archivo .exe en lugar de uno o varios archivos .py, para simplificar las cosas.

    Todo esto lo podemos lograr gracias a py2exe, una extensión para distutils que, como su nombre indica, permite crear ejecutables para Windows a partir de código Python, y que permite ejecutar estas aplicaciones sin necesidad de tener instalado el intérprete de Python en el sistema.

    Py2exe funciona examinando nuestro código fuente en busca de los módulos y paquetes que utilizamos, compilandolos y construyendo un nuevo archivo que incluye estos archivos y un pequeño intérprete de Python integrado.

    Para probar el funcionamiento de py2exe creemos un pequeño programa ejemplo.py

    print “Soy un .exe”
    y el archivo setup.py correspondiente. Los cambios que tenemos que realizar a setup.py son sencillos: importar py2exe, y utilizar los argumentos console y Windows para indicar el nombre del script o scripts que queramos convertir en ejecutables de consola o ejecutables de interfaz gráfica, respectivamente.

    from distutils.core import setup
    import py2exe
    setup(name=”aplicación de ejemplo”,
    versión=”0.1”,
    description=”Ejemplo del funcionamiento de distutils”,
    author=”Raul Gonzalez”,
    author_email=”zootropo en gmail”,
    url=”http://mundogeek.net/tutorial-python/”,
    license=”GPL”,
    scripts=[“ejemplo.py”],
    console=[“ejemplo.py”]
    )
    Para crear el ejecutable, utilizamos una nueva opción de línea de comandos para setup.py disponible tras importar el módulo y llamada, cómo no, py2exe:

    python setup.py py2exe
    Con esto py2exe generara un directorio build, con las librerías compiladas, y un directorio dist, con los archivos que conforman nuestra aplicación.

    Entre los archivos que podemos encontrar en dist tendremos uno o varios ejecutables con el mismo nombre que los scripts indicados en console y Windows, un archivo python*.dll, que es el intérprete de Python, y un archivo library.zip, que contiene varios archivos pyc que son los módulos que utiliza la aplicación compilados.

    Si queremos reducir el número de archivos a distribuir, podemos utilizar la opción --bundle de py2exe para añadir a library.zip las dll y los pyd (--bundle 2) o las dll, los pyd y el intérprete (--bundle 1).

    python setup.py py2exe --bundle 1
    o bien podemos añadir un nuevo argumento options a la función setup que indique el valor a utilizar (opción bundle_files), de forma que no tengamos que añadir el flag --bundle cada vez que usemos el comando py2exe:

    from distutils.core import setup
    import py2exe
    setup(name=”aplicación de ejemplo”,
    versión=”0.1”,
    description=”Ejemplo del funcionamiento de distutils”,
    author=”Raul Gonzalez”,
    author_email=”zootropo en gmail”,
    url=”http://mundogeek.net/tutorial-python/”,
    license=”GPL”,
    scripts=[“ejemplo.py”],
    console=[“ejemplo.py”],
    options={“py2exe”: {“bundle_files”: 1}}
    )
    Por último podemos incluso prescindir de library.zip e incrustarlo en el ejecutable utilizando el argumento zipfile=None

    from distutils.core import setup
    import py2exe
    setup(name=”aplicación de ejemplo”,
    versión=”0.1”,
    description=”Ejemplo del funcionamiento de distutils”,
    author=”Raul Gonzalez”,
    author_email=”zootropo en gmail”,
    url=”http://mundogeek.net/tutorial-python/”,
    license=”GPL”,
    scripts=[“ejemplo.py”],
    console=[“ejemplo.py”],
    options={“py2exe”: {“bundle_files”: 1}},
    zipfile=None
    )
    Fin de este estupendo manual, como dije al principio del primer mensaje, tenga en cuenta que este manual esta disponible en formato pdf, adjunto al primer mensaje, donde podrá leer con más comodidad e incluso imprimirlo, te recomiendo pruebes la descarga directa del mismo, desde la página del autor: http://mundogeek.net/tutorial-python/ en caso de funcionar este enlace, puedes hacer la descarga directa desde el primer mensaje.

    3DPoder.
    Miniaturas adjuntadas Miniaturas adjuntadas Python para todos - por Raul Gonzalez Duque-aplicaciones2.jpg  
    Imágenes adjuntadas Imágenes adjuntadas Python para todos - por Raul Gonzalez Duque-aplicaciones.jpg 
    Última edición por 3dpoder; 12-01-2009 a las 03:24

Página 2 de 2 PrimerPrimer 12

Temas similares

  1. Ofertas de Trabajo Jose Miguel Duque busca diseñador 2D
    Por Arag0n en el foro Colaboraciones e Iniciativas
    Respuestas: 0
    Último mensaje: 12-04-2012, 16:23
  2. Nueva Web - Raul Gonzalez Macias - 3D - Psykulture
    Por Un Anácrono en el foro Demandas de Empleo / Curriculum
    Respuestas: 0
    Último mensaje: 24-07-2010, 15:44
  3. version python para blender 2 45
    Por gigo en el foro Programación y Scripts
    Respuestas: 2
    Último mensaje: 22-01-2008, 21:58
  4. Duque Malicias
    Por aker en el foro Trabajos Finalizados
    Respuestas: 6
    Último mensaje: 04-11-2005, 00:07
  5. Callejon del futuro - autor del Render: DuQue
    Por 3dpoder en el foro Primer concurso de 3DPoder: Finalizado
    Respuestas: 8
    Último mensaje: 24-10-2003, 15:19

Actualmente estos son sus permisos de publicación en el foro.

  • -No puedes crear nuevos temas al no estar registrado o no haber iniciado sesión en el foro.
  • -No puedes responder temas al no estar registrado o no haber iniciado sesión en el foro.
  • -No puedes subir archivos adjuntos al no estar registrado o no haber iniciado sesión en el foro.
  • -No puedes editar tus mensajes al no estar registrado o no haber iniciado sesión en el foro.
  •