LiteralString no Python: strings confiáveis

Publicado em: 29/08/2026
Tempo de leitura: 6 minutos
Close-up view of a computer screen displaying code in a software development environment.

typing.LiteralString representa strings consideradas literais ou derivadas apenas de outras strings literais. O objetivo é ajudar APIs sensíveis a distinguir texto definido pelo desenvolvedor de conteúdo dinâmico recebido de usuários, arquivos, rede ou banco de dados. Essa distinção pode reduzir riscos de injeção em SQL, comandos, templates e formatos, mas não substitui parametrização, escaping, validação ou autorização.

Neste guia, você aprenderá como LiteralString é inferido, quais operações preservam confiança, como projetar APIs, onde o recurso encontra limites, como combiná-lo com parâmetros SQL e como evitar uma falsa sensação de segurança.

O problema de strings dinâmicas

def executar_sql(query: str) -> None:
    ...

nome = input("Nome: ")
executar_sql(f"SELECT * FROM usuarios WHERE nome = '{nome}'")

A assinatura aceita qualquer string. O analisador não distingue a consulta fixa de uma consulta construída com entrada externa. Em runtime, o exemplo é vulnerável a injeção.

Declarando LiteralString

from typing import LiteralString


def executar_sql(query: LiteralString) -> None:
    ...

executar_sql("SELECT * FROM usuarios")

Uma string escrita diretamente no código é compatível. Uma variável ampla do tipo str normalmente não é.

Entrada externa deve ser rejeitada

query: str = input("SQL: ")
executar_sql(query)  # erro estático esperado

O analisador sinaliza que uma string arbitrária não atende ao contrato. Isso cria uma barreira útil em revisão e CI.

LiteralString é um subtipo de str

Uma LiteralString pode ser usada onde str é esperado. O contrário não é verdadeiro. Funções comuns de leitura e exibição continuam aceitando o valor normalmente.

Concatenação de literais

prefixo: LiteralString = "SELECT id, nome "
sufixo: LiteralString = "FROM usuarios"
query = prefixo + sufixo
executar_sql(query)

Operações que combinam apenas LiteralStrings podem preservar o tipo confiável. A inferência exata depende do analisador e da expressão.

f-strings

tabela: LiteralString = "usuarios"
query = f"SELECT * FROM {tabela}"

Uma f-string pode permanecer LiteralString quando todas as partes interpoladas também são confiáveis. Se qualquer expressão for apenas str, a confiança deve ser perdida.

Não use interpolação para dados SQL

Mesmo uma API com LiteralString deve separar estrutura e valores:

def consultar(query: LiteralString, parametros: tuple[object, ...]) -> None:
    ...

nome = input("Nome: ")
consultar(
    "SELECT * FROM usuarios WHERE nome = ?",
    (nome,),
)

A consulta é literal e o dado externo é enviado como parâmetro. O driver é responsável por encoding e escaping. Essa é a proteção principal.

Nomes de tabela dinâmicos

Drivers geralmente parametrizam valores, não identificadores SQL. Se a tabela precisa variar, escolha-a a partir de um conjunto fechado:

from typing import Literal

Tabela = Literal["usuarios", "pedidos"]

def selecionar_tabela(tabela: Tabela) -> LiteralString:
    if tabela == "usuarios":
        return "usuarios"
    return "pedidos"

Não transforme uma string arbitrária em LiteralString com cast. Valide contra allowlists e produza literais conhecidos.

Comandos de shell

def executar_comando(comando: LiteralString) -> None:
    ...

O tipo pode desencorajar comandos construídos com entrada externa, mas a melhor prática é evitar shell=True e passar uma lista de argumentos ao subprocesso:

subprocess.run(["git", "show", revisao], check=True)

A lista separa programa e argumentos. LiteralString não torna o shell seguro automaticamente.

Templates HTML

Uma função de renderização pode exigir template literal e receber dados separadamente:

