27 ene. 2016

Docker en Docker (DinD)

















Docker en Docker es simplemente correr un Docker dentro de un contenedor Docker.

El sitio de  DinD lo pueden visitar en el siguiente enlace, la imagen docker oficial de DinD la pueden ver en el siguiente enlace.


Lo primero que hay que hacer es correr docker con unas opciones:

docker run --rm --privileged -t -i -e LOG=file jpetazzo/dind

El modo privileged  permite negociar algunas características del sistema de archivo.

Luego de iniciar el contenedor se ejecuta  otro contenedor esta vez de busyox que devuelve "Hello new World!".

docker run busybox echo "Hello New World!"

A continuación se muestra una figura del resultado de la ejecución.

26 ene. 2016

Ejecutando microsservicios con docker usando docker-compose

Continuando con los artículos de docker, en este caso se hablará de iniciar dos contenedores donde uno le da servicio al otro, un contenedor tendrá el servicio de redis y el otro será una aplicación web sencilla hecha en flask.

Para poder lograr el manejo de varios contenedores existe docker-compose, para el caso de este artículo el enlace sobre docker-compose en flask lo pueden revisar en el siguiente enlace.

Los artículos anteriores que tratan el tema de docker son:

  1. Instalar Docker en Debian Jessie
  2. Uso de Docker en Debian Jessie (parte 1)
  3. Uso de Docker en Debian Jessie (parte 2)
  4. Crear una imagen Docker con un archivo Dockerfile
  5. Iniciando Django usando Docker
  6. Iniciando Gitlab por medio de Docker


Primero se crea el directorio de trabajo.
mkdir flask
cd flask 

Se crea el archivo app.py con el siguiente contenido:

#Se importa flask y redis
from flask import Flask
from redis import Redis

#Se crea la instancia de flask 
app = Flask(__name__)
#Se crea la instancia de redis
redis = Redis(host="redis",port=6379)


#Se crea la función hello definiendo la ruta del decorador.
@app.route("/")
def hello():
    #Se incrementa el valor de la variable hits de redis
    redis.incr('hits')
    #Se retorna un mensaje con el valor actual de la variable en redis
    return "Hello is me %s...\n" %redis.get('hits')


if __name__ == "__main__":
    #Se ejecuta la aplicación en modo debug
    app.run(host="0.0.0.0",debug=True)



Se crea el archivo requerimientos.txt, donde se define que se requiere instalar flask y redis:
flask
redis

Se crea el archivo Dockerfile con el siguiente contenido:
FROM python:2.7
WORKDIR /code
ADD requerimientos.txt /code/
RUN pip install -r requerimientos.txt
ADD . /code
CMD python app.py


Se construye la imagen web:
docker build -t web .


Se define los servicios por medio del archivo docker-compose.yml que contiene lo siguiente:

web:
  build: .
  ports:
   - "5000:5000"
  volumes:
   - .:/code
  links:
   - redis
redis:
  image: redis


Descripción del archivo docker-compose.yml:
  1. web: Define la imagen llamada web para ser construída, maneja los puertos 5000 local y externo será 5000 también, además se define el directorio volume llamado code, al final para web se define links que hace llamado a la imagen redis.
  2. redis: Se define la imagen redis.
Construir y correr la aplicación con compose:

Desde el directorio donde se encuentran los archivos ejecutar :
docker-compose up


En la siguiente figura se muestra que está corriendo la aplicación:



Al abrir el navegador en en localhost:5000 se tiene lo que muestra la siguiente figura:


Al ejecutar docker ps se tiene los contenedores que se están ejecutando:


También se tiene para docker-compose el comando ps y se obtiene lo siguiente:

El resultado es simplificado con respecto a docker ps.

Para detener los procesos o la ejecución del servicio se usa el comando stop de docker-compose:

docker-componse stop


Con docker compose se puede ejecutar multiples imagenes y usarlas en una aplicación principal, esto logra el concepto de microservicios.

20 ene. 2016

Instalar Gitlab por medio de Docker

