Felipe Sassi
8/2/2022

Poetry: construindo pacotes Python de uma forma fácil

Como construir e disponibilizar no PyPI um pacote Python com o auxílio da ferramenta Poetry
Construir pacotes Python é algo extremamente interessante e ajuda o desenvolvedor a expandir seu horizonte de possibilidades.

A criação de pacotes Python sempre foi algo que me chamou a atenção. Em meu trabalho atual, acabei me tornando um core developer da biblioteca de aprendizado de máquina de uso interno da empresa, além de contribuidor de alguns projetos open-source.

Sendo core developer, comecei a entender melhor como a construção de pacotes é feita e a conhecer diferentes ferramentas que podem auxiliar o desenvolvedor nessa tarefa.

Uma das ferramentas que mais me chamou a atenção foi o Poetry, que torna a construção de pacotes em Python cada vez mais simples. Essa ferramenta gera um ambiente virtual (isolado) para podermos trabalhar com nosso projeto, além de nos ajudar com o controle de versão, gerenciamento de dependências, build e compartilhamento no repositório de pacotes Python PyPI (que você provavelmente conhece).

A ideia deste artigo é criar um pacote Python (para fins educativos, somente) e compartilhá-lo no repositório PyPI, tudo isso com o auxílio da ferramenta Poetry. Todo o código fonte desenvolvido aqui poderá ser acessado no repositório GitHub do projeto:

https://github.com/felipesassi/random-data-gen

Definição do projeto

A ideia aqui é construirmos um pacote que gere tabelas de dados transacionais fictícios. Queremos gerar tabelas contendo o ID único do usuário (consumer_id), data da compra (transaction_created_at) e valor da compra (transaction_payment_value).

O nome do nosso pacote será RandomDataGen (eu não sou muito criativo hehe).

Basicamente, a ideia final é poder importar esse pacote e utilizá-lo da seguinte forma:

Utilizando nosso pacote RandomDataGen.

Como saída, devemos ter algo assim (lembrando que toda a geração de dados será aleatória):

Exemplo de dataframe gerado pelo nosso pacote.


Mas, antes de mais nada, precisamos entender como o Poetry funciona e como ele pode nos ajudar nessa tarefa.

Instalando o Poetry

A instalação do Poetry é muito simples, basta seguirmos os passos apresentados aqui.

Iniciando o uso

Após a instalação, o Poetry estará pronto para ser utilizado. O primeiro passo é criarmos uma pasta chamada random-data-gen e navegarmos até ela com com o terminal. Após isso, devemos rodar o seguinte comando:

Criando um projeto com Poetry.

Este comando cria uma estrutura de pastas e de arquivos no diretório “.” (que no nosso caso deve ser um diretório chamado random-data-gen) com o nome desse diretório sendo utilizado como nome do projeto.

A estrutura de pastas gerada pelo Poetry tem a seguinte forma:

random-data-gen/
│   README.md
│   pyproject.toml

└───random_data_gen/
│   │   __init__.py
│  
└───tests/
   │   __init__.py
   │   test_random_data_gen.py

random-data-gen/
│   README.md
│   pyproject.toml
│
└───random_data_gen/
│   │   __init__.py
│   
└───tests/
    │   __init__.py
    │   test_random_data_gen.py

Nessa estrutura, arquivo README.md deve ser utilizado para descrição do projeto. O arquivo pyproject.toml contém todas as informações do pacote que está sendo desenvolvido e é nele que toda a mágica do Poetry acontece. A pasta /random_data_gen será o diretório utilizado para armazenarmos todo o código do projeto. Por fim, a pasta /tests irá conter todos os testes funcionais presentes no projeto.

Entrando em detalhes, o arquivo pyproject.toml contém as seguintes informações:

Informações encontradas no arquivo pyproject.toml.

Os campos name, version, description e authors são autoexplicativos. Os campos abaixo da seção tool.poetry.dependencies são referentes as dependências utilizadas pelo projeto (que no caso de um projeto vazio será somente a versão do Python desejada).

  • python = “^3.8” indica que a versão do Python utilizada pelo pacote deve ser pelo menos a 3.8 (que é o nosso caso).

Os campos abaixo da seção tool.poetry.dev-dependencies indicam dependências que serão utilizadas somente durante desenvolvimento (que, no nosso caso, é somente a biblioteca pytest, para testes funcionais). Aqui, podemos incluir outras dependências, como black para formatação de código, mypy para verificação de tipos etc.

