D-Strings no Python 3.15: Adeus textwrap.dedent()

Entenda as d-strings da PEP 822 no Python 3.15, como funcionam, sintaxe com f-strings e por que substituem textwrap.dedent() em strings multiline.

6 min de leitura Equipe python.dev.br

Se você já escreveu strings multiline dentro de funções ou classes em Python, provavelmente conhece o problema: a indentação do código contamina o conteúdo da string. A solução clássica sempre foi textwrap.dedent(), mas ela tem limitações e adiciona overhead em tempo de execução.

A PEP 822 propõe uma solução elegante para o Python 3.15: as d-strings (dedented strings). Com o prefixo d antes de aspas triplas, a indentação é removida automaticamente pelo compilador, sem custo em runtime. É uma daquelas mudanças que parece pequena, mas melhora o dia a dia de quem escreve Python profissionalmente.

O problema com strings multiline hoje

Veja um exemplo comum em código Python atual:

def gerar_sql():
    query = """
        SELECT u.nome, u.email
        FROM usuarios u
        WHERE u.ativo = true
        ORDER BY u.nome
    """
    return query

print(repr(gerar_sql()))
# '\n        SELECT u.nome, u.email\n        FROM usuarios u\n  ...'

A string resultante vem com toda a indentação do bloco. Para resolver, usamos textwrap.dedent():

import textwrap

def gerar_sql():
    query = textwrap.dedent("""
        SELECT u.nome, u.email
        FROM usuarios u
        WHERE u.ativo = true
        ORDER BY u.nome
    """)
    return query

Funciona, mas tem problemas:

  • Overhead em runtime: dedent() é uma função que processa a string toda vez que o código executa
  • Verbosidade: precisa importar textwrap e envolver a string na chamada
  • Não funciona com t-strings: a nova sintaxe de template strings da PEP 750 não é compatível com textwrap.dedent()
  • Confusão com a primeira linha: o comportamento com a linha logo após as aspas de abertura nem sempre é intuitivo

O que são d-strings?

D-strings são strings multiline com o prefixo d que removem automaticamente a indentação comum de todas as linhas. O processamento acontece em tempo de compilação — sem nenhum custo em runtime.

A sintaxe básica:

s = d"""
    Hello
    World!
    """
print(repr(s))  # 'Hello\nWorld!\n'

