Por marcos, hace 4 meses y 21 días

Creando webservices con Python

Este post nace a raíz de las horas que he «perdido» en el curro intentando desplegar un servidor de webservices en Python, partiendo del descriptor (fichero WSDL). La causa de esas horas «perdidas» es el uso de tipos de datos complejos, es decir, no un simple String por ejemplo, sino una secuencia de éstos (vector de Strings) y el uso de la herramienta wsdl2py para generar la base de lo que será nuestro servidor.

La librería que usaremos es ZSI, que es actualmente la que tiene un desarrollo más activo y parece ser que se impone como la de referencia para desarrollar webservices usando Python. Por contra, la documentación disponible es escasa.

Entremos en materia.

Tenemos el siguiente descriptor que define el servicio que queremos ofrecer y las operaciones que tiene disponibles. Digamos que el servicio tiene que devolver una lista de pares (peso,Variable) para una petición de un String. Caso práctico: le pedimos al webservice la última lista de la compra que hizo Juan (el String «Juan» sería el input del servicio) en la frutería, nos devolverá una lista de parejas (kilos, fruta), por ejemplo :)

Veamos la parte relevante:

  1. <!-- types -->
  2. <wsdl:types>
  3. <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="Pares_NS">
  4. <complexType name="parPesoVariable">
  5. <sequence>
  6. <element name="peso" type="xsd:long" minOccurs="1" maxOccurs="1"/>
  7. <element name="variable" type="xsd:string" minOccurs="1" maxOccurs="1"/>
  8. </sequence>
  9. </complexType>
  10. <complexType name="parPesoVariableList">
  11. <sequence>
  12. <element name="par" type="dat:parPesoVariable" minOccurs="0" maxOccurs="unbounded"/>
  13. </sequence>
  14. </complexType>
  15. </schema>
  16. </wsdl:types>

  17. <!-- messages -->

  18. <wsdl:message name="getParesRequest">
  19. <wsdl:part name="input" type="xsd:string"/>
  20. </wsdl:message>
  21. <wsdl:message name="getParesResponse">
  22. <wsdl:part name="output" type="dat:parPesoVariableList"/>
  23. </wsdl:message>

El primer paso será generar la base de código Python que luego extenderemos para crear nuestro servicio. Para ello usamos el script python wsdl2py y su compañero de viaje wsdl2dispatch. Atención como hacemos uso de tipos de dato complejos (complexType), necesitamos pasarle a wsdl2py el argumento --complexType (o -b), sino, tendremos errores en la serialización de los objetos python y del XML cuando os habléis con vuestro servicio. Aquí me tiré yo horas por no hacer un --help :)

  1. $ wsdl2py --complexType --file Servicio.wsdl
  2. $ wsdl2dispatch --file Servicio.wsdl

Estos dos comandos nos crearán 3 ficheros .py:

  • Servicio_services.py
  • Servicio_services_server.py
  • Servicio_services_types.py

Para crear nuestro servidor, extenderemos Servicio_services_server en una nueva clase (myServer, por ejemplo).

  1. from ZSI import *
  2. class MyServicio(ServicioService):
  3. _wsdl = "".join(open("Servicio.wsdl").readlines()) # para devolver el WSDL a cliente que lo solicite

  4. def soap_getPares(self, ps, **kw):

  5. response = ServicioService.soap_getPares(self, ps, **kw)
  6. request = self.request
  7. return self.getPares(request)

  8. def getPares(self, req):

  9. db = DB.connect()
  10. pares = db.getListaCompra(req.get_element_input())
  11. return pares
  12. ...

  13. # y ahora un poco de OptionParser antes de ponerlo en marcha

  14. op = OptionParser(usage="%prog [options]")
  15. op.add_option("-l", "--loglevel", help="loglevel (DEBUG, WARN)",
  16. metavar="LOGLEVEL")
  17. op.add_option("-p", "--port", help="HTTP port",
  18. metavar="PORT", default=8080, type="int")
  19. options, args = op.parse_args()

  20. if options.loglevel:

  21. loglevel = eval(options.loglevel, logging.__dict__)
  22. logger = logging.getLogger("")
  23. logger.setLevel(loglevel)

  24. # Ejecutamos el servidor

  25. AsServer(port=options.port, services=[MyServicio(),])

Ahora ya sólo faltaría el cliente (y la base de datos, que lo puse ahí vilmente :) ). También, ya habréis visto, los tabuladores del código se los ha pasado el wordpress por el forro, así que no hagáis copypaste ;-)

El cliente, lo dejo como ejercicio al ávido lector (siempre lo había querido decir esto xDD), os dejo un enlace una buena guía por si os quedáis atascados.

2 comentarios

Gravatar #1. Enrique
hace 2 meses y 29 días

Hola Marcos,

Ando buscando información acerca de Python, el nuevo google app engine y la posibilidad de montar servicios web en Python sobre esa infraestructura (que luego pudieran llamarse con un cliente externo al app de google).

Sabes si esto sería posible o conoces alguna referencia al respecto?

Gracias, y enhorabuena por tu blog,

Enrique.

Gravatar #2. marcos
hace 2 meses y 19 días

@Enrique: pues no tengo ni idea de cómo funciona el nuevo Google App, lo siento :)

Un saludo

Escribir un comentario

Si quieres añadir tu comentario a esta entrada, simplemente rellena el siguiente formulario:





* Campos requeridos

Puedes usar estas etiquetas XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>.

No hay trackbacks

Para notificar de una mención en tu blog a esta entrada, habilita la notificación automática (Opciones > Discusión en WordPress) o especifica esta url de trackback: http://​blog.tenak.net/​go/​162/​trackback/