Lo artículos anteriores de docker son los siguientes:

  1. Instalar Docker en Debian Jessie.
  2. Uso de Docker en Debian Jessie (parte 1).
  3. Uso de Docker en Debian Jessie (parte 2).
  4. Crear una imagen Docker con un archivo Dockerfile.
  5. Iniciando Django usando Docker.
En el primer artículo de uso de Docker en Debian Jessie se muestra al final como usar SonarQube por medio de Docker con dos instancias de la misma imagen.


En esté artículo se bajará la imagen Docker de Gitlab edición comunitaria. El sitio donde se aloja está en el siguiente enlace y la documentación  en inglés a la cual se basa esté artículo está en el siguiente enlace.


Se ejecutará el gitlab-ce que se bajará del sitio de dockerhub. El comando es el siguiente:
docker run --detach  --hostname pruebasgitlab --publish 443:443 --publish 80:80 --publish 22:22  --name gitlab   --restart always --volume /srv/gitlab/config:/etc/gitlab --volume /srv/gitlab/logs:/var/log/gitlab --volume /srv/gitlab/data:/var/opt/gitlab gitlab/gitlab-ce:latest



  • hostname: El nombre del host es pruebasgitlab.
  • publish: publica el puerto 443, 80 y 22 de la instancia del contenedor.
  • name: El nombre de la imagen es gitlab.
  • restart always: Siempre levanta la instancia . 
  • volume: Se define unos volumenes (son directorios del host anfitrion que se montan en los contenedores), en este caso son 3 directorios.
  • gitlab/gitlab-ce:latest: Se trae la última versión de la imagen de gitlab-ce.


El contenedor de Gitlab usa volumenes montados para datos persistentes:

  • /srv/gitlab/data se monta en /var/opt/gitlab
  • /srv/gitlab/logs se monta en /var/log/gitlab
  • /srv/gitlab/config se monta en /etc/gitlab


Para configurar el Gitlab se tiene un archivo que se tiene en el equipo /etc/gitlab/gitlab.rb .

Para acceder a la configuración se puede iniciar bas en el contenedor que está corriendo, esto permite navegar en la estructura de directorio y ejecutar cualquier programa:

docker exec -it gitlab /bin/bash

Ahora se puede editar el archivo /etc/gitlab/gitlab.rb .

También se puede editar con el siguiente comando:

docker exec -it gitlab vi /etc/gitlab/gitla.rb

Para habilitar configuraciones de https se puede revisar el siguiente enlace, o las configuraciones de smtp en este otro.

Para reiniciar el gitlab luego del cambio de configuraciones se ejecuta:

doker restart gitlab

A continuación se muestra una captura de pantala del gitlab en funcionamiento desde Docker:


En este artículo se muestra como acceder a varios puertos como el 80,443 y el 22 el cual permite conectarse a dicho contenedor de manera remota.


18 ene. 2016

Iniciando Django usando Docker

En el artículo anterior se tocó el tema de uso de un archivo Dockerfile para crear una imagen de docker que muestra una página estática por medio de apache.

En este artículo se usará el framework django para mostrar el funcionamiento del admin de Django desde Docker(artículos anteriores de Django).

A continuación los artículos anteriores:

  1. Instalar Docker en Debian Jessie.
  2. Uso de docker en Debian Jessie (parte 1).
  3. Uso de docker en Debian Jessie (parte 2).
  4. Crear una imagen Docker a partir de un archivo Dockerfile.


En este caso se tiene creado un proyecto y una aplicación de Django con la siguientes directorios y archivos, se ha creado una cuenta de administrador (artículo de creación de proyecto y aplicación):

djangoapp
├── db.sqlite3
├── djangoapp
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── settings.py
│   ├── settings.pyc
│   ├── urls.py
│   ├── urls.pyc
│   ├── wsgi.py
│   └── wsgi.pyc
└── manage.py

El archivo Dockerfile contiene lo siguiente:

FROM debian
MAINTAINER Ernesto Crespo <ecrespo@gmail.com>
RUN apt-get update
RUN apt-get install -y python-pip
RUN apt-get clean
RUN pip install virtualenv
RUN pip install django
RUN echo "America/Venezuela/Caracas" > /etc/timezone && dpkg-reconfigure -f noninteractive tzdata