def renderizar(template: LiteralString, contexto: dict[str, object]) -> str:
    ...

O engine ainda precisa escapar valores por contexto. HTML, atributos, JavaScript e URLs possuem regras diferentes. LiteralString protege a origem do template, não o conteúdo renderizado.

Format strings

def logar(formato: LiteralString, *args: object) -> None:
    ...

logar("usuário %s entrou", nome)

Manter o formato como literal ajuda ferramentas de logging e reduz problemas com placeholders controlados externamente. Os argumentos continuam separados.

Internacionalização

Textos traduzidos carregados de catálogos são str, não LiteralString, mesmo que o catálogo seja controlado pelo projeto. Não force o tipo apenas para satisfazer a assinatura. APIs de tradução e templates precisam de outro modelo de confiança.

Arquivos de configuração

Uma string lida de YAML, JSON ou variável de ambiente é dinâmica. Ela não se torna confiável porque o arquivo está no repositório. Em alguns ambientes, configuração pode ser alterada por deploy, operadores ou atacantes.

Funções que preservam confiança

def adicionar_limite(query: LiteralString) -> LiteralString:
    return query + " LIMIT 100"

Uma função pode receber e retornar LiteralString quando só adiciona conteúdo literal. Se ela incorpora um str arbitrário, o retorno deve ser str.

Builders seguros

Um builder pode representar fragmentos aprovados:

def ordenar_por_nome(query: LiteralString) -> LiteralString:
    return query + " ORDER BY nome"

Para opções dinâmicas, mapeie enums ou Literals para fragmentos fixos. Não aceite fragmentos livres.

cast não sanitiza

from typing import cast

entrada = input("SQL: ")
query = cast(LiteralString, entrada)

O cast apenas silencia o analisador. Ele não valida, escapa nem altera o valor. Usá-lo sobre entrada não confiável destrói a garantia do contrato.

Validação não cria automaticamente LiteralString

Mesmo após verificar uma regex ou allowlist, o analisador pode continuar vendo str. Uma função pequena pode retornar um Literal fechado ou escolher explicitamente um literal. Evite funções genéricas que prometem transformar qualquer texto em confiável.

Literal e LiteralString

Literal["asc", "desc"] representa valores exatos de um conjunto. Esses valores são compatíveis com LiteralString. Literal é melhor para opções fechadas; LiteralString é melhor para APIs que aceitam qualquer texto construído exclusivamente a partir de literais confiáveis.

TypeGuard e LiteralString

TypeGuard não consegue provar de forma geral que uma string arbitrária “veio de literal”. A origem é uma propriedade do fluxo estático, não algo observável no conteúdo. Duas strings iguais podem ter origens diferentes.

Dados de bibliotecas externas

Retornos anotados como str perdem confiança, mesmo que a implementação retorne constantes. Uma biblioteca pode declarar LiteralString quando sua API realmente preserva a origem, mas deve garantir esse contrato em todas as versões.

Stubs

Bibliotecas de banco, logging e templates podem usar LiteralString em arquivos .pyi. Teste a experiência do consumidor com mypy e pyright. Uma anotação excessivamente restritiva pode tornar usos legítimos impossíveis.

Não é rastreamento completo de taint

LiteralString oferece uma aproximação simples de confiança. Ele não rastreia fontes, sanitizadores, contextos, codificações ou fluxos entre processos como um sistema completo de taint analysis.

Segurança em camadas

Use LiteralString como uma camada adicional: parâmetros SQL, listas de argumentos, escaping contextual, allowlists, privilégios mínimos, validação e testes continuam necessários. O tipo ajuda a prevenir erros antes do runtime, não substitui controles reais.

Compatibilidade

Use typing_extensions.LiteralString em versões anteriores. O suporte e a precisão dependem do analisador. Mantenha versões atualizadas e adicione casos de tipagem ao CI.

