Translate

sábado, 15 de septiembre de 2012

Web en Apache con Python y WSGI (I)

Buscando la solución perfecta...

Probando distintos frameworks ( marcos ) de Python ( Bottle, Flask, CherryPy, Twistted, Tornado, ...) he comprobado que no existe una solución perfecta que pueda aprender en poco tiempo, en definitiva, que comprenda lo que estoy haciendo.

Cuando existe un problema a resolver, lo separo en partes más pequeñas, que luego puedo resolver fácilmente. Este es el procedimiento que seguiré para explicar como realizar una Web que funcione en un servidor Apache, con Python + Web Server Gateway Interface ó Python+WSGI.

Configurar httpd.conf de Apache con mod_wsgi

Compruebe si en el fichero /etc/httpd/conf/httpd.conf existe una línea como

LoadModule wsgi_module modules/mod_wsgi.so

Si no la encuentra, compruebe también si existe otra como :

Include conf.d/*.conf

que el servidor utilizará para cargar configuraciones de otros ficheros, en este caso del directorio /conf.d  y compruebe si en el directorio indicado existe algún fichero que cargue la línea mencionada anteriormente.

Si la comprobación ha sido negativa. Puede instalar mod_wsgi.so, para Centos 5.6 se puede instalar con :

# yum install mod_wsgi

ó

# cd /usr/src
# wget http://modwsgi.googlecode.com/files/mod_wsgi-3.4.tar.gz
# tar zxvf mod_wsgi-3.4.tar.gz
# cd mod_wsgi-3.4
# ./configure
Nota : Si obtiene un error 'apxs: command not found ', le indica que no existe el paquete para instalar módulos en apache, necesitará...
yum install httpd-devel

Después de descomprimir el paquete tar.gz compilamos e instalamos

# make 
# make install
y agregamos la línea para cargar el módulo en el httpd.conf junto a las rentantes líneas de carga de módulos :

LoadModule wsgi_module        lib/apache/mod_wsgi.so
Y reiniciamos el Apache

# service httpd restart

Si esta instalacón no fuera correcta para su distribución, debe buscar una solución para instalarlo, antes de continuar.

Configurar el Virtual Host en Apache

De forma habitual ( hablando de Linux ), Apache servirá las páginas que se crean en /var/www/html

Para esta prueba, crearemos la siguiente estructura :
/var/www/web_de_prueba/
                                           |--- app 
                                           |       |-------modulos  
                                           |
                                           |---- html
                                           |-----log
                                           |-----error 

Traduciendo esta información a un Virtual Host de Apache será :


   AllowOverride None
   Order Deny,Allow
   Deny from all

   DocumentRoot /var/www/web_de_prueba/html
   ServerName web_de_prueba
   WSGIScriptAlias / /var/www/web_de_prueba/app/app.wsgi
   ServerAlias web_de_prueba
   ServerAdmin web_de_prueba@web_de_prueba.es

   ErrorLog /var/www/web_de_prueba/log/error.log
   CustomLog /var/www/web_de_prueba/log/access.log combined
  
   ErrorDocument 400 /error/400.html
   ErrorDocument 401 /error/401.html
   ErrorDocument 403 /error/403.html  
   ErrorDocument 404 /error/404.html
   ErrorDocument 405 /error/405.html
   ErrorDocument 500 /error/500.html
   ErrorDocument 503 /error/503.html

  
      Options FollowSymLinks
      AllowOverride All
      Order Allow,deny
      Allow from all
  


Además, es necesario añadir en el fichero /etc/hosts :

127.0.0.1    web_de_pruebas

No olvide cada vez que realice una modificación, rearrancar el servidor Web :

#  service httpd restart


No aguanto 'Hola Mundo !'

Ya sé que es lo habitual, pero he comprobado tantos 'Hola Mundo!' que necesito hacer alguna prueba que aporte más energía para perder el miedo a un mundo que se programará fuera de un framework. Donde obtendremos un control completo sobre el programa :

En /var/www/web_de_prueba/app  creamos el fichero app.wsgi con el siguiente código :


# -*- coding: utf-8 -*- 

import os 
import sys 

sys.path.append('/var/www/web_de_prueba/app') 

def application(environ, start_response):   
     ''' Para los que añoran el Hola Mundo! '''    output = "Hola Mundo"+"<br>"
     try:
         ''' Y comprobamos los valores que nos proporcionan los items de '''
         for tup in environ.items():           
              output += str(tup) + "
"   
     except Exception as e:       
         output += "%s \n" % e
     start_response('200 OK',[('Content-Type','text/html;charset=utf-8')])

     return output

 Reiniciar el servidor

 # service httpd restart

y comprobar en el navegador  http://web_de_prueba
verá algo parecido a lo siguiente ....

 Hola Mundo
('mod_wsgi.listener_port', '80')
('mod_wsgi.listener_host', '')
('SERVER_SOFTWARE', 'Apache/2.2.15 (CentOS)')
('SCRIPT_NAME', '')
('mod_wsgi.handler_script', '')
('SERVER_SIGNATURE', '
Apache/2.2.15 (CentOS) Server at web_de_prueba Port 80
\n')
('REQUEST_METHOD', 'GET')
('PATH_INFO', '/')
('SERVER_PROTOCOL', 'HTTP/1.1')
('QUERY_STRING', '')
('HTTP_USER_AGENT', 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0.7) Gecko/20120829 Firefox/10.0.7')
('HTTP_CONNECTION', 'keep-alive')
('SERVER_NAME', 'web_de_prueba')
('REMOTE_ADDR', '127.0.0.1')
('mod_wsgi.request_handler', 'wsgi-script')
('wsgi.url_scheme', 'http')
('PATH_TRANSLATED', '/var/www/web_de_prueba/app/app.wsgi/')
('SERVER_PORT', '80')
('wsgi.multiprocess', True)
('mod_wsgi.input_chunked', '0')
('SERVER_ADDR', '127.0.0.1')
('DOCUMENT_ROOT', '/var/www
/web_de_prueba/html')
('mod_wsgi.process_group', '')
('SCRIPT_FILENAME', '/var/www
/web_de_prueba/app/app.wsgi')
('SERVER_ADMIN', 'web_de_prueba@web_de_prueba.es')
('wsgi.input', )
('HTTP_HOST', 'web_de_prueba')
('wsgi.multithread', False)
('mod_wsgi.callable_object', 'application')
('HTTP_CACHE_CONTROL', 'max-age=0')
('REQUEST_URI', '/')
('HTTP_ACCEPT', 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8')
('wsgi.version', (1, 1))
('GATEWAY_INTERFACE', 'CGI/1.1')
('wsgi.run_once', False)
('wsgi.errors', )
('REMOTE_PORT', '42954')
('HTTP_ACCEPT_LANGUAGE', 'es-es,es;q=0.8,en-us;q=0.5,en;q=0.3')
('mod_wsgi.version', (3, 2))
('mod_wsgi.application_group', '
web_de_prueba|')
('mod_wsgi.script_reloading', '1')
('wsgi.file_wrapper', )
('HTTP_ACCEPT_ENCODING', 'gzip, deflate')


Si observa la aplicación ...

def application(environ, start_response)

comprobamos que tiene dos parámetros y :
contiene toda la información que necesitamos saber del servidor y cualquier otro dato que pueda recibir por medio de un formulario, como muestra, observe los resultados obtenidos. Más adelante realizaremos otra prueba para comprobar la utilidad de esta información.

contiene varios parámetros posicionales :
    
start_response(status, response_headers, exc_info=None)   

puede ser "200 OK" or "404 Not Found"
esta compuesta de una lista de tuplas ((header_name, header_value),...)  que se explicarán mas adelante.

Para la siguiente prueba, crearemos un sencillo formulario, con el mismo código, aunque un poco cambiada la notación, en vez de usar += hemos usado .append( para construir el html  :

# -*- coding: utf-8 -*-

import os
import sys
import string

sys.path.append('/var/www/web_de_prueba/app')

def application(environ, start_response):
    # crear el html
    output = ['<html><head></head><body>']

    # crear un formulario sencillo
    output.append('<form method="post">')
    output.append('<input type="text" name="test1">')
    output.append('<input type="text" name="test2">')
    output.append('<input type="submit">')
    output.append('</form>')

    try:
         for tup in environ.items():
             output.append(str(tup) + "<br>")
    except Exception as e:
         output.append("%s \n" % e)

    # cuando recibimos un valor del formulario, lo mostramos
    if environ['REQUEST_METHOD'] == 'POST':
        # datos recibidos a traves de POST:
        output.append('<h1>DATOS DEL FORMULARIO</h1>')
        if environ['wsgi.input'] != None:
            output.append(environ['wsgi.input'].read())
 
    output.append('</body></html>')
   
    # enviar resultados
   
    output_len = len(' '.join(output))
    start_response('200 OK', [('Content-type', 'text/html'),
                              ('Content-Length', str(output_len))])
    # creamos un string con toda la lista
    return ' '.join(output) 
 

Compruebe el código de ...

    start_response('200 OK', [('Content-type', 'text/html'),
                              ('Content-Length', str(output_len))])

se incorpora la información de la longitud de los datos a mostrar.

Reiniciar el servidor 

 # service httpd restart

y comprobar en el navegador  http://web_de_prueba  podrá jugar con su formulario.

Avanzamos un poco más comprobando el parámetro 'REQUEST_URI', en el código anterior, sustituya la parte que hace referencia al formulario (# crear un formulario sencillo) por estos dos formularios:

    if environ['REQUEST_URI'].endswith('prueba1'):
       output.append('<form method="post">')
       output.append('<input type="text" name="test1">')
       output.append('<input type="text" name="test2">')
       output.append('<input type="submit">')
       output.append('</form>')
    if environ['REQUEST_URI'].endswith('prueba2'):
       output.append('<form method="post">')
       output.append('<input type="text" name="test3">')
       output.append('<input type="submit">')
       output.append('</form>')


He incorporado una comprobación, para saber a qué formulario nos estamos refiriendo, cuando se indique http://web_de_prueba/prueba1 ó http://web_de_prueba/prueba2 veremos distintos resultados. 

Con /prueba1

DATOS DE FORMULARIO

test1=varios+cosas&test2=nada+de+nada

y con /prueba2

DATOS DE FORMULARIO

test3=varios+cosas

Para próximas entregas, quedará cómo cargar templates externos, guardar datos en una bb.dd., sessiones, cookies, autenticación, ... y comprendiendo lo que escribimos.

Hasta pronto !!!

Entorno de las pruebas :
CENTOS 6.2   +  PYTHON 2.6.6 + APACHE 2.2.15

En el siguiente artículo hablaré sobre los generadores/importadores de plantillas o TEMPLATE.py

domingo, 29 de julio de 2012

Recuerda siempre que eres único... Exactamente igual que todos los demás

La curiosidad siempre ha sido una característica importante para observar, para aprender, para repetir y para mejorar. Si no eres un crack, no importa, observa, copia, reproduce lo que has visto y mejoralo, este ha sido el gran procedimiento de mejora continua de cualquier idea.

En el desarrollo de cursos, he procurado ponerme en los zapatos de los curiosos, ayudándoles a observar lo que estaba sucediendo, aplicando los conocimientos a un proceso real que les fuera cercano y que podía proponer cualquiera. Aprendes cuando exploras distintos caminos para conseguir un objetivo y se pierde interés cuando recorres el mismo camino una y otra vez.

Basándome en estas experiencias, solo puedo decir, que aquí no encontrarás otra cosa que a un alumno más, curioso por aprender y aplicar los conocimientos a procesos tangibles y reales, que busca constantemente para mejorar el camino hacia el objetivo.

Aplicaremos PYTHON complementando su gran flexibilidad ( clases, funciones, listas, diccionarios, tuplas, ...etc,) con QT para desarrollar un cliente gráfico. Desarrollaremos servidores ( WEB, SOCKET, ...). Y no puede faltar el tratamiento de ficheros y BB.DD. con MySQL ( o MariaDB) o Redis y MongoDB ( estas últimas del tipo NoSQL ). No olvidaremos que todavía es necesarios obtener informes o documentos en formato PDF, que podremos imprimir o enviar mediante cualquier medio de comunicación.

Este será el principal objetivo de este BLOG, aprender aplicando los conocimientos a proyectos reales muy alejados del habitual . Espero que también haya propuestas ajenas a este que escribe, para explorar otros caminos. Observo habitualmente,  personas que realizan su trabajo y que aplican matices o experiencias que son altamente enriquecedoras, que me permiten innovar y reunir buenas prácticas en procesos que mejoran día a día.

Espero a los curiosos....