Um ponto interessante é a distinção entre as seções dependencies e dev-dependencies:

  • tool.poetry.dependencies: essa seção especifica as dependências que serão necessárias para o funcionamento do pacote (e que serão instaladas quando o usuário instalar o pacote via pip install, por exemplo);
  • tool.poetry.dev-dependencies: essa seção especifica as dependências necessárias para construção e manutenção do pacote, e que não serão instaladas quando algum usuário instalar o pacote.

Essa distinção é muito interessante, pois evita que pacotes desnecessários tenham de ser instalados por quem vai utilizar o pacote, além de garantir mais organização na criação do projeto.

Os campos abaixo de build-system são campos padrão e não necessitaremos alterar durante esse projeto. Para quem quiser ler mais sobre os arquivos pyproject.toml, irei deixar uma referência ao final do artigo.

Criando nosso pacote

Agora que sabemos um pouco mais como o Poetry funciona, vamos utilizá-lo para construir nosso pacote. O primeiro passo é inicializar nosso ambiente virtual com o seguinte comando (passo muito semelhante ao uso do comando conda activate):

Comando utilizado para inicialização do ambiente virtual.

Antes de mais nada, precisamos especificar as dependências que vamos utilizar, que no nosso caso serão duas:

  • pandas: para construção do dataframe que será retornado pelo nosso módulo;
  • numpy: para geração de dados numéricos utilizados pelo pacote.

A instalação desses pacotes dentro do nosso ambiente virtual é muito simples, precisamos apenas do seguinte comando:

Instalando os pacotes necessários para o nosso projeto.

Após instalarmos esses pacotes, podemos verificar que as dependências no nosso arquivo pyproject.toml foram alteradas:

Arquivo pyproject.toml após adição das dependências do projeto.

Basicamente essa seção está nos dizendo que:

  • Precisamos da biblioteca pandas com versão igual ou superior a versão 1.4.0;
  • Precisamos da biblioteca numpy com versão igual ou superior a versão 1.22.1.

Desenvolvendo o pacote

Agora que criamos um ambiente virtual e instalamos tudo que é necessário, podemos começar a criação do nosso pacote Python. O primeiro passo é criar os seguintes arquivos e pastas na nossa estrutura inicial do projeto (em negrito para facilitar a leitura):

random-data-gen/
│   README.md
│   pyproject.toml
|   poetry.lock

└───random_data_gen/
│   │   __init__.py
|   |   data_generator.py
|   |
|   └───features/
|       |   transactional_features.py  
|  
└───tests/
   │   __init__.py
   │   test_random_data_gen.py

A pasta /features irá conter o código para geração de cada uma das três colunas presentes na tabela final (código que está dentro do arquivo transactional_features.py). O arquivo data_generator.py irá conter a lógica principal da aplicação, que será responsável por criar o conjunto de dados transacionais.

O intuito não é entrar em detalhes nesses códigos, caso queira saber mais você pode visitar o repositório do projeto. A título de exemplificação, uma parte do código presente no arquivo transactional_features.py é apresentado na figura abaixo:

Exemplo de código presente dentro do arquivo transactional_features.py

Adicionando dependências de desenvolvimento

Com o intuito de padronizar nosso código e verificar erros de tipagem, iremos instalar três pacotes como dependência de desenvolvimento. Essas dependências não serão utilizadas pela versão final do pacote RandomDataGen, sendo instaladas somente quando alguém necessitar contribuir com seu desenvolvimento.

Nesse projeto iremos instalar três dependências:

  • black: responsável pela formatação de código;
  • mypy: responsável pela checagem de tipos (por isso a função apresentada anteriormente tinha os tipos de cada uma das variáveis de entrada e saída definidos);
  • flake8: responsável por garantir a padronização do código segundo o estilo de código definido na PEP8 (irei deixar uma referência sobre isso no final do artigo).

A explicação em detalhes do funcionamento desses três pacotes pode ser vista em suas documentações oficiais, que estarão como referências no final desse artigo.

A instalação de dependências de desenvolvimento com o Poetry é extremamente simples:

Instalando dependências de desenvolvimento com Poetry. A flag — dev indica que essas dependências serão utilizadas para desenvolvimento do pacote.

Depois da instalação, nosso arquivo pyproject.toml ficará da seguinte forma:

