<h2>Introdução: Por que Qualidade de Código Importa</h2>
<p>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.</p>
<p>Aqui entram <strong>Ruff</strong>, <strong>Black</strong> e <strong>isort</strong> — 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.</p>
<h2>Ruff: O Linter Ultrarrápido</h2>
<h3>O que é um Linter?</h3>
<p>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 <strong>Ruff</strong> é um linter Python extremamente rápido, escrito em Rust, que substituiu com vantagem ferramentas como Flake8 em muitos projetos modernos.</p>
<h3>Instalação e Configuração Básica</h3>
<p>Instale o Ruff com pip:</p>
<pre><code class="language-bash">pip install ruff</code></pre>
<p>Agora crie um arquivo Python com alguns problemas propositais para entender como o Ruff funciona:</p>
<pre><code class="language-python"># exemplo.py
import os
import sys
import json
def funcao_com_problema( ):
x = 5
y = 10
return x
resultado = funcao_com_problema()
print(resultado)</code></pre>
<p>Execute o Ruff para ver os problemas detectados:</p>
<pre><code class="language-bash">ruff check exemplo.py</code></pre>
<p>A saída será algo como:</p>
<pre><code>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</code></pre>
<p>O Ruff identificou imports não utilizados e uma variável não usada. Muitos desses problemas podem ser corrigidos automaticamente com a flag <code>--fix</code>:</p>
<pre><code class="language-bash">ruff check exemplo.py --fix</code></pre>
<p>Após executar esse comando, seu arquivo será ajustado automaticamente.</p>
<h3>Configuração via pyproject.toml</h3>
<p>Para configurar o comportamento do Ruff em seus projetos, crie ou edite o arquivo <code>pyproject.toml</code> na raiz do seu projeto:</p>
<pre><code class="language-toml">[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</code></pre>
<p>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.</p>
<h2>Black: Formatador Opinado</h2>
<h3>Filosofia do Black</h3>
<p>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.</p>
<h3>Instalação e Uso Básico</h3>
<p>Instale o Black:</p>
<pre><code class="language-bash">pip install black</code></pre>
<p>Considere este código desformatado:</p>
<pre><code class="language-python"># 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'}</code></pre>
<p>Execute o Black:</p>
<pre><code class="language-bash">black desformatado.py</code></pre>
<p>O arquivo será reescrito assim:</p>
<pre><code class="language-python"># 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"}</code></pre>
<p>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).</p>
<h3>Configuração e Compatibilidade</h3>
<p>Configure o Black no mesmo <code>pyproject.toml</code>:</p>
<pre><code class="language-toml">[tool.black]
line-length = 100
target-version = ['py38']
include = '\.pyi?$'
exclude = '''
/(
\.git
\.hg | \.mypy_cache | \.tox | \.venv | _build | buck-out | build | dist
)/
'''</code></pre>
<p>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.</p>
<h3>Black em Modo Check</h3>
<p>Se você quiser verificar se o código estaria formatado corretamente sem fazer mudanças (útil em CI/CD), use:</p>
<pre><code class="language-bash">black --check exemplo.py</code></pre>
<h2>isort: Organizando Imports</h2>
<h3>Por que Imports Precisam de Organização?</h3>
<p>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 <strong>isort</strong> automatiza essa tarefa.</p>
<h3>Instalação e Funcionamento</h3>
<p>Instale o isort:</p>
<pre><code class="language-bash">pip install isort</code></pre>
<p>Veja este exemplo com imports desorganizados:</p>
<pre><code class="language-python"># 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</code></pre>
<p>Execute o isort:</p>
<pre><code class="language-bash">isort imports_desordenados.py</code></pre>
<p>O resultado será:</p>
<pre><code class="language-python"># 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</code></pre>
<p>O isort organizou os imports em três grupos: biblioteca padrão (<code>json</code>, <code>os</code>, <code>sys</code>), third-party (<code>django</code>, <code>requests</code>), e local (<code>myapp</code>). Dentro de cada grupo, estão alfabeticamente ordenados.</p>
<h3>Configuração para Compatibilidade com Black</h3>
<p>É <strong>essencial</strong> configurar o isort para ser compatível com Black. Adicione ao <code>pyproject.toml</code>:</p>
<pre><code class="language-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</code></pre>
<p>A linha <code>profile = "black"</code> é crucial — ela faz o isort seguir o comportamento esperado pelo Black, evitando conflitos entre as ferramentas.</p>
<h2>Integração Completa das Três Ferramentas</h2>
<h3>Configuração Unificada no pyproject.toml</h3>
<p>Aqui está uma configuração profissional e completa que integra as três ferramentas:</p>
<pre><code class="language-toml">[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</code></pre>
<h3>Workflow Prático: Exemplo Completo</h3>
<p>Imagine um projeto real com múltiplos arquivos. Aqui está um workflow típico:</p>
<pre><code class="language-bash"># 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 .</code></pre>
<p>Essa é a ordem recomendada porque isort organiza imports, Black formata o estilo geral, e Ruff faz verificações mais profundas.</p>
<h3>Exemplo Real: Antes e Depois</h3>
<p>Arquivo original com vários problemas:</p>
<pre><code class="language-python"># 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'}))</code></pre>
<p>Após executar <code>isort .</code>:</p>
<pre><code class="language-python"># 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):
...</code></pre>
<p>Após executar <code>black .</code>:</p>
<pre><code class="language-python"># 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"}))</code></pre>
<p>Após executar <code>ruff check . --fix</code>:</p>
<pre><code class="language-python"># 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"}))</code></pre>
<p>Observe que Ruff removeu a variável não usada <code>unused_var</code> e simplificou a lógica do if/else redundante.</p>
<h3>Integração com Git Hooks (Pre-commit)</h3>
<p>Para garantir que ninguém faça commit com código ruim, use a ferramenta <code>pre-commit</code>:</p>
<pre><code class="language-bash">pip install pre-commit</code></pre>
<p>Crie um arquivo <code>.pre-commit-config.yaml</code> na raiz do seu projeto:</p>
<pre><code class="language-yaml">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]</code></pre>
<p>Instale os hooks:</p>
<pre><code class="language-bash">pre-commit install</code></pre>
<p>Agora, toda vez que alguém tentar fazer commit, essas ferramentas rodarão automaticamente e bloquearão o commit se houver problemas.</p>
<h2>Conclusão</h2>
<p>Você aprendeu que <strong>Ruff, Black e isort</strong> 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 <code>pyproject.toml</code> com <code>line-length = 100</code> 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.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://docs.astral.sh/ruff/" target="_blank" rel="noopener noreferrer">Ruff - Documentation</a></li>
<li><a href="https://black.readthedocs.io/en/stable/" target="_blank" rel="noopener noreferrer">Black - The Uncompromising Code Formatter</a></li>
<li><a href="https://pycqa.github.io/isort/" target="_blank" rel="noopener noreferrer">isort - Python Utility / Library to Sort Imports</a></li>
<li><a href="https://pre-commit.com/" target="_blank" rel="noopener noreferrer">Pre-commit - A Framework for Managing Git Hooks</a></li>
<li><a href="https://pep8.org/" target="_blank" rel="noopener noreferrer">PEP 8 - Style Guide for Python Code</a></li>
</ul>
<p><!-- FIM --></p>