# El puerto 8000 se publica en el contenedor
EXPOSE 8000

# Copiar aplicacion del subdirectorio djangoapp/ al directorio
# /djangoapp en el contenedor
ADD djangoapp /srv/djangoapp
COPY djangoapp /srv/djangoapp
RUN chown -R www-data:www-data /srv/djangoapp
RUN chmod a+x /srv/djangoapp/manage.py
# Establecer el directorio de trabajo
WORKDIR /srv/djangoapp

# Se lanza el servidor web del proyecto django
CMD python manage.py runserver 0.0.0.0:8000



  1. FROM debian: Se usa de base la última versión de Debian.
  2. MAINTAINER: Se define el nombre del mantenedor del contenedor.
  3. RUN: Se ejecuta la serie de comandos para instalar los paquetes necesarios, cambiar el huso horario del contenedor, cambiar de premisos y propietario de la carpeta donde estará el proyecto de django.
  4. EXPOSE 8000: Se publica el puerto 8000.
  5. ADD djangoapp /srv/djangoapp : Se copia el proyecto Django.
  6. COPY djangoapp /srv/djangoapp: Se copia el proyecto Django.
  7. WORKDIR /srv/djangoapp: Se define el directorio de trabajo.
  8. CMD python manage.py runserver 0.0.0.0:8000: Se ejecuta el servidor web del proyecto Django en el puerto 8000.
Al salvar el archivo se ejecuta la construcción de la imagen:

docker build -t myapp .

Al terminar de crear la imagen se tiene:

$ docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
myapp                       latest              948e276ee3d9        31 minutes ago      388.5 MB
ecrespo/empaquetadodebian   latest              ee5883957d64        3 weeks ago         656.9 MB
sameersbn/gitlab            latest              5a4b34c1445c        5 weeks ago         675.8 MB
busybox                     latest              fc0db02f3072        5 weeks ago         1.113 MB
debian                      latest              8b9a99209d5c        6 weeks ago         125.1 MB
sonarqube                   latest              dd47274097f7        10 weeks ago        942.5 MB
hello-world                 latest              975b84d108f1        3 months ago        960 B
docker/whalesay             latest              fb434121fc77        7 months ago        247 MB
sonarqube                   5.1                 37c5c2e971dc        8 months ago        589 MB

Para ejecutar la instancia del docker myapp

docker run -p 8000:8000 myapp

Se asocia el puerto 8000 del equipo al puerto 8000 del docker myapp.

En la siguiente figura se tiene el inicio de sesión del admin de Django:


En la siguiente figura se muestra la administración de Django:


Se agrega un usuario al admin de Django:




Los procesos de docker son:


Se puede detener la instancia de docker el cual contenedor tiene el id 5fedc4d0615b.
$ docker stop 5fedc4d0615b
5fedc4d0615b

O iniciar la instancia 
$ docker start 5fedc4d0615b
5fedc4d0615b


Crear otro usuario admin:
Para ello en docker se puede ejecutar una consola interactiva con el siguiente comando:

$ docker exec -it determined_brattain /bin/bash 
root@5fedc4d0615b:/srv/djangoapp# 


root@5fedc4d0615b:/srv/djangoapp# python manage.py createsuperuser --username=admin --email=admin@myapp.com
Password: 
Password (again): 
Superuser created successfully.

Se sale de la consola y se prueba ingresar con el usuario admin. 


Ya se puede ir personalizando una aplicación en la instancia de la imagen de docker que se ha creado.

17 ene. 2016

Crear una imagen Docker a partir de un archivo dockerfile

En los artículos anteriores sobre docker se tiene:

  1. Instalar Docker en Debian Jessie.
  2. Uso de Docker en Debian Jessie (parte 1).
  3. Uso de Docker en Debian Jessie (parte 2).

En este artículo se muestra como construir una imagen Docker a partir de un archivo llamado Dockerfile.

