17 de março de 2019

Utilzando Celery junto com o SQS

Uma das formas de lidar com processamento assíncrono no Python (e diversas outras linguagens) sem a necessidade de usar threads e afins é utilizando filas e tasks (que são aplicações que rodam conforme demanda, em um processamento similar a batches).

Para isto o Python dispõe de uma biblioteca chamada Celery, que de uma maneira bem simples permite realizar o processamento assíncrono. Explicando de uma maneira simples, ao invocar uma função, ao invés de executar seu código, o Celery pega os parâmetros e mais algumas metainformações da chamada e coloca numa fila, enquanto isso, em outro lugar (processo, computador ou continente), o Celery captura as mensagens desta fila e dispara as tasks. Aqui está uma implementação básica de uma task que soma dois valores:


Primeiro criamos a instância app Celery e dizemos qual módulo está a configuração. Perceba que a função foi anotada com o decorator @app.task(queue='test-celery), que já faz a função ser gerenciada pelo Celery, o parâmetro queue serve para indicar qual a fila que deverá ser utilizada para isto.

A configuração também é simples:


Nele configuramos o broker_url (com ID e secret do usuário com acesso programático ao SQS), que indica qual broker (gestor de fila) utilizaremos (no nosso exemplo usamos o SQS, porém o Celery dá melhor suporte e com mais funcionalidades no RabbitMQ e Redis). Também configuramos as filas que utilizaremos em task_queues, senão será usada a fila padrão (celery). 

Após isso podemos executar o Celery:

celery -A tasks worker --loglevel=info

E assim podemos invocar a função (em outro terminal ou até mesmo em outro computador com o mesmo projeto), porém chamamos ela através do método .delay(parâmetros):

Assim a função será invocada através do Celery.

Caso queira baixar este código de uma vez só e instalar suas dependências, confira aqui no Github.

Obs: O CacheLock que você viu ajuda a não ter a possibilidade uma mesma mensagem ser consumida por duas tasks ao mesmo tempo.

24 de fevereiro de 2019

Usando terminal e programando em Python no Android


O terminal é uma das ferramentas mais poderosas que temos ao manipular informações no computador, desde o desenvolvimento de software até o hacking, passando por manipulação de texto, administração de sistemas e também para algumas brincadeiras. O acesso ao terminal é simples quando você está dentro de um Linux no computador (e até no Windows agora), porém nem sempre temos um notebook por perto, que por mais que seja portátil, não é simples de carregar por ai. Então, nas pesquisas pela internet, buscando uma alternativa para o tablet e smartphone, eu encontrei o Termux.

O Termux emula um terminal dentro do seu Android, porém ao contrário de alguns aplicativo que apenas emulam alguns comandos, ele tem um sistema completo de terminal, ainda permitindo instalar pacotes de maneira similar ao Debian:

Instalando um pacote do sistema com o comando apk install

Aproveitando todo esse poder, também instalei o Python 3.7.2 e diversas dependências de bibliotecas de sistema (openssl-dev, libffi-dev e etc) junto com o Git e clonei um projeto do Github. A aplicação funcionou normalmente, sendo um bot para Telegram que faz scrap da API do Reddit, usando as libs telegram-bot, requests e click, além de outras dependências como pytest, pytest-cov e muito mais. Você pode conferir o projeto aqui.

Rodando os testes junto com a cobertura

Uma das limitações que percebi no Termux, que na verdade é uma limitação do Android, é que como ele não permite a criação de links simbólicos no sistema e a definição de executáveis (o que acredito que ser fácil de superar quando o aparelho está "rooteado"), você não pode criar virtualenv's dentro do sistema, o que pode ser facilmente ser superado pelo fato de você criar suas virtualenv's na home do Termux.

22 de janeiro de 2019

Construindo um interpretador de Lisp em Python

Ultimamente tenho me interessado cada vez mais em como linguagens de programação funcionam e encontrei um material bem legal, simples e prático para ajudar nos estudos. É um artigo do Peter Norvig sobre como criar um interpretador de Scheme em Python, confira: http://norvig.com/lispy.html

11 de janeiro de 2019

Git Squash: Agregando seus commits em um só!

Durante o processo de desenvolvimento de software é comum que ao criarmos mais código, possamos enviar ele para o repositório de código, assim os colegas podem acompanhar o que está sendo alterado e também você criar um backup de suas informação.

Mas surge uma dúvida na cabeça do programador: qual é o mínimo de código que deve criar antes de realizar um commit? Uma linha? Duas? Cem? Fica complexo definir isso, pois se criarmos muitos commits a nossa funcionalidade nova não fica atômica, mas se demorarmos para commitar e enviar para o repositório, corremos o risco de criar um commit grande demais para review, além é claro do risco de perder o código local. 

Para ajudar nisso, o git possui uma funcionalidade muito interessante, o git squash, que permite que você agrupe seus commits já realizados, podendo assim criar commits mais organizados.

Vamos supor que você criou seu arquivo de teste unitário, comitou esta alteração e deseja já deixa-la no repositório remoto (na sua branch específica, é claro) como backup:

git add test_nova_funcionalidade.py
git commit -m "Teste unitário da nova funcionalidade"
git push origin nova_funcionalidade

Despois de criar os testes, obviamente você irá criar a funcionalidade:

git add nova_funcionalidade.py
git commit -m "Nova funcionalidade implementada"
git push origin nova_funcionalidade

Além disso, você descobriu que precisa ajustar um outro arquivo já existente, para dar suporte a sua alteração, no nosso caso, vamos ajustar nosso README.md:

git add README.md
git commit -m "Alterado o README.md"
git push origin nova_funcionalidade

Agora tudo está concluído, vamos ver como as coisas ficaram lá no Github:


Perceba que existem três commits, porém como a nossa funcionalidade é única, queremos criar algo atômico para agruapar estas alterações e neste ponto entra o git squash.

Iremos agrupar os commits reescrevendo o histórico de nossos últimos 3 commits, para isso precisaremos usar o comando:

git rebase -i HEAD~3

Nisto será aberto uma tela listando nossos últimos três commits:
pick b755797 Teste unitário da nova funcionalidade
pick 8a7064e Nova funcionalidade implementada
pick fdc8f9b Alterado o README.md
# Rebase 8c0eaf4..fdc8f9b onto 8c0eaf4 (3 commands)
Iremos deixar apenas o primeiro da lista como está e iremos alterar o pick para squash:

pick b755797 Teste unitário da nova funcionalidade
squash 8a7064e Nova funcionalidade implementada
squash fdc8f9b Alterado o README.md

Assim salvamos a nossa alteração e abrirá uma nova tela onde podemos digitar uma nova mensagem (esta já virá com todas as mensagens dos commits anteriores, que podem ser apagados). Colocarei uma mensagem que resume tudo o que foi feito:

Criada nova funcionalidade
- Criado teste unitário
- Criada funcionalidade
- Alterado README.md

Agora salvamos e temos o nosso novo commit:

[detached HEAD 1543120] Criada nova funcionalidade
 Date: Sun Jan 6 22:09:46 2019 -0200
 3 files changed, 3 insertions(+), 1 deletion(-)
 create mode 100644 nova_funcionalidade.py
 create mode 100644 test_nova_funcionalidade.py
Successfully rebased and updated refs/heads/nova_funcionalidade.

Podemos fazer o push forçado (pois será necessário reescrever o histórico):

git push -f origin nova_funcionalidade

E agora ao verificar novamente no Github, vemos apenas um único commit com todas as alterações:


Assim o Pull Request fica bem mais simples, e caso necessário, você pode retornar a versão apenas um único commit.

4 de janeiro de 2019

Desempacotando parâmetros em Python

Uma das coisas mais interessantes do Python é como ele interage com as suas estruturas de dados padrão, sendo um dos exemplos mais interessantes o desempacotamento de listas e dicionários. A situação é simples, vamos supor que você tenha uma lista de parâmetros e deseja passar para uma função, podemos testar a seguinte situação:

Mas temos o seguinte erro:

TypeError: funcao() missing 2 required positional arguments: 'parametro2' and 'parametro3'

Isso ocorre pois a lista é enviada para o parametro1, deixando os outros parâmetros vazios. Para resolvermos isso o Python nos fornece uma sintaxe que permite desempacotar os valores para enviar como parâmetro, utilzando * e ** (este último para dicionários).

Muitas vezes você irá ver por ai a seguinte sintaxe:

Se você quiser enviar os parâmetros de maneira nomeada (para poder trocar a ordem deles), você pode utilizar um dicionário para desempacotar:

Esta solução é muito útil para diversas situações, como deixar a chamada de uma função com muitos parâmetros mais limpa, construir os parâmetros no decorrer do código para só chamar ao final e até criar dinâmicamente os parâmetros:



6 de julho de 2018

Rodando automáticamente o Flake8 antes de todos os commits do Git

Quando programamos em Python é importante verificarmos se o nosso código está de acordo com a PEP 8, seguindo seu guia de estilos. Para isto muitas vezes rodamos o Flake8, que é uma aplicação que analisa o nosso código e avalia o que está seguindo e não está seguindo as boas práticas da PEP 8.

Para instalar o Flake8 é muito simples, basta rodarmos o comando: 

pip install flake8

Após instalarmos para podermos avaliar nosso código no diretório atual basta executarmos o comando:

flake8 .

e ele retornará com as melhorias a serem feitas no código.

Facilitando mais ainda nosso fluxo de trabalho, é possível também através dos hooks do Git executar o flake8 sempre antes de cada commit, e caso apresente algum erro ele não deixará o commit prosseguir sem realizar as correções necessárias: Para instalar o hook primeiro rode o seguinte comando:

flake8 --install-hook git

Depois é necessário ativa-lo rodando o simples comando:

git config --bool flake8.strict true

Assim, sempre antes de cada commit ele irá executar o Flake8 avaliando o código.