xml-rpc server com web.py

O web.py é realmente fantástico. O design do python, então, nem se fala. É muito prático de desenvolver qualquer coisa, e com web services não poderia ser diferente.

Preparei um serviço de XML-RPC em web.py, que pode ser usado em ambiente de desenvolvimento com o próprio web server built-in, ou em produção via WSGI, rodando esse script pelo lighttpd (meu preferido) ou algo similar.

Também, não poderia deixar de preparar um cliente bem simples, na verdade até tosco, pra acessar as funções desse serviço.

O servidor provê dois métodos: multiply, que recebe a e b, e retorna a*b; sort, que recebe uma lista e retorna seu conteúdo ordenado.

Um dos primeiros problemas que há nesse tipo de sistema, é quando o cliente envia argumentos diferentes dos esperados pelo método. Por exemplo, ao invés de enviar um multiply(2, 3) e esperar o resultado 6, o cliente envia multiply(‘a’, 5, 7).

Isso gera um Exception no lado do servidor e normalmente o cliente não recebe XML, e sim o erro. Pra contornar esse problema, fiz com que o servidor execute os métodos em “safe mode”, de maneira que se houver um exception, o erro seja retornado como texto, dentro do XML, e assim o cliente interpreta o resultado normalmente.

O código do cliente é bem simples, porém não trata nenhum erro. Se o serviço (servidor) não estiver no ar, vai mostrar o connection refused na tela mesmo. Aí vai:

#!/usr/bin/env python
# coding: utf-8
# 20080902 AF

import xmlrpclib

rpc_srv = xmlrpclib.ServerProxy("http://localhost:8080/xmlrpc")
print 'multiply: %s' % rpc_srv.multiply(2, 3)
print 'sort: %s' % rpc_srv.sort([9, 3, 1, 6, 2, 8])

Já o servidor, tem algumas linhas a mais de código. Porém, ele também é muito simples, e extensível. Dentro da classe ServerMethods, basta criar um novo método para que esteja disponível imediatamente via XML-RPC.

Ao acessar a URL padrão do servidor “/” pelo browser, ele irá listar os métodos disponíveis:

Normalmente, um servidor em produção para fins específicos não deve mostrar essa lista por questões de segurança. Nesse caso, um tutorial, é bem útil para ilustrar o quão fácil é lidar com o XML-RPC no python.

Abaixo, o código do servidor:

#!/usr/bin/env python
# coding: utf-8
# 20080903 AF

import web
from inspect import ismethod
from SimpleXMLRPCServer import SimpleXMLRPCDispatcher

dispatcher = SimpleXMLRPCDispatcher(allow_none=False, encoding=False)

urls = (
    '/',        'index',
    '/xmlrpc',  'xmlrpc',
)

# dump a list with all available methods
class index:
    def GET(self):
        web.header('Content-type', 'text/html')
        print '<h1>web.py xml-rpc server</h1>'
        print 'This server provides the following methods:<ul>'
        for n in dispatcher.system_listMethods():
            print '<li><b>%s:</b> %s' % (n, dispatcher.system_methodHelp(n))
        print '</ul>'

# xml-rpc service
class xmlrpc:
    def POST(self):
        print dispatcher._marshaled_dispatch(web.data())

# metaclass to make all server methods run in "safe mode"
class Safe:
    def __init__(self):
        for name in dir(self):
            obj = getattr(self, name)
            if ismethod(obj) and name != '__init__':
                setattr(self, name, self.__decorator(obj))

    def __decorator(self, meth):
        def call(*args):
            try: return meth(*args)
            except Exception, e: return 'error: %s' % e
        # keep the doc string!
        call.__doc__ = meth.__doc__ or 'no help'
        return call

# methods on this class will be provided by this server
class ServerMethods(Safe):
    def sort(self, data):
        "sort(list): return the sorted version of that list"
        data.sort()
        return data

    def multiply(self, a, b):
        "multiply(a, b): return a*b"
        return a * b

if __name__ == '__main__':
    dispatcher.register_instance(ServerMethods())
    web.run(urls, globals())

Mais fácil, rápido, prático e extensível que isso, desconheço.

Anúncios

4 Comentários on “xml-rpc server com web.py”

  1. Com qual extensão eu salvo o código do servidor.

  2. Mario Jorge disse:

    ele dá erro no web.run()
    diz: AttributeError: ‘module’ object has no attribute ‘run’

    alguma ideia do que pode ser?


Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s