Se usará una imagen de Debian como base que a partir de allí se tendrá una con apache corriendo, en el directorio /var/www/html se tendrá un archivo index.html con dos encabezados html que mostrarán que se tiene corriendo apache por medio de docker.

Lo primero que se tiene que hacer es crear el archivo Dockerfile en un directorio por ejemplo dockerbuilds, el archivo contendrá lo siguiente:

FROM debian
MAINTAINER Ernesto Crespo
RUN apt-get update
RUN apt-get install apache2 -y --force-yes
RUN echo "<h1>Apache con docker</h1> <h2>Prueba hecha por Seraph1</h2>" > /var/www/html/index.html
EXPOSE 80
ENTRYPOINT apache2ctl -D FOREGROUND

Descripción del archivo:
  1. FROM:Lo primero que se define es la imagen que se va a usar, en este caso Debian.
  2. MAINTAINER: El nombre del mantenedor de la imagen.
  3. RUN: Instrucción que permite ejecutar comandos dentro del contenedor. En este caso se  actualiza la lista de paquetes de Debian, se instala apache y se copia el contenido de index.html en la ruta que le corresponde.
  4. Se expone el puerto 80 del servidor  apache.
  5. Se indica que se ejecute apache en primer plano cada vez que se inicie el contenedor.

Luego de tener el archivo listo, se ejecuta el comando para la creación de la imagen usando dicho archivo:

docker build -t ecrespo/apache .

Se define que el nombre de la imagen será ecrespo/apache.

Al terminar de realizar el proceso de construcción se tendrá la imagen en el equipo:
$docker images
REPOSITORY                                      TAG                 IMAGE ID            CREATED              VIRTUAL SIZE
ecrespo/apache                                  latest              ae0d4610be9d        About a minute ago   197.8 MB
debian-django                                   latest              36896e115e32        2 weeks ago          601.1 MB
ecrespo/empaquetadodebian                       latest              ee5883957d64        2 weeks ago          656.9 MB
debian                                          latest              8b9a99209d5c        6 weeks ago          125.1 MB
sonarqube                                       latest              dd47274097f7        10 weeks ago         942.5 MB
debian                                          8.1                 f05335696a9b        4 months ago         125.2 MB

Se tiene la imagen de 197.8MB. 

Para terminar se ejecuta la imagen que se llama ecrespo/apache, la instancia se llamará apache1, se captura el puerto local 80 y lo muestra en el equipo como puerto 90.

docker run --name apache1 -d -p 90:80 ecrespo/apache

Se lista los procesos de docker y se tiene el contenedor de apache corriendo:

$docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
0195ebf31328        ecrespo/apache      "/bin/sh -c 'apache2c"   44 minutes ago      Up 44 minutes       0.0.0.0:90->80/tcp   apache1

El nombre es ecrespo/apache, el comando que se ejecuta es el de apachectl, tiempo de creado 44 minutos y arriba desde hace 44 minutos, el nombre de la instancia es apache1 y redirecciona el puerto 90 al puerto 80.


Con docker stats pasando el id del contenedor se puede tener información de consumo de recursos de dicho contenedor.




Ahora se abre el navegador con localhost:90 y se tiene lo que muestra el mensaje que veremos en la siguiente figura:




Para detener el contenedor se ejecuta docker stop con el id del contenedor:

$ docker stop 0195ebf31328
0195ebf31328


Al listar los procesos se nota que ya no se tiene el contenedor apache1 corriendo.

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES







En este artículo se muestra una pequeña personalización del apache, poco a poco se irá aumentando el nivel de personalización de los contenedores para tener servicios reales en funcionamiento. 

12 ene. 2016

Automatizar prueba funcional del Inicio y fin de sesión del Admin de Django

Los artículos anteriores sobre PyAutoGUI trataron sobre:

Este artículo realiza un inicio de sesión en el Admin de Django, coloca el usuario, la clave, le da click al botón aceptar, le dice al navegador que no quiere guardar la clave, cierra la sesión y por último vuelve a la página de inicio del admin de Django. 

El script guardará capturas de pantalla de cada paso que se realice. 

Nota: Las coordenadas de los widgets de la página fueron capturados previamente.


