Refatorando um projeto com o Python e Poetry para Twitch
No artigo anterior, eu mostrei como iniciei um projeto de chatbot para a plataforma Twitch, conforme adquiri mais conhecimentos, com ajuda da comunidade, tanto sobre projetos quanto sobre a linguagem Python, consegui refatorar o projeto, mantendo a funcionalidade inicial, porém com mais organização.
Lembrando que o tutorial foi escrito utilizando o sistema operacional Windows, pode haver diferenças nos comandos para outros sistemas.
Identificando o problema
O principal motivo dessa decisão foi que muitas responsabilidades estavam misturadas em um único arquivo, como exemplo, tínhamos conexão com o IRC da Twitch, informações sensíveis de token, abertura e escrita de arquivos, funções de envio de mensagens para o chat, etc. tudo isso no arquivo principal.
Além disso, não tínhamos informações de quais bibliotecas externas e suas versões eram necessárias para o projeto.
Escolhendo as ferramentas
Para resolver a falta de especificação das bibliotecas, temos as principais opções:
- Uso de um arquivo requirements.txt ou setup.py, indicando quais bibliotecas devem ser instaladas e as instalando com um comando simples no terminal:
‘pip install -r requirements’
- Uso de um gerenciador de dependências Poetry, nele podemos declarar quais bibliotecas e versões serão instaladas para o projeto e também gerenciar o ambiente virtual.
Entre elas, escolhemos o Poetry pois ele faz a gerência o ambiente de maneira mais fácil, juntamente faz o build do projeto.
Instalando e configurando o Poetry
Para instalar o Poetry usamos o comando conforme a documentação, através do terminal powershell.
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py -
Finalizando com sucesso, aparecerá a seguinte mensagem:
Para iniciar um projeto Poetry, ainda no powershell, utilizamos o comando poetry init -n na raiz do projeto. O arquivo de configuração pyproject.toml será gerado automaticamente com algumas especificações padrão, esse arquivo é compatível com outros gerenciadores do Python, não só com o Poetry.
No primeiro bloco [tool.poetry] podemos especificar alguns parâmetros básicos como nome do projeto, versão, descrição e autores.
No segundo bloco [tool.poetry.dependencies] especificamos as dependências utilizadas no projeto, aqui adicionaremos em uma nova linha a dependência chamada twitchio = "~1.2.3", o símbolo ~ no início da versão aponta que ela será fixada os dois primeiros dígitos, deixando somente o último, referente à bugfix, o mais atual possível.
Abaixo do bloco de dependências do projeto, vamos adicionar um novo bloco, nomeado da seguinte forma: [tool.poetry.dev-dependencies]. Nele temos as dependências de desenvolvimento do projeto, elas não são necessárias para a execução do chatbot, apenas facilitam o desenvolvimento. A dependência que especificamos aqui é uma biblioteca que nos ajuda a formatar o código em Python gerando alertas quanto à indentação, bibliotecas não utilizadas, etc. Abaixo do bloco, adicionamos a especificação flake8 = "^3.9".
O próximo bloco [build-system] determinamos que estamos usando o Poetry para fazer o build do programa.
Ao final adicionamos mais um bloco [tool.poetry.scripts], nele adicionaremos a especificação chatbot = "chatbot.index:run" que será o comando responsável pela inicialização do chatbot.
Para o funcionamento correto do start precisamos adicionar dois novos arquivos: __init__.py vazio e __main__.py com o seguinte conteúdo:
E no arquivo principal index.py, criamos a função de inicialização logo após os imports:
Após finalizada a configuração do arquivo, adicionando todas dependências necessárias, vamos novamente ao terminal powershell para fazer a instalação com o seguinte comando:
poetry install.
Quando rodamos esse comando, podemos ver no terminal que o Poetry cria um ambiente virtual para fazer a instalação das bibliotecas.
Creating virtualenv chatbot-jMkYLH2J-py3.11 in C:\Users\...Cache\virtualenvs
Desta forma, qualquer coisa que eventualmente aconteça de errado, o ambiente pode ser facilmente excluído, sem a necessidade de desinstalar as bibliotecas.
Finalizando essa instalação, é gerado o arquivo poetry.lock, nele são listadas todas as dependências com suas respectivas versões instaladas. Em caso de fazer a instalação em uma máquina diferente, esse arquivo pode apresentar alguns problemas, podendo ser excluído e criado um novo a partir das especificações do pyproject.toml.
Outro arquivo de configuração que teremos é o .editorconfig, onde definiremos os seguintes parâmetros:
Essas definições são importantes para a estruturação da escrita do código, seguindo um padrão de caracteres, indentação, enconde, espaçamento, entre outros. Desta forma evitamos conflitos de Git, por exemplo a quebra de linha em diferentes sistemas operacionais apresentam-se de formas distintas como \n ou \r\n.
Organizando o projeto
O padrão de projeto Python usa um diretório com o mesmo nome do projeto para colocar os arquivos que de desenvolvimento, deixando os arquivos de configuração e de dados na raiz do projeto.
A partir do projeto anterior, vamos fazer algumas alterações. Inicialmente vamos excluir o diretório venv, pois quem fará o gerenciamento do ambiente virtual agora será o Poetry. Podemos deletar também o diretório __pycache__, ele armazena arquivos bytecode que facilitam a inicialização do chatbot, porém serão criados novos arquivos a partir da organização que faremos.
Vamos organizar os arquivos existentes index.py e musica.py dentro de um novo diretório, criado na raiz do projeto, chamado chatbot e o arquivo first.txt pode ser excluído.
Nessa etapa teremos a seguinte estrutura de diretório e arquivos:
chatbot(raiz)
│ .editorconfig
│ poetry.lock
│ pyproject.toml
│
└───chatbot
│ __init__.py
│ __main__.py
│ index.py
│ musica.py
Separando dados sensíveis
No arquivo principal temos dados sensíveis para inicialização do chatbot, como token, nick e initial_chanels, é importante que essas informações sensíveis não estejam visíveis diretamente no código, então faremos algumas modificações.
Criamos um arquivo chamado config.ini na raiz do projeto, vamos trazer os dados que estão definidos no index.py para cá:
Criando um arquivo config.py dentro do diretório chatbot, montaremos uma estrutura que consiga capturar os dados do config.ini. Neste arquivo também vamos definir constantes para armazenar as informações necessárias.
- ConfigParser: biblioteca responsável pela leitura e escrita em arquivos de configuração;
- Config: instância de uma ConfigParser;
- Config.read: faz a leitura do arquivo de configuração, definido um encoding;
- TOKEN e USERNAME: chaves únicas lidas do arquivo config.ini armazenadas com o tipo de dado string;
- STREAMERS: nomes dos canais lidos do arquivo config.ini e armazenadas separadamente com o tipo de dado lista.
Após configurado, o index.py não terá mais os dados expostos diretamente, passando pela série de etapas acima, podemos fazer o import das constantes e atribuí-las nos parâmetros do construtor da seguinte forma:
Encapsulamento de lógica
Outro ponto de melhoria a ser aplicado é a separação das responsabilidades de cada parte do código. Podemos perceber que toda lógica de processamento está misturada com as funções de chamada dos comandos no arquivo principal.
Comando first
Começando pelo comando first, vamos separar a responsabilidade de manipulação de persistência de dados, deixando a função de chamada do comando responsável apenas por verificar e retornar a posição do usuário.
Criamos um arquivo one_per_live.py no diretório chatbot, com as seguintes especificações:
Vamos analisar esse arquivo.
O shelve, importado no início do arquivo, é uma biblioteca padrão do Python que serve para persistência de dados, com ele podemos fazer manipulações mais facilmente.
Criamos uma classe chamada OnePerLive, com as seguintes funções:
1) __init__ (contrutor): temos os seguintes atributos:
- Self.filename: recebe o nome do arquivo onde os dados vão persistir;
- Self.file: chamamos a função shelve_open passando como parâmetro o nome do arquivo e setando writeback para permitir que sejam escritos novos dados;
- Self._check_struct(): é uma função criada para verificação dos dados contidos no arquivo;
2) _get_date: é responsável por verificar a data atual e o horário definido como virada do dia.
3) _check_struct: verifica e manipula os dados dentro do arquivo, adicionando chaves como ‘date’ e ‘username’
Notem que há um sinal _ antes dessas duas funções. Esse caractere sinaliza que elas poderão ser acessadas apenas dentro da própria classe.
- __len__: é responsável por verificar a quantidade de dados contidos na chave ‘username’ e retornar zero caso tenha virado o dia.
- __contains__: recebe um nome de usuário como parâmetro e retorna se ele está na lista ou não, também fazendo a verificação da virada do dia.
Essas duas funções, assim como o construtor, também contém caracteres especiais antes e depois do seu nome, esse tipo de função é chamado de dunder, ou método mágico, comumente são utilizados para fazer sobrecarga de métodos.
- Add recebe um nome de usuário para ser adicionado no arquivo de persistência, na chave ‘username’.
Finalizando a classe OnePerLive, vamos chamá-la no arquivo principal, no construtor da classe Bot:
Inicialmente faremos o import da classe e criaremos o atributo self.first o qual recebe uma instância da classe, passando como parâmetro o nome do arquivo de persistência de dados first.tmp, assim podemos excluir todo o código de manipulação de texto que existia anteriormente.
Em seguida vamos fazer algumas alterações no comando first, setando o atributo self.first e utilizando os métodos da classe.
Comando craps
A próxima melhoria que podemos fazer é tirar a lógica do jogo craps do arquivo principal, para isso vamos criar um arquivo craps.py, organizando de uma forma mais legível:
A chamada do comando no arquivo principal fica muito mais simplificada, bastando importar a função craps e invocá-la sempre que o comando é chamado:
A estrutura final fica desta forma:
chatbot(raiz)
│ .editorconfig
│ config.ini
│ poetry.lock
│ pyproject.toml
│
└───chatbot
│ __init__.py
│ __main__.py
│ config.py
│ craps.py
│ index.py
│ musica.py
│ one_per_live.py
Com todas alterações feitas, vamos rodar o chatbot abrindo o powershell na pasta raiz do projeto e executando o seguinte comando:
poetry run chatbot
Considerações finais
O trabalho de refatoração de um código serve para melhorarmos a performance, a legibilidade, o design, etc. Nessa versão criamos classes, evitamos a repetição e contamos com o reaproveitamento de código. Com o exemplo da persistência de dados, podemos criar outras instâncias da mesma classe para gerar novas funcionalidades.
Todas as configurações apresentadas aqui servem não somente para o chatbot, elas podem ser aplicadas em qualquer projeto python que necessite de estruturação e organização. Podemos melhorar ainda mais esse projeto versionando o código no GitHub e criando uma documentação com MkDocs.
A Revelo Content Network acolhe todas as raças, etnias, nacionalidades, credos, gêneros, orientações, pontos de vista e ideologias, desde que promovam diversidade, equidade, inclusão e crescimento na carreira dos profissionais de tecnologia.