Regras fundamentais:

  1. O prefixo d só funciona com aspas triplas (""" ou ''')
  2. As aspas de abertura devem ser seguidas de uma quebra de linha
  3. A quebra de linha após a abertura não é incluída no resultado
  4. A indentação a remover é calculada pela indentação comum mais longa entre todas as linhas (incluindo a linha das aspas de fechamento)

Se você está aprendendo sobre os diferentes tipos de strings em Python, vale revisar também nosso glossário sobre strings e o artigo sobre f-strings.

Exemplos práticos

SQL sem indentação indesejada

def buscar_usuarios(status):
    return d"""
        SELECT u.nome, u.email, u.criado_em
        FROM usuarios u
        WHERE u.status = '{status}'
        ORDER BY u.criado_em DESC
        LIMIT 100
        """

print(buscar_usuarios("ativo"))
# SELECT u.nome, u.email, u.criado_em
# FROM usuarios u
# WHERE u.status = 'ativo'
# ORDER BY u.criado_em DESC
# LIMIT 100

HTML em geradores de template

def card_usuario(nome, cargo):
    return d"""
        <div class="card">
            <h3>{nome}</h3>
            <p>{cargo}</p>
        </div>
        """

Mensagens de log e docstrings de CLI

def mostrar_ajuda():
    print(d"""
        Uso: meuscript [opções] <arquivo>

        Opções:
          -v, --verbose    Modo detalhado
          -o, --output     Arquivo de saída
          -h, --help       Mostra esta ajuda
        """)

Controle do trailing newline

A posição das aspas de fechamento controla se há quebra de linha no final:

# Com newline no final (aspas na linha seguinte)
s = d"""
    Hello
    World!
    """
print(repr(s))  # 'Hello\nWorld!\n'

# Sem newline no final (aspas na mesma linha)
s = d"""
    Hello
    World!"""
print(repr(s))  # 'Hello\nWorld!'

Preservando indentação relativa

Se as aspas de fechamento estiverem alinhadas à esquerda, a indentação relativa é preservada:

s = d"""
    def hello():
        print("world")
"""
print(repr(s))  # '    def hello():\n        print("world")\n'

Isso é muito útil para gerar código Python indentado corretamente.

Combinação com outros prefixos

Uma das grandes vantagens das d-strings é que combinam naturalmente com outros prefixos de string:

df-string (dedent + formatação)

nome = "Maria"
saudacao = df"""
    Olá, {nome}!
    Bem-vinda ao sistema.
    """
print(saudacao)
# Olá, Maria!
# Bem-vinda ao sistema.

dr-string (dedent + raw)

padrao = dr"""
    ^\d{3}\.\d{3}\.\d{3}-\d{2}$
    """
print(repr(padrao))  # '^\\d{3}\\.\\d{3}\\.\\d{3}-\\d{2}$\n'

Ideal para regex sem precisar escapar barras invertidas. Se você trabalha com expressões regulares, veja nosso artigo sobre regex em Python.

db-string (dedent + bytes)

dados = db"""
    HTTP/1.1 200 OK
    Content-Type: text/plain
    """
print(type(dados))  # <class 'bytes'>

dt-string (dedent + template)

nome = "João"
t = dt"""
    Olá, {nome}!
    """
print(type(t))  # <class 'string.templatelib.Template'>

Essa combinação é especialmente relevante porque textwrap.dedent() não funciona com template strings da PEP 750, já que elas não são str. As d-strings resolvem esse problema.

Como funciona o algoritmo de dedent

O algoritmo é executado pelo compilador (não em runtime) e segue estes passos:

  1. Identifica a indentação de cada linha (espaços e tabs no início)
  2. Ignora linhas em branco (somente whitespace) no cálculo
  3. Inclui a linha das aspas de fechamento no cálculo
  4. Calcula a indentação comum mais longa compartilhada por todas as linhas
  5. Remove essa indentação do início de cada linha

Tabs e espaços são tratados como caracteres distintos — misturar os dois pode causar IndentationError:

# Mistura de tabs e espaços pode gerar erro
s = d"""
	Hello      # tab
    World!     # espaços
    """
# IndentationError se não houver prefixo comum

D-strings vs textwrap.dedent()

Aspectotextwrap.dedent()D-strings
Quando executaRuntimeCompilação
OverheadSim (processa a string)Zero
ImportaçãoPrecisa de import textwrapNenhuma
Funciona com f-stringsSim, mas verbosoSim, com df"""
Funciona com t-stringsNãoSim, com dt"""
Continuação de linhaNão dedenta a linha seguinteDedenta normalmente
Trailing newlineRemove a primeira, mantém a últimaControlado pela posição das aspas

Migração de código existente

Se você tem código usando textwrap.dedent(), a migração é direta na maioria dos casos:

# Antes
import textwrap

msg = textwrap.dedent("""\
    Linha 1
    Linha 2
    Linha 3
""")

# Depois
msg = d"""
    Linha 1
    Linha 2
    Linha 3
    """

Atenção: o comportamento com a primeira e última linha pode diferir em casos específicos. Teste a saída ao migrar.

Erros comuns e restrições

A PEP define claramente o que não é permitido:

# Aspas simples ou duplas (sem triplas) — SyntaxError
s = d"hello"

# Conteúdo na mesma linha das aspas de abertura — SyntaxError
s = d"""Hello
    World!"""

# Barra invertida logo após a abertura — SyntaxError
s = d"""\
    Hello"""

Essas restrições existem para manter a sintaxe previsível e consistente.

Status atual e quando usar

A PEP 822 está em status Draft e é direcionada ao Python 3.15. O Steering Council ainda não aprovou formalmente, mas a proposta tem boa recepção na comunidade. Uma implementação de referência já existe.

Quando a feature estiver disponível, use d-strings sempre que:

  • Escrever SQL, HTML, YAML ou qualquer texto multiline dentro de funções
  • Precisar de strings formatadas multiline com df"""
  • Trabalhar com template strings (dt""")
  • Quiser eliminar o import textwrap e o overhead de dedent()

Se você quer acompanhar outras novidades do Python 3.15, confira nosso artigo sobre lazy imports (PEP 810) e fique de olho no blog para mais atualizações.

Conclusão

As d-strings são o tipo de melhoria que não revoluciona a linguagem, mas remove uma fonte constante de atrito. Quem já perdeu tempo debugando indentação em strings multiline ou se incomodou com o boilerplate de textwrap.dedent() vai entender o valor dessa PEP.

A combinação com f-strings e t-strings é o ponto mais forte: pela primeira vez teremos uma forma limpa e sem overhead de escrever templates multiline formatados dentro de código indentado. Para quem trabalha com geração de código, SQL dinâmico ou templates HTML em Python, essa é uma adição bem-vinda ao toolkit da linguagem.

E

Equipe python.dev.br

Contribuidor do Python Brasil — Aprenda Python em Português