El código se muestra a continuación:

#!/usr/bin/env python3
import pyautogui, time
time.sleep(2)
#Entrada de datos del usuario
pyautogui.click(540, 295)
pyautogui.typewrite('ecrespo')
#Se realiza la captura de pantalla
im1 = pyautogui.screenshot()
#Se salva en un archivo
im1.save('./imagenes/1.png')
time.sleep(2)
#Entrada de datos de la clave
pyautogui.click(540, 358)
pyautogui.typewrite('123456')
#Se realiza la captura de pantalla
im1 = pyautogui.screenshot()
#Se salva en un archivo
im1.save('./imagenes/2.png')
time.sleep(2)
#Darle click al boton de inicio de sesion
pyautogui.click(668, 397)
time.sleep(4)
#Se realiza la captura de pantalla
im1 = pyautogui.screenshot()
#Se salva en un archivo
im1.save('./imagenes/3.png')
#No guardar la clave del usuario
pyautogui.click(1232, 204)
time.sleep(4)
#Se realiza la captura de pantalla
im1 = pyautogui.screenshot()
#Se salva en un archivo
im1.save('./imagenes/4.png')
#Darle click a cerrar sesion
pyautogui.click(1335, 108)
time.sleep(4)
#Se realiza la captura de pantalla
im1 = pyautogui.screenshot()
#Se salva en un archivo
im1.save('./imagenes/5.png')
#Regresar a la pagina de inicio de sesion
pyautogui.click(42, 215)
#Se realiza la captura de pantalla
im1 = pyautogui.screenshot()
#Se salva en un archivo
im1.save('./imagenes/6.png')

A continuación se muestra las capturas de pantallas realizadas:

El vídeo de la interacción se muestra a continuación:

Con esta herramienta se puede automatizar pruebas funcionales para aplicaciones de escritorio como de aplicaciones web. 

Utilizar drag del ratón con Python usando PyAutoGUI

En el artículo anterior se muestra la librería PyAutoGUI, haciendo una captura de pantalla, capturando la posición del ratón y realizar movimientos con el mismo.

En este artículo se muestra la ejecución del click del ratón y de drag.


Este primer ejemplo la idea es darle click al menú superior en Gnome donde se muestra el volumen, el brillo y acceso a la configuraciones.

El código se muestra a continuación.

 #!/usr/bin/env python

#Importar el modulo

import pyautogui



#Se hace click en la posicion del menu del sistema del escritorio gnome.

pyautogui.click(2712,13)



#Se realiza la captura de pantalla

im1 = pyautogui.screenshot()



#Se salva en un archivo

im1.save('prueba2.png')





La siguiente figura muestra la ejecución del script:


El siguiente script realizará un dibujo sobre Gimp usando drag y al final hace una captura de pantalla del dibujo:

#!/usr/bin/env python3

import pyautogui, time





time.sleep(5)

pyautogui.click()

# click to put drawing program in focus



distancia = 200

while distancia > 0:

 pyautogui.dragRel(distancia, 0, duration=0.2)

 # se mueve a la derecha

 distancia = distancia - 10

 #se mueve hacia abajo

 pyautogui.dragRel(0, distancia, duration=0.2)

 # se mueve hacia la izquierda

 pyautogui.dragRel(-distancia, 0, duration=0.2)

 distance = distancia - 10

 #Se mueve hacia arriba

 pyautogui.dragRel(0, -distancia, duration=0.2)



#Al terminar el dibujo se espera 5 seg para la captura de pantalla.

time.sleep(5)

#Se realiza la captura de pantalla

im1 = pyautogui.screenshot()



#Se salva en un archivo

im1.save('prueba3.png')

La siguiente imagen muestra el resultado del script:

Y el vídeo de la acción del script:

11 ene. 2016

Controlando el movimiento del ratón desde python con PyAutoGUI

En el desarrollo de software el proceso final es la ejecución de pruebas funcionales. En el caso de aplicaciones gráficas de escritorio o web, viene un técnico a insertar datos, darle clic a un botón todo un proceso a mano!!!

