Python

Empacotando e publicando pacotes Python no PyPi

Neste artigo iremos aprender como funciona o processo de empacotar e distribuir nossos próprios pacotes Python no PyPi.

há 1 ano 7 meses

Formação Desenvolvedor Python
Conheça a formação em detalhes

O Python é uma linguagem de programação bastante popular e robusta, que possui diversos pacotes para nos ajudar nos mais diferentes tipos de tarefas, não atoa o Python é conhecido como uma linguagem que possui baterias inclusas. Porém, nem sempre o Python irá possuir um pacote pronto para realizar as tarefas que precisamos e isso é facilmente solucionado com a possibilidade de instalarmos novos pacotes com o pip (package installer for Python).

Mas, você já se perguntou como podemos criar e disponibilizar os nossos próprios pacotes? Pois nesse artigo iremos falar exatamente sobre isso, como podemos empacotar e publicar nossos próprios pacotes no PyPi (Python Package Index).

Python - Fundamentos
Curso Python - Fundamentos
Conhecer o curso

Projeto Base

Primeiramente precisamos de algum código Python que queremos disponibilizar para a comunidade, para não termos que perder tempo desenvolvendo um projeto do zero irei disponibilizar a base do projeto pronta para podermos focar apenas nas partes necessárias para o processo de empacotamento e publicação.

Baixando o projeto base

O projeto que iremos utilizar é o twblog, uma simples CLI (command line interface) que consegue listar e detalhar os últimos artigos publicados no blog da TreinaWeb. Para baixar o projeto base basta acessar o repositório treinaweb-artigo-publicacao-pacotes-python disponível no GitHub da TreinaWeb e realizar o download da release intitulada “Projeto base”.

Print da página do GitHub mostrando a release intitulada projeto base do pacote twblog-reader

Uma vez que tenha realizado o download do arquivo de código-fonte no formato zip ou tar.gz basta descompactá-lo e então abrir o projeto no editor de código ou IDE de sua preferência.

Preparando o ambiente

Python - Orientação a objetos
Curso Python - Orientação a objetos
Conhecer o curso

Agora que já baixamos o projeto base e estamos com o mesmo aberto no nosso editor de código ou IDE, nós precisamos criar e ativar uma virtualenv para podermos instalar as dependências do projeto de maneira isolada.

Para criar e ativar a virtualenv vamos executar os seguintes comandos no terminal:

  • No Linxu e MacOS
python3 -m venv .venv
source .venv/bin/activate
  • No Windows
python -m venv .venv
.venv\Scripts\activate

Agora que estamos com a virtualenv criada e ativada precisamos instalar as dependências do projeto, para isso executamos o seguinte comando:

pip install -r requirements.txt

Como isso já temos o necessário para que o projeto funcione em nossa máquina, mas para termos certeza, vamos testá-lo. Esse projeto possui duas funcionalidades, a primeira é a de listar os últimos artigos publicados no blog e a segunda é a de exibir a texto de um dos artigos listados na funcionalidade anterior.

Para executar a primeira funcionalidade podemos executar o comando python -m twblog_reader, após a execução deste comando será exibido uma lista dos últimos 16 artigos publicado no blog. Agora vamos testar a funcionalidade de exibir o texto de um dos artigos com o comando python -m twblog_reader 0, onde 0 é o número do artigo com base na listagem do comando anterior, após a execução do comando será exibido no terminal o texto do artigo em questão em formato Markdown.

Preparando o pacote para publicação

Agora que já conhecemos o projeto que iremos empacotar e publicar no PyPi e que também garantimos que nosso ambiente está devidamente configurado e o projeto está funcionando. Nós iremos preparar o nosso projeto para que ele contenha os arquivos e configurações necessárias para que o mesmo passe pelo processo de empacotamento.

Adicionando a licença

