Paulo Henrique Rodrigues Pinheiro

Um blog sobre programação para programadores!


Mocando um serviço com Bottle

Como minha vida ficou mais fácil com o Bottle ou, uma linda história de Natal

Logo do projeto Bottle

https://bottlepy.org

O problema

Trabalho com um complexo processo de autenticação e autorização, uma solução fornecida via API REST por outro fornecedor.

Sempre que cada desenvolvedor precisa de um dado para teste em algum dos diversos ambientes, temos essa dependência externa na criação dos dados, já que não temos acesso para nós mesmos criarmos.

Há algum tempo imaginei que poderíamos emular esse serviço e vim pensando em algumas possibilidades.

A linda história de Natal

Eis que chega o Natal e o cliente tem um recesso de duas semanas. Estamos com todas as entregas em dia, suporte praticamente zerado, e o Papai Noel nos presenteia com "Escolham uma melhoria em qualquer sistema para trabalhar nesse período".

Eu quase choro de emoção e aproveitando uma outra demanda que tinha acabado de entregar, defendo com uma certa veemência, sem muita esperança, a necessidade de termos um mock desse serviço, para poder executar melhor certos testes.

Para minha surpresa, meu líder inclusive sugeriu que o mock pode ser usado não só para as tarefas básicas de desenvolvimento, mas também para desenvolvimento de experimentos para os quais o fornecedor ainda não realizou entregas mas que podemos ir adiantando nosso lado, além de nos ajudar nas rotinas de suporte.

E assim ganhei esse presentão de Natal que foi poder me dedicar integralmente no final de ano e começo desse, a esse projeto.

A escolha do Bottle

Inicialmente pensei que seria muito fácil, tão fácil, que comecei a prototipar com o módulo http.server do Python. Mas logo me vi mais ocupado com problemas de HTTP do que com os problemas que precisa realmente resolver.

Foi nesse ponto que comecei a pensar em algo que fosse direto e leve. De cara, descartei o Django, framework que usamos. Poderia ser uma boa experiência para usar a versão mais nova, mas também acredito que me focaria mais na ferramenta do que no meu problema. E assim com outras opções REST.

Minha opção pessoal para esses casos sempre foi o CherryPy, mas resolvi dar uma mudada, lembrei que o Bottle tinha certa equivalência e de nunca ter usado. Eis uma oportunidade!

Foco no problema

A documentação do Bottle é muito boa, e desde o começo pude concentrar no meu problema. Conforme ia implementando os endpoints, já conseguia refatorar o código com muita facilidade.

E agora tenho mais uma poderosa ferramenta em meu arsenal. Essa solução que desenvolvi não é um código público, então não tenho essa experiência com ele ainda, mas ao que parece nada impede de usá-lo para pequenas soluções públicas.

Outras ferramentas

Usei também o pytest, não gosto do jeitão amarrado no unittest, apesar de ser um módulo padrão.

E, pela primeira vez, usei para chave/valor temporário em disco, o módulo shelve, muito simples de usar.

E claro, muitas listas e muitos dicionários, como formas de declarar informações de forma simples, um dos grandes poderes do Python.

Show me the code!

Não posso, mas algumas observações.

Plugins

Pode-se escrever plugins e, digamos, alterar alguns comportamentos do Bottle.

Por exemplo, escrevi um plugin que altera o corpo de todas as respostas para JSON, dessa forma só retorno meu dicionário ou lista.

Esse eu mostro pois é basicamente uma adaptação da documentação:


def return_json_response(callback):
    def wrapper(*args, **kwargs):
        body = callback(*args, **kwargs)

        return json.dumps(body)
    return wrapper

bottle.install(return_json_response)

O Bottle já faz normalmente com a configuração autojson, mas eu estava tenho problemas com algumas listas e tive que fazer isso pra poder ir em frente

Outro uso, um plugin que imprime a resposta na tela, e tem-se o JSON formatado junto com a linha de LOG, o que ajuda pacas nos debugs.

Subindo o server

A opção mais importante para o server, nessa aplicação, é o reloader, pois queremos alterar parâmetros de usuários e testar comportamentos:


bottle.run(host='localhost', port=8080, reloader=True)

E, para o problema do JSON, a feature autojson foi desabilitada (deve estar antes de subir o servidor):


app = bottle.default_app()
app.config['autojson'] = False

Os endpoints

Eles devem ser marcados com e devolver suas respostas:


@bottle.route('/api/listagem')
def get_listagem():
    return listagem()

Caso queira retornar algum erro:


bottle.abort(404)

Existem as rotas dinâmicas também:


@bottle.route('/api/usuario/<id_user>')
def get_usuario(id_user):
    return usuario(id_user)

Importante, para post se usa @bottle.post.

Com o objeto request se tem acesso aos enviados via query:


artigo_id = request.query.get('artigoid')

O mais importante

No arquivo principal, basta um


import bottle

Minha vida agora

Agora crio meus usuários, adiciono ou altero as informações que preciso, em minha máquina, e as tenho funcionando no exato momento da necessidade. Próximo passo é subir esse mock para o ambiente compartilhado de dev, e então para stage.

Meus colegas logo estarão mais livres também, e com o projeto passando a ter a contribuição de todos, teremos um baita evolução.