Existen herramientas que facilitan la automatización de dichas pruebas, una de ellas es PyAutoGUI.

Esta librería facilita interactuar con el ratón y el teclado de manera programática.

Si se tiene una pantalla de 1920x1080 de resolución el manejo de coordenadas dentro de la pantalla es como lo muestra la siguiente figura:


El desplezamiento en la pantalla es de izquierda a derecha de manera horizontal con la valor de X, y de manera vertical de arriba hacía abajo con el valor de Y.

Para instalar la librería PyAutoGUI se requiere tener python-xib instalado, para instalarlo en Debian distros basadas en debian se usa apt:

#apt-get install python-xlib

La librería se instala por medio de pip:
#pip install PyAutoGUI

La primera prueba que se hará es mostrar la resolución de la pantalla del equipo donde me encuentro escribiendo este artículo:

El código se muestra a continuación:
#!/usr/bin/env python3

#Se importa la libreria pyautogui
import pyautogui
#Se muestra en pantalla la resolucion de dicha pantalla
#como una tupla.

print(pyautogui.size())

Al ejecutar el script se tiene:
$python ej1.py 
Xlib.protocol.request.QueryExtension
(1366, 768)

Así que, la resolución de la pantalla del equipo donde estoy escribiendo la presentación tiene una resolución de 1366x768.

También se puede capturar la posición del ratón:

#!/usr/bin/env python3

#Se importa la libreria pyautogui
import pyautogui

#Captura la posicion del raton
resultado = pyautogui.position()


print(resultado)

El script devuelve una tupla de la posición del ratón en un instante dado.
Se ejecutará el script 4 veces, una cada esquina de la pantalla para obtener las coordenas de la pantalla de resolución 1366x768:

Esquina superior izquierda:
ernesto@grievous:~/bin/python/autogui$ python ej2.py 
Xlib.protocol.request.QueryExtension
(0, 0)

Esquina superior derecha:
ernesto@grievous:~/bin/python/autogui$ python ej2.py 
Xlib.protocol.request.QueryExtension
(1366, 0)

Esquina inferior izquierda:
ernesto@grievous:~/bin/python/autogui$ python ej2.py 
Xlib.protocol.request.QueryExtension
(0, 768)

Esquina inferior derecha
ernesto@grievous:~/bin/python/autogui$ python ej2.py 
Xlib.protocol.request.QueryExtension
(1366, 768)



También se puede crear una captura de pantalla, El script s muestra a continuación:
#!/usr/bin/env python 

#Se importa el modulo
import pyautogui

#Se realiza la captura de pantalla
im1 = pyautogui.screenshot()

#Se salva en un archivo
im1.save('prueba.png')

Y a continuación la captura de pantalla:


Para terminar se mostrará un script que permite mover el ratón a una cierta posición:

#!/usr/bin/env python



#Importar el modulo
import pyautogui

#Se genera un ciclo de 10 veces
for i in range(20):
        #Se mueve a la coordenada (100,100)
        pyautogui.moveTo(100,100,duration=0.25)
        #Se mueve a la coordenada (200,100)
        pyautogui.moveTo(300,100,duration=0.25)
        #Se mueve a la coordenada (200,200)
        pyautogui.moveTo(300,300,duration=0.25)
        #Se mueve a la coordenada (100;200)
        pyautogui.moveTo(100,300,duration=0.25)


En el siguiente vídeo se muestra el funcionamiento del Script:


En siguiente artículos se seguirá demostrando el uso de la librería.

4 ene. 2016

Módulo que permite acceder a archivos de configuración .ini usando ConfigParser en Python

Hace años tenía un módulo en python que permitía acceder a archivos de configuración del tipo .ini (secciones, clave-valor) por medio del módulo ConfigParser (un ejemplo de como usar ConfigParser lo pueden ver acá).

Ese script o módulo lo han utilizado en varios proyectos y últimamente lo estaba actualizando e incorporando en un desarrollo de envío de SMS.

Este módulo es de propósito general y no debería estar incorporado en una aplicación en específico o en cada aplicación que lo necesite, así que he creado un proyecto en github donde alojaré dicho módulo, en un futuro cercano espero subirlo a los repositorios de Python.