Seção de dependências do arquivo pyproject.toml após a instalação do black e do mypy.

Para configurar a ferramenta black, devemos adicionar a seguinte seção no nosso arquivo pyprojet.toml (nessa configuração estamos definindo basicamente a quantidade de caracteres máxima por linha):

Seção de configuração da ferramenta black.

A título de exemplo, as figuras abaixo apresentam o código antes e depois da aplicação da ferramenta black:

Código original, antes da aplicação da ferramenta black.
Código formatado, após a aplicação da ferramenta black.

Podemos ver que a ferramenta inseriu quebras de linha nas linhas que excediam o máximo de caracteres esperado (que no nosso caso foi definido como 100 caracteres por linha).

Publicando o pacote

Após finalizarmos o desenvolvimento do pacote devemos pública-lo no repositório de pacotes Python PyPI. Esse repositório pode ser acessado aqui (para publicar um pacote você precisa se cadastrar na plataforma).

Devemos incluir nosso README.md no cabeçalho de nosso arquivo pyproject.toml para que ele seja utilizado como descrição na página oficial do projeto:

Inserindo o arquivo README.md no nosso arquivo pyproject.toml. Isso garantirá que a página do nosso pacote tenha uma documentação inicial.

Antes de publicarmos o pacote precisamos rodar o seguinte comando:

Comando utilizando para criar os arquivos .whl e .tar.gz que serão enviados para o PYPI.

Esse comando cria os arquivos .whl e .tar.gz dentro de uma pasta /dist, na raiz do nosso projeto.

Após isso, podemos rodar o comando:

Comando utilizado para publicar o pacote no repositório MyPI.

Esse comando irá guiá-lo pelo login no PyPI e após isso enviará o pacote para lá.

Caso você deseje publicar esse mesmo pacote, uma alteração no nome deverá ser realizada, pois o PyPI não permite pacotes com o mesmo nome (o que faz sentido, não é?!).

Agora que publicamos nosso pacote, podemos acessá-lo pelo link:

https://pypi.org/project/random-data-gen/

Conclusão e próximos passos

Nesse artigo vimos como a ferramenta Poetry pode nos ajudar a desenvolver pacotes Python de uma forma rápida e elegante. Passamos desde a instalação da ferramenta até a publicação do pacote no PyPI.

O Poetry abstraiu diversas etapas do desenvolvimento, tornando a tarefa do desenvolvedor mais focada em resolver a lógica do problema/criação do pacote do que nas tarefas secundárias que são necessárias para conclusão dessa tarefa.

Os nossos próximos passos serão:

  • Criar testes funcionais para esse pacote, tornando ele mais robusto a alterações;
  • Entrar em detalhes sobre as bibliotecas black, mypy e flake8;
  • Adicionar um arquivo Makefile para facilitar a contribuição (esse arquivo já existe no repositório do projeto);
  • Criar a documentação do projeto com a ferramenta sphinx;
  • Adicionar etapas de continuous integration (CI) e continuous delivery (CD) para que possamos automatizar todo o processo de checagem de código e disponibilização de novas versões do pacote.

Espero que tenham gostado!

Quer saber mais sobre o assunto ou descobrir como a Datarisk pode te ajudar nessas questões? Acesse o nosso site e leia também nossos outros artigos no blog. Até mais!


Referências

Poetry
poetry show --tree requests-toolbelt 0.8.0 A utility belt for advanced users... └── requests =2.0.1 ├── certifi…

python-poetry.org

The pyproject.toml file
The pyproject.toml file The tool.poetry section of the pyproject.toml file is composed of multiple sections. name The…

python-poetry.org

mypy
Compile-time type checking Static typing makes it easier to find bugs with less debugging. Easier maintenance Type…

mypy-lang.org

GitHub - psf/black: The uncompromising Python code formatter
"Any color you like." Black is the uncompromising Python code formatter. By using it, you agree to cede control over…

github.com

Flake8: Your Tool For Style Guide Enforcement - flake8 4.0.1 documentation
It is very important to install Flake8 on the correct version of Python for your needs. If you want Flake8 to properly…

flake8.pycqa.org

PEP 8 -- Style Guide for Python Code
The official home of the Python Programming Language

www.python.org



Thanks to João Paulo Nogueira and Emanuel Fontelles

Mais artigos da Datarisk

Ver todos os artigos
© Copyright 2017 - 2022 | Datarisk.io | Todos os direitos reservados