Se estamos disponibilizando um pacote para que outras pessoas possam utilizar, é essencial adicionar uma licença que defina de forma clara o que outras pessoas podem fazer com o nosso pacote. Uma licença é um documento legal, geralmente nós não escrevemos esse documento, ao invés disso escolhemos uma licença já existente e então a utilizamos.

Para ajudar nesse processo de escolha de qual licença utilizar recomendo o site choosealicense.com, esse site consegue indicar uma licença open source com base nas suas necessidades.

Para esse projeto eu quero uma licença que seja simples e bastante permissiva, ou seja, que possibilite outras pessoas de replicarem, modificarem e até mesmo distribuírem o projeto sem muitas restrições. Nesse contexto o choosealicense.com recomenda a licença do MIT, então será ela que iremos utilizar.

Para adicionarmos uma licença em nosso projeto basta que criemos um arquivo chamado LICENSE e dentro deste arquivo iremos colocar o seguinte conteúdo:

MIT License

Copyright (c) [year] [fullname]

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Lembre-se de alterar [year] pelo ano atual e [fullname] pelo seu nome.

Após a adição do arquivo LICENSE teremos a seguinte estrutura de arquivos e pastas:

.
├── LICENSE
├── requirements.txt
└── twblog_reader
    ├── __init__.py
    ├── __main__.py
    ├── entities.py
    ├── feed.py
    └── ui.py

Criando o README.md

Python - SQLAlchemy ORM
Curso Python - SQLAlchemy ORM
Conhecer o curso

Com a licença adicionada nós já informamos o que outras podem fazer com o nosso pacote, mas também é necessário informar para essas pessoas sobre o quê nosso pacote se trata, quais funcionalidades o mesmo possui, ou seja, precisamos de uma documentação básica do nosso pacote.

Essa documentação básica pode ser criada através de um arquivo chamado README.md, esse é um arquivo em formato markdown onde iremos colocar informações importantes para as pessoas que irão utilizar o nosso projeto, podemos informar, por exemplo, quais as funcionalidades disponíveis a partir da instalação do nosso pacote.

Então, vamos criar um arquivo README.md com o seguinte conteúdo:

# TWBlog Reader

Esse é uma simples CLI (Command Line Interface) para ler os artigos do blog da [TreinaWeb](https://treinaweb.com.br/blog). Esse projeto foi desenvolvido para o artigo [Empacotando e publicando pacotes Python](https://treinaweb.com.br/blog/empacotando-e-publicando-pacotes-python/).

## Instalação

Para instalar o pacote, basta executar o comando abaixo:

<pre><code>pip install twblog</code></pre>

## Uso

Após a instalação do pacote será disponibilizado o comando `twblog` na linha de comando. Para ver a lista do dezesseis últimos artigos, basta executar o comando abaixo:

<pre><code>twblog</code></pre>

A saída será parecida com a abaixo:

<pre><code>Últimos artigos do blog da TreinaWeb:

0 - Desenvolvedor mobile: por que investir nessa carreira?
1 - ASP.NET - Conhecendo a Minimal API
2 - Como se preparar para entrevistas técnicas
3 - Seletores avançados do CSS
4 - Criando o primeiro CRUD com NestJS
5 - Seletores Básicos do CSS
6 - Orientação a objetos em Dart
7 - 5 dicas para entrevistas na área de TI
8 - Novos recursos do ECMAScript 2022
9 - O que preciso conhecer antes de iniciar estudos em uma linguagem de programação?
10 - Criando o primeiro CRUD com FastAPI
11 - Unidades de medidas no CSS
12 - Orientação a objetos no C#
13 - Guia da linguagem C#
14 - Estruturas condicionais e de repetição no C#
15 - Conhecendo variáveis e constantes no C#</code></pre>

Para ver o conteúdo de um artigo específico, basta executar o comando abaixo:

<pre><code>twblog [id]</code></pre>

Onde `[id]` é o identificador do artigo que você deseja ver. Por exemplo, para ver o artigo de identificador 5, basta executar o comando abaixo:

<pre><code>twblog 5</code></pre>

## Licença

Esse projeto está sob a licença MIT. Veja o arquivo [LICENSE](LICENSE) para mais detalhes.

Veja que nesse exemplo de README.md temos informações sobre do que se trata o projeto, como ele pode ser instalado e também como ele pode ser utilizado. Além dessas informações, é comum haver outras seções com informações de como outras pessoas podem contribuir com o projeto, quem são os contribuidores e mantenedores do projeto, entre outras informações.

Após a adição do README.md a nossa estrutura de pastas e arquivos ficará da seguinte maneira:

.
├── LICENSE
├── README.md
├── requirements.txt
└── twblog_reader
    ├── __init__.py
    ├── __main__.py
    ├── entities.py
    ├── feed.py
    └── ui.py

Configurando o build do pacote

Python -  Arquivos e I/O
Curso Python - Arquivos e I/O
Conhecer o curso

Agora nós precisamos configurar o nosso pacote para publicação, nessa configuração nós precisamos informar dois tipos de informação:

  1. Configuração do sistema de build;
  2. Configuração do pacote.

O sistema de build é responsável por criar os arquivos que de fato serão enviados para o PyPi, geralmente esses arquivos estão no formato wheel ou no formato source distribution (sdist). Por um bom tempo esse processo era realizado a partir da criação um arquivo chamado setup.py, porém a partir da criação da PEP 517 e da PEP 518 essa configuração é feita a partir de um arquivo chamado pyproject.toml, além disso, a partir da criação dessas PEP’s também se tornou possível utilizar outros sistemas de build para realizar o empacotamento de projetos Python além do distutils e do setuptools.

Inicialmente iremos colocar no arquivo pyproject.toml informações que digam para os sistemas de build “frontend” como pip e build qual o sistema de build “backend” deve ser utilizado para criar os arquivos de distribuição do nosso pacote. Nesse artigo iremos utilizar como sistema de build “backend” o setuptools, porém é possível escolher outros sistemas de build como Hatchling, Flit, PDM, Poetry, entre outros.

Vamos criar o arquivo pyproject.toml e inicialmente iremos colocar o seguinte conteúdo:

[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

Esse arquivo tem o formato TOML e incialmente criamos a seção [build-system] que informa qual o “backend” de build a ser utilizado, no nosso caso estamos utilizando o setuptools na versão 61.0 ou superior, nessa seção temos as seguintes configurações:

  • requires: é uma lista de pacotes necessários para realizar o build do pacote. Nós não precisamos realizar a instalação desses pacotes, os sistemas de build “frontend” como o pip se encarregam realizar a instalação desses pacotes de maneira temporária e em um ambiente virtual isolado durante o processo de build;
  • build-backend: o nome do objeto Python que o sistema de build “frontend” irá utilizar para realizar o build.

Configurando os metadados do projeto

Agora que já temos a configuração de como irá funcionar o processo de build, nós também precisamos informar os metadados do projeto, os metadados são informações sobre o nosso projeto, como o nome, descrição, autores, dependências e entre outras informações.

Os metadados do projeto também são informados no arquivo pyproject.toml, então vamos abrir novamente esse arquivo adicionar o seguinte conteúdo logo abaixo do que já existe:

[project]
name = "twblog"
version = "0.1.0"
description = "Um simples leitor de artigos do blog da TreinaWeb"
readme = "README.md"
authors = [
    { name = "Cleyson Lima", email = "cleyson.lima@treinaweb.com.br" }
]
license = { file = "LICENSE" }
classifiers = [
    "Environment :: Console",
    "License :: OSI Approved :: MIT License",
    "Natural Language :: Portuguese (Brazilian)",
    "Operating System :: OS Independent",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.9",
    "Programming Language :: Python :: 3.10",
    "Topic :: Utilities",
]
keywords = ["twblog", "treinaweb", "blog", "terminal", "cli"]
requires-python = ">=3.9"
dependencies = ["requests>=2.28.1", "html2text>=2020.1.16", "beautifulsoup4>=4.11.1"]

Agora nós temos a seção [project] que contém os metadados do projeto, com as seguintes informações:

  • name: é o nome de distribuição do nosso pacote. Pode ser qualquer nome desde que contenha apenas letras, números, ., _ e -. Também não pode ser um nome já utilizando por outro pacote no PyPi;
  • version: é a versão do pacote. Recomendo a leitura da PEP 440 que define o esquema de identificação de versões de projetos e pacotes Python. Também recomendo a leitura da especificação Semantic Version;
  • description: é um pequeno resumo de apenas uma única sentença sobre o pacote;
  • authors: é usado para identificar as pessoas autoras do pacote, basicamente é uma lista de objetos onde passamos o nome e e-mail de cada uma das pessoas autoras;
  • readme: é o caminho de onde está localizado o arquivo que contem a descrição detalhada de nosso pacote;
  • license: é o caminho do arquivo de licença;
  • classifiers: é uma forma de passar informações adicionais para o PyPi realizar uma melhor categorização do nosso pacote. Aqui devemos pelo menos informar quais as versões do Python que são suportadas, qual a licença utilizada e quais sistemas operacionais suportados. Caso queira ver a lista completa do classifiers disponíveis acesse https://pypi.org/classifiers/;
  • keywords: aqui informamos palavras-chave para ajudar na indexação do nosso pacote no PyPi, dessa forma fica mais fácil de outras pessoas conseguirem encontrar o nosso pacote com um simples busca;
  • requires-python: é a versão do Python suportado pelo pacote, ou seja, o nosso pacote só pode ser utilizado em instalações do Python iguais ou superior à versão 3.9;
  • dependecies: aqui informamos quais pacotes necessitam ser instalados em conjunto para que o nosso pacote possa funcionar corretamente. Quando alguém realizar a instalação do nosso pacote, o pip irá baixar os pacotes descritos nessa parte de forma automática. Inclusive essa configuração substitui a necessidade de termos um arquivo requirements.txt em nosso projeto.

Importante lembrar que na propriedade name é necessário colocar um nome não utilizado por nenhum projeto no PyPi, logo, caso você queira colocar o projeto na sua conta do PyPi é necessário colocar um valor diferente de twblog na propriedade name do seu projeto, pois esse nome já está utilizado na minha conta do PyPi.

Essas foram apenas alguns metadados que podemos informar sobre o nosso projeto, existem várias outros metadados que podem ser informados, mas esses são os mais utilizados, caso queira saber mais detalhes sobre a seção de metadados recomendo a leitura da especificação sobre metadados de projetos Python.

Criando um comando para nosso pacote

Até o momento nós já temos toda a configuração necessário para dar início aos processos de empacotamento e distribuição do nosso pacote, porém esse pacote será uma CLI, ou seja, assim que ele for instalado queremos que ele adicione um comando executável pelo terminal e para isso precisamos adicionar mais uma seção de configuração ao arquivo pyproject.toml, então iremos adicionar mais esse conteúdo neste arquivo:

[project.scripts]
twblog = "twblog_reader.__main__:main"

Agora nós temos mais uma seção que é a [project.scripts], nessa seção iremos informar quais são os scripts do nosso pacote, basicamente definimos uma chave chamada twblog que será o nome comando disponível após a instalação do pacote e como valor colocamos uma string que aponta para o que deve ser executado. A string "twblog_reader.__main__:main" informa que será executando uma função chamada main que está localizada em um arquivo chamado __main__.py que, está localizado na pasta twblog_reader.

Agora vamos testar se esse comando de fato irá funcionar, para isso podemos realizar a instalação do nosso próprio pacote localmente sem necessidade de antes colocá-lo no PyPi. Para isso podemos executar o seguinte comando no terminal:

pip install -e .

Como o comando acima estamos falando para o pip realizar a instalação dos pacotes definidos na pasta atual, por isso o . ao final do comando. Já a flag -e é para que a instalação do nosso pacote seja realizada de maneira editável, isso faz com que ao realizar alguma modificação no código a versão instalada localmente já será atualizada de maneira automática sem a necessidade de reexecutar o comando.

Após o comando de instalação realizado acima podemos chamar o comando que foi definido no nosso pacote, para testarmos basta executar o comando twblog e veremos que temos o mesmo comportamento de quando executamos o nosso projeto utilizando diretamente o interpretador do Python logo no começo desse artigo, só que agora ele é executando como um comando disponível no terminal.

Gerando os arquivos de distribuição

Agora que já temos tudo devidamente configurado e testado nós precisamos gerar os arquivos de distribuição, esses arquivos é que serão de fato enviados para publicação no PyPi. Para gerar esses arquivos nós iremos utilizar uma ferramenta chamada build.

Então, para gerarmos esses arquivos nós precisamos primeiro realizar a instalação da ferramenta build, para isso execute o seguinte comando:

pip install build

Agora que temos a ferramenta build instalada nós iremos utilizá-la para gerar os arquivos de distribuição, para isso iremos executar o seguinte comando:

python -m build

Veja que após a execução do comando acima houve a criação de pastas e arquivos dentro do nosso projeto, fazendo com que o mesmo fique com a seguinte estrutura de pastas e arquivos:

.
├── LICENSE
├── README.md
├── dist
│   ├── twblog-0.1.0-py3-none-any.whl
│   └── twblog-0.1.0.tar.gz
├── pyproject.toml
├── requirements.txt
├── twblog.egg-info
│   ├── PKG-INFO
│   ├── SOURCES.txt
│   ├── dependency_links.txt
│   ├── entry_points.txt
│   ├── requires.txt
│   └── top_level.txt
└── twblog_reader
    ├── __init__.py
    ├── __main__.py
    ├── entities.py
    ├── feed.py
    └── ui.py

Foram criadas as pastas twblog.egg-info que contém arquivos com informações sobre o nosso projeto, informações essas utilizadas pela própria ferramenta de build que estamos utilizando e também foi criada a pasta dist que contém os arquivos que de fato serão enviados para o PyPi.

Distribuindo nosso pacote no PyPi

Agora que já realizamos o build do projeto e geramos os arquivos para distribuição, podemos devidamente realizar o envio desses arquivos para o PyPi.

Criando uma conta no PyPi

Desenvolvedor Flask Full-Stack
Formação Desenvolvedor Flask Full-Stack
Conhecer a formação

Para podermos realizar o envio do nosso pacote para o PyPi é necessário realizar a criação de uma conta. Para realizar o registro acesse o seguinte link: https://pypi.org/account/register/, preencha todas as suas informações e por fim clique no botão “Create account”.

Print da página de registro de conta no PyPi

Após o processo de criação da sua conta, você receberá um e-mail de confirmação e então basta clicar no link enviado por e-mail para realizar a confirmação da conta. Feito isso a sua conta no PyPi já está criada e então podemos dar prosseguimento com o processo de publicação do nosso pacote.

Publicando o pacote no PyPi

Para realizar a publicação do nosso pacote, ou seja, o envio dos arquivos gerados pela ferramenta build, nós iremos utilizar outra ferramenta chamada twine. Primeiro, precisamos realizar a sua instalação com o comando:

pip install twine

Agora que temos o twine instalado, nós vamos utilizá-lo para realizar o envio do nosso pacote para o PyPi com o seguinte comando:

python -m twine upload dist/*

Após a execução do comando acima o twine irá perguntar qual o seu usuário e senha registrados no PyPi, após informar qual seu usuário e senha irá aparecer no seu terminal uma mensagem como essa:

Uploading twblog-0.1.0-py3-none-any.whl
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.8/12.8 kB • 00:00 • 42.1 MB/s
Uploading twblog-0.1.0.tar.gz
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 10.8/10.8 kB • 00:00 • 42.1 MB/s

View at:
https://pypi.org/project/twblog/0.1.0/

Veja que foi realizado o upload dos dois arquivos contidos na pasta dist e logo em seguida o twine informa qual o link do PyPi que contém o nosso pacote, então vamos acessar esse link e ver se de fato nosso pacote foi publicado no PyPi.

Ao acessar o link https://pypi.org/project/twblog/0.1.0/ veremos a seguinte página:

Print da página de detalhes do projeto twblog no PyPi

Veja que todas as informações definidas nos arquivos LICENSE, README.md e pyproject.toml estão sendo exibidas na página de detalhes do nosso pacote do PyPi.

Utilizando nosso pacote recém-publicado

Clean Code e SOLID - Escrevendo código limpo e escalável
Curso Clean Code e SOLID - Escrevendo código limpo e escalável
Conhecer o curso

Agora que temos o nosso pacote devidamente publicado podemos testar se é possível realizar a sua instalação via pip. Para isso vamos abrir um novo terminal e executar o comando:

  • No Linux e MacOS
sudo pip3 install twblog
  • No Windows
pip install twblog

Após a execução do comando acima o pip irá até o PyPi e então irá realizar o download do nosso pacote e realizar a sua instalação em nosso ambiente, após o processo de instalação podemos executar o comando twblog para executar o nosso pacote e conseguir ler os últimos artigos do blog da TreinaWeb diretamente pelo terminal.

Conclusão

Neste artigo vimos todas as etapas para realizarmos o empacotamento e publicação de nossos próprios pacotes no PyPi, vimos as diferentes ferramentas e conceitos envolvidos em todo esse processo. Pode parecer algo extremamente complexo à primeira vista, mas uma vez que você tenha entendido sobre cada uma dessas etapas e tiver praticado publicando seus próprios pacotes, verá que não é nada tão difícil de ser realizado, eu diria até que é um processo mais trabalhoso do que difícil, principalmente pelo fato de que algumas etapas ainda são feitas de forma manual, como, por exemplo, a criação do arquivo pyproject.toml.

Aqui fica a minha recomendação para que você estude sobre outras ferramentas que tornam todo esse processo mais automatizado e cuidam de todas essas etapas para você, algumas dessas ferramentas são Hatchling, Flit, PDM, e Poetry, sendo o Poetry a que eu particularmente mais gosto de utilizar, inclusive pretendo futuramente trazer um novo artigo só para falar sobre ele.

Caso queira aprender mais sobre o Python, saiba que aqui na TreinaWeb nós temos a formação Desenvolvedor Python que possui 51h03 de vídeo e um total de 354 exercícios.

Veja quais são os cursos que fazem parte desta formação:

  • Python - Fundamentos
  • Python - Orientação a objetos
  • Python - Algoritmos - Parte 1
  • Python - Estrutura de dados - Parte 1
  • Python - Estrutura de dados - Parte 2
  • Python - Collections
  • Projeto de Banco de dados - Fundamentos
  • MySQL - Desenvolvedor
  • Python - Banco de dados com DB API
  • Python - SQLAlchemy ORM
  • Python - Arquivos e I/O
  • Python - Consumindo APIs
  • Python - Fundamentos de Kivy

Autor(a) do artigo

Cleyson Lima
Cleyson Lima

Professor, programador, fã de One Piece e finge saber cozinhar. Cleyson é graduando em Licenciatura em Informática pelo IFPI - Campus Teresina Zona Sul, nos anos de 2019 e 2020 esteve envolvido em vários projetos coordenados pela secretaria municipal de educação da cidade de Teresina, onde o foco era introduzir alunos da rede pública no mundo da programação e robótica. Hoje é instrutor dos cursos de Spring na TreinaWeb, mas diz que seu coração sempre pertencerá ao Python.

Todos os artigos

Artigos relacionados Ver todos