La segunda versión de dicho módulo toma conceptos de los artículos de programación orientada a objetos de artículos anteriores:

  1. Volviendo a lo básico, POO en Python (parte 1)
  2. Volviendo a lo básico, POO en Python (parte 2)
  3. Volviendo a lo básico, POO en Python (parte 3)

La primera versión del código la pueden ver a continuación:

#!/usr/bin/env python
import ConfigParser,os
"""
Descripcion: Modulo que permite manipular archivos de configuracion.
Autor: Ernesto Crespo
Correo: ecrespo@gmail.com
Licencia: GPL Version 3
Copyright: Copyright (C) 2011 Ernesto Nadir Crespo Avila
Version: 0.2

"""

class config:
    """Modulo que permite manejar archivo de configuracion"""    
    def __init__(self,cnffile):
        self.__cnffile = cnffile
        self.__config = ConfigParser.ConfigParser()
        self.__config.read(self.__cnffile)
        
        
    def ShowItemSection(self,section):
        return self.__config.items(section)
    
    def ShowValueItem(self,section,option):
        return self.__config.get(section,option)
    
    def change(self,section,option,value):
        self.__config.set(section,option,value)

    
    def write(self):
        self.__config.write(open(self.__cnffile,'w'))



La nueva versión cambia la declaración del objeto, usa el decorador @property para definit el getter y el setter, define el método getattr y agrega el método que muestra las secciones:

El código de la nueva versión se muestra a continuación:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

#import ConfigParser
from ConfigParser import ConfigParser

"""
Nombre: pywrapper_config
Descripcion: Modulo que permite manipular archivos de configuracion.
Autor: Ernesto Crespo
Correo: ecrespo@gmail.com
Licencia: GPL Version 3
Copyright: Copyright (C) 2016 Ernesto Nadir Crespo Avila <ecrespo@gmail.com> 
Version: 0.3

"""

#Clase config
class Config(object):
    """Clase Config: facilita el uso del modulo ConfigParser"""

    def __init__(self, cnffile):
        """Constructor toma el archivo de configuracion e inicializa ConfigParser"""
        self._cnffile = cnffile
        self._config = ConfigParser()
        self._config.read(self._cnffile)

    @property
    def cnffile(self):
        """Se consulta el valor del archivo de configuracion"""
        return self._cnffile
    
    @cnffile.setter
    def cnffile(self,cnffile):
        """Se modifica el valor del archivo de configuración y se vuelve a leer el mismo"""
        self._cnffile = cnffile
        self._config.read(self._cnffile)

    def __getattr__(self):
        """__getattr__ devuelve None si se trata de acceder a un atributo que no existe"""
        return None 

    def show_sections(self):
        """Muestra las secciones del archivo de configuracion"""
        return self._config.sections()

    def show_item_section(self, section):
        """Se define la funcion que muestra los item de una seccion"""
        return self._config.items(section)

#
    def show_value_item(self, section, option):
        """Se muestra el valor de los item"""
        return self._config.get(section, option)

#
    def change(self, section, option, value):
        """Se cambia el valor de la opcion"""
        self._config.set(section, option, value)

#
    def write(self):
        """Se escribe al archivo de configuracion"""
        self._config.write(open(self._cnffile,'w'))



if __name__ == '__main__':
    #Se crea la instancia de Config pasando el archivo de configuracion ini
    configuracion = Config("./conf/androidsms.conf")
    #Se muestra las secciones
    print("Secciones: {0} ".format(configuracion.show_sections()))
    #Se muestra los items de la seccion server
    print("Items de la seccion server: {0} ".format(configuracion.show_item_section("server")))
    #Se muestra la ip de la seccion server
    print("IP: {0}".format(configuracion.show_value_item("server","ip")))
    #Se muestra el archivo de configuracion
    print("Archivo de configuracion: {0}".format(configuracion.cnffile))
    #Se cambia en caliente de archivo de configuracion
    configuracion.cnffile = "./conf/python_android_sms.conf"
    print("Archivo de configuracion: {0}".format(configuracion.cnffile))
    #Se muestra las secciones del nuevo archivo de configuracion
    print("Secciones: {0}".format(configuracion.show_sections()))