Erros comuns

  • Usar cast em entrada externa: não há sanitização.
  • Interpolar valores SQL: use parâmetros do driver.
  • Assumir que shell ficou seguro: evite shell=True.
  • Tratar configuração como literal: é dado dinâmico.
  • Confundir com taint analysis completo: o modelo é limitado.
  • Confiar apenas no tipo: runtime precisa de defesas.

Exemplo completo: repositório SQL

from typing import Literal, LiteralString

Ordem = Literal["nome", "criado_em"]


def coluna_ordem(ordem: Ordem) -> LiteralString:
    if ordem == "nome":
        return "nome"
    return "criado_em"


def listar_usuarios(
    conexao,
    termo: str,
    ordem: Ordem,
) -> list[tuple[object, ...]]:
    coluna = coluna_ordem(ordem)
    query: LiteralString
    if coluna == "nome":
        query = (
            "SELECT id, nome FROM usuarios "
            "WHERE nome LIKE ? ORDER BY nome"
        )
    else:
        query = (
            "SELECT id, nome FROM usuarios "
            "WHERE nome LIKE ? ORDER BY criado_em"
        )
    cursor = conexao.execute(query, (f"%{termo}%",))
    return cursor.fetchall()

Os valores de busca são parametrizados. A coluna é escolhida por uma união Literal e cada consulta final é escrita como literal. Nenhuma entrada externa vira fragmento SQL.

Testes estáticos

from typing import assert_type

assert_type(coluna_ordem("nome"), LiteralString)

texto: str = input()
# executar_sql(texto) deve falhar na análise

Inclua casos válidos e inválidos para proteger a fronteira durante mudanças de stubs e analisadores.

Quando usar

Use LiteralString em APIs onde a estrutura textual precisa ser definida pelo desenvolvedor: consultas, templates, formatos e expressões. Não use em funções comuns que processam texto arbitrário, pois isso criaria atrito sem benefício.

Conclusão

LiteralString permite declarar que uma API espera texto originado de literais confiáveis. Ele ajuda o analisador a bloquear strings dinâmicas em posições sensíveis e incentiva a separação entre estrutura e dados.

A documentação oficial de LiteralString no módulo typing detalha a inferência. Use-o como defesa adicional, nunca como substituto de queries parametrizadas, escaping, allowlists, validação e princípios de menor privilégio.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    A laptop screen showing a code editor with visible programming code in a dimly lit environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    dataclass_transform no Python: classes geradas

    Aprenda dataclass_transform no Python para tipar decorators, metaclasses e frameworks que geram __init__, campos e métodos.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026
    High-angle view of woman coding on a laptop, with a Python book nearby. Ideal for programming and tech content.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    TypeVarTuple no Python: genéricos variádicos

    Aprenda TypeVarTuple no Python para preservar tuplas heterogêneas, modelar dimensões e criar genéricos com vários parâmetros.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026
    Close-up of a laptop screen with code and a coffee mug, perfect for tech abstract themes.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    assert_type e reveal_type: teste inferência de tipos

    Aprenda assert_type e reveal_type no Python para inspecionar inferência, testar APIs tipadas e evitar regressões no analisador.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026
    Close-up image of a woman's hand holding a stack of spiral-bound notebooks and papers against a dark background.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    get_type_hints no Python: leia anotações

    Aprenda get_type_hints no Python para resolver referências futuras, ler Annotated e inspecionar funções e classes com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026
    A laptop screen showing a code editor with visible programming code in a dimly lit environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    runtime_checkable no Python: Protocol em runtime

    Aprenda runtime_checkable no Python para testar Protocol com isinstance, entender limites e criar contratos estruturais seguros.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026
    Close-up of a laptop screen with code and a coffee mug, perfect for tech abstract themes.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    Unpack no Python: kwargs e tipos variádicos

    Aprenda typing.Unpack no Python para tipar **kwargs com TypedDict, expandir tuplas variádicas e preservar assinaturas precisas.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026