Introdução: Por que Qualidade de Código Importa
Quando você começa a programar em Python, é fácil focar apenas em fazer o código funcionar. Porém, código que funciona e código de qualidade são duas coisas diferentes. Imagine trabalhar em um projeto onde cada desenvolvedor escreve com um estilo diferente: alguns usam aspas duplas, outros simples; alguns colocam dois espaços antes de uma função, outros colocam quatro; as importações aparecem em qualquer ordem. Esse projeto vira um caos de manutenção.
Aqui entram Ruff, Black e isort — três ferramentas que automatizam a garantia de qualidade e consistência do seu código Python. Elas trabalham juntas para eliminar discussões sobre estilo em code reviews e deixar você focado no que realmente importa: a lógica do programa. Neste artigo, você aprenderá como configurar e usar essas três ferramentas de forma integrada e profissional.
Ruff: O Linter Ultrarrápido
O que é um Linter?
Um linter é uma ferramenta que analisa seu código em busca de erros, problemas de estilo e práticas ruins — sem executar o código. Ela encontra coisas como variáveis não usadas, imports desnecessários, nomes que violam convenções e muitos outros padrões. O Ruff é um linter Python extremamente rápido, escrito em Rust, que substituiu com vantagem ferramentas como Flake8 em muitos projetos modernos.
Instalação e Configuração Básica
Instale o Ruff com pip:
pip install ruff
Agora crie um arquivo Python com alguns problemas propositais para entender como o Ruff funciona:
# exemplo.py
import os
import sys
import json
def funcao_com_problema( ):
x = 5
y = 10
return x
resultado = funcao_com_problema()
print(resultado)
Execute o Ruff para ver os problemas detectados:
ruff check exemplo.py
A saída será algo como:
exemplo.py:2:1: F401 [*] `sys` imported but unused
exemplo.py:3:1: F401 [*] `json` imported but unused
exemplo.py:6:1: ARG001 [*] Unused argument `y`
O Ruff identificou imports não utilizados e uma variável não usada. Muitos desses problemas podem ser corrigidos automaticamente com a flag --fix:
ruff check exemplo.py --fix
Após executar esse comando, seu arquivo será ajustado automaticamente.
Configuração via pyproject.toml
Para configurar o comportamento do Ruff em seus projetos, crie ou edite o arquivo pyproject.toml na raiz do seu projeto:
[tool.ruff]
line-length = 100
target-version = "py38"
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # Pyflakes
"I", # isort
"N", # pep8-naming
"UP", # pyupgrade
]
ignore = ["E501"] # ignorar linhas muito longas (Black cuida disso)
[tool.ruff.per-file-ignores]
"__init__.py" = ["F401"] # permitir imports não usadas em __init__.py
Essa configuração diz ao Ruff para usar um comprimento de linha de 100 caracteres, visar Python 3.8+, e aplicar várias categorias de verificação. Você pode ser tão restritivo ou permissivo quanto quiser ajustando essas opções.
Black: Formatador Opinado
Filosofia do Black
Black é um formatador de código Python que resolve debates sobre estilo de uma vez por todas: ele impõe um único estilo — o estilo do Black. Você não discute mais sobre aspas simples vs. duplas ou quantos espaços antes de uma função. Black decide para você, baseado em princípios sólidos. Isso libera tempo em code reviews para discutir coisas que realmente importam.
Instalação e Uso Básico
Instale o Black:
pip install black
Considere este código desformatado:
# desformatado.py
def funcao(a,b,c):
resultado=a+b+c
if resultado>100:
print( "Grande demais" )
else:
print("Ok")
return resultado
x={'nome':'João','idade':30,'cidade':'São Paulo'}
Execute o Black:
black desformatado.py
O arquivo será reescrito assim:
# desformatado.py
def funcao(a, b, c):
resultado = a + b + c
if resultado > 100:
print("Grande demais")
else:
print("Ok")
return resultado
x = {"nome": "João", "idade": 30, "cidade": "São Paulo"}
Repare que Black adicionou espaços em volta de operadores, separou funções com duas linhas em branco, ajustou o espaçamento dentro de parênteses e mudou aspas simples para duplas (a preferência do Black).
Configuração e Compatibilidade
Configure o Black no mesmo pyproject.toml:
[tool.black]
line-length = 100
target-version = ['py38']
include = '\.pyi?$'
exclude = '''
/(
\.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
)/
'''
Isso garante que Black e Ruff trabalhem com as mesmas configurações de comprimento de linha. É crucial manter a linha-length consistente entre as três ferramentas.
Black em Modo Check
Se você quiser verificar se o código estaria formatado corretamente sem fazer mudanças (útil em CI/CD), use:
black --check exemplo.py
isort: Organizando Imports
Por que Imports Precisam de Organização?
Python permite imports em qualquer ordem, mas é considerada boa prática organizá-los: primeiro biblioteca padrão, depois third-party, depois imports locais. Além disso, imports na mesma categoria devem estar em ordem alfabética. O isort automatiza essa tarefa.
Instalação e Funcionamento
Instale o isort:
pip install isort
Veja este exemplo com imports desorganizados:
# imports_desordenados.py
from django.shortcuts import render
import sys
from myapp.models import User
import os
from requests import get
import json
from myapp.utils import helper
Execute o isort:
isort imports_desordenados.py
O resultado será:
# imports_desordenados.py
import json
import os
import sys
from typing import Optional
import requests
from django.shortcuts import render
from requests import get
from myapp.models import User
from myapp.utils import helper
O isort organizou os imports em três grupos: biblioteca padrão (json, os, sys), third-party (django, requests), e local (myapp). Dentro de cada grupo, estão alfabeticamente ordenados.
Configuração para Compatibilidade com Black
É essencial configurar o isort para ser compatível com Black. Adicione ao pyproject.toml:
[tool.isort]
profile = "black"
line_length = 100
multi_line_mode = 3
include_trailing_comma = true
force_grid_wrap = 0
use_parentheses = true
ensure_newline_before_comments = true
A linha profile = "black" é crucial — ela faz o isort seguir o comportamento esperado pelo Black, evitando conflitos entre as ferramentas.
Integração Completa das Três Ferramentas
Configuração Unificada no pyproject.toml
Aqui está uma configuração profissional e completa que integra as três ferramentas:
[build-system]
requires = ["setuptools>=45", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "meu-projeto"
version = "0.1.0"
[tool.ruff]
line-length = 100
target-version = "py38"
select = ["E", "W", "F", "I", "N", "UP"]
ignore = ["E501"]
[tool.black]
line-length = 100
target-version = ["py38"]
[tool.isort]
profile = "black"
line_length = 100
Workflow Prático: Exemplo Completo
Imagine um projeto real com múltiplos arquivos. Aqui está um workflow típico:
# 1. Primeiro, execute isort para organizar imports
isort .
# 2. Depois, execute Black para formatar o código
black .
# 3. Por fim, execute Ruff para verificar erros lógicos
ruff check .
Essa é a ordem recomendada porque isort organiza imports, Black formata o estilo geral, e Ruff faz verificações mais profundas.
Exemplo Real: Antes e Depois
Arquivo original com vários problemas:
# main.py
from datetime import datetime
import requests
import os
from myapp.config import DATABASE_URL
import json
from requests import get
def processar_dados(dados,debug=False):
url='https://api.example.com/dados'
response=get(url)
if response.status_code==200:
resultado=response.json()
return resultado
else:
return None
unused_var = 42
print(processar_dados({'teste':'valor'}))
Após executar isort .:
# main.py
import json
import os
from datetime import datetime
import requests
from requests import get
from myapp.config import DATABASE_URL
def processar_dados(dados,debug=False):
...
Após executar black .:
# main.py
import json
import os
from datetime import datetime
import requests
from requests import get
from myapp.config import DATABASE_URL
def processar_dados(dados, debug=False):
url = "https://api.example.com/dados"
response = get(url)
if response.status_code == 200:
resultado = response.json()
return resultado
else:
return None
unused_var = 42
print(processar_dados({"teste": "valor"}))
Após executar ruff check . --fix:
# main.py
import json
import os
from datetime import datetime
import requests
from requests import get
from myapp.config import DATABASE_URL
def processar_dados(dados, debug=False):
url = "https://api.example.com/dados"
response = get(url)
if response.status_code == 200:
resultado = response.json()
return resultado
return None
print(processar_dados({"teste": "valor"}))
Observe que Ruff removeu a variável não usada unused_var e simplificou a lógica do if/else redundante.
Integração com Git Hooks (Pre-commit)
Para garantir que ninguém faça commit com código ruim, use a ferramenta pre-commit:
pip install pre-commit
Crie um arquivo .pre-commit-config.yaml na raiz do seu projeto:
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.6
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/psf/black
rev: 23.10.0
hooks:
- id: black
language_version: python3.8
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
hooks:
- id: isort
args: [--profile=black]
Instale os hooks:
pre-commit install
Agora, toda vez que alguém tentar fazer commit, essas ferramentas rodarão automaticamente e bloquearão o commit se houver problemas.
Conclusão
Você aprendeu que Ruff, Black e isort são ferramentas complementares que devem trabalhar juntas. Ruff verifica qualidade e erros lógicos, Black padroniza o estilo visual, e isort organiza imports. A configuração unificada no pyproject.toml com line-length = 100 garante que não haja conflitos. Integrando essas ferramentas com pre-commit hooks, você automatiza completamente a garantia de qualidade, liberando tempo em code reviews para discussões que realmente agregam valor — não para discutir se usar aspas simples ou duplas.