A continuación se muestra el resultado de ejecutar el código anterior:
$ python pywrapper_config.py 
Secciones: ['sqlite', 'time', 'server'] 
Items de la seccion server: [('ip', '"127.0.0.1"'), ('port', '"8080"')] 
IP: "127.0.0.1"
Archivo de configuracion: ./conf/androidsms.conf
Archivo de configuracion: ./conf/python_android_sms.conf
Secciones: ['sqlite', 'time', 'server', 'tiempo', 'usb', 'paths']


El truco de cambiar de archivo de configuración se tiene en el setter, a continuación el código: 

    @cnffile.setter
    def cnffile(self,cnffile):
        """Se modifica el valor del archivo de configuración y se vuelve a leer el mismo"""
        self._cnffile = cnffile
        self._config.read(self._cnffile)

Aparte de agregar el archivo de configuración a la variable privada cnffile se vuelve a leer el archivo de configuración, esto facilita el cambio de archivos de configuración en caliente sin tener que volver a instanciar la clase Config.

31 dic. 2015

Volviendo a lo básico, POO en Python (parte 3)

Continuando con algunos conceptos de POO en Python, se tienen los artículos anteriores:

  1. Volviendo a lo básico, POO en Python (parte 1)
  2. Volviendo a lo básico, POO en Python (parte 2)



En esté artículo se muestra el uso de __getattr__ (se ejecuta al acceder a un atributo que no existe), como obtener valores y modificar valores, y como se simplifica con el uso del decorador @property.


Se creará la clase Punto donde se mostrará el uso de __getattr__:

class Punto(object):

 

 def __init__(self,x=3,y=5):

  self._x = x

  self._y = y

 

 def __getattr__(self,attr):

  print "No se puede acceder a un atributo invalido"



 



if __name__ == "__main__":

 punto = Punto(5,5)

 punto.var

 

Al ejecutar el programa se tiene:

$ python ej6.py 
No se puede acceder a un atributo invalido


Al intentar acceder a un atributo no existente se ejecuta el método __getattr__ donde se muestra en pantalla el mensaje no se puede acceder a un atributo invalido.



El siguiente código se vuelve a mostrar la clase punto pero está vez se tiene dos métodos, uno que muestra el valor del punto y otro que modifica el valor del punto y se usará la función interna property.

La sintaxis de la función property es:
property(getter,setter,delete,doc).

El código a continuación:


class Punto(object):

 def __init__(self,x,y):

  self._x = x

  self._y = y



 def get_punto(self):

  return (self._x, self._y)



 def set_punto(self,punto):



  self._x, self._y = punto



 punto = property(get_punto,set_punto)





if __name__ == "__main__":

 punto = Punto(4,6)

 print(punto.punto)

 punto.punto = (10,15)

 print(punto.punto)

Al ejecutar el programa se tiene:

$ python ej7.py 
(4, 6)
(10, 15)


Como último ejemplo de uso de getter y setter se muestra el uso del decorador @property, y como simplifica el uso de property con respecto al código anterior:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
class Punto(object):
def __init__(self,punto):
self._x,self._y = punto
@property
def punto(self):
return (self._x,self._y)
@punto.setter
def punto(self,punto):
self._x,self._y = punto
if __name__ == "__main__":
cordenada = Punto((4,6))
print(cordenada.punto)
cordenada.punto = (10,15)
print(cordenada.punto)
Al ejecutar el programa se tiene: 

$ python ej8.py 
(4, 6)
(10, 15)

Lo primero que se hace es definir un método punto donde se retorna una tupla con el valor de x y de y, a este método se le coloca el decorador property. Luego se crea otro método llamado punto donde se toma un punto y se le asigna a x y a y pero definiendo un decorador @punto.setter el primer método hace el trabajo del getter y el segundo del setter. 



Referencia:

  1. Python Avanzado
  2. Ejemplos de referencia (módulos y tareas)





AddThis