O módulo stringprep expõe as tabelas definidas pelo RFC 3454 para preparação de strings Unicode usadas em protocolos de internet. A proposta do padrão é transformar identificadores para uma forma comparável, remover ou mapear certos caracteres, rejeitar categorias proibidas e aplicar regras de texto bidirecional antes que os dados sejam enviados ou armazenados.
É importante entender o limite da biblioteca: stringprep não oferece uma função única que “limpa” qualquer texto. Ele fornece funções de consulta e mapeamento. Cada protocolo precisa definir um perfil indicando quais tabelas usar, se deve aplicar case folding, qual normalização executar e quais regras adicionais validar.
Um módulo baseado em um padrão histórico
O RFC 3454 foi criado para perfis como Nameprep, usado nas primeiras versões de nomes de domínio internacionalizados. As tabelas são baseadas em Unicode 3.2, por isso não refletem automaticamente todos os caracteres adicionados em versões modernas do Unicode.
Não invente um perfil próprio para autenticação, domínios ou identificadores críticos. Use o padrão atual do protocolo e uma biblioteca específica quando disponível. O módulo é valioso para interoperabilidade, manutenção de sistemas legados e compreensão das etapas de preparação.
Tabelas como funções
As tabelas seriam grandes se fossem representadas por listas. Por isso, o Python as expõe como funções características que retornam verdadeiro ou falso, e funções de mapeamento que devolvem a substituição.
import stringprep
caractere = "\u00ad" # soft hyphen
print(stringprep.in_table_b1(caractere))A tabela B.1 contém caracteres normalmente mapeados para nada. O resultado da consulta não altera a string; a aplicação precisa realizar a transformação.
Pontos de código não atribuídos
in_table_a1() informa se um caractere estava não atribuído no Unicode 3.2.
def tem_nao_atribuido(texto):
return any(stringprep.in_table_a1(c) for c in texto)Um perfil pode proibir esses pontos para evitar que um caractere adquira significado diferente depois de uma atualização. Porém, como a tabela é histórica, caracteres modernos podem aparecer como não atribuídos nesse contexto.
Mapear caracteres para nada
A tabela B.1 inclui caracteres que alguns perfis removem, como certas marcas de formatação.
def aplicar_b1(texto):
return "".join(
c for c in texto
if not stringprep.in_table_b1(c)
)Remover caracteres pode criar colisões entre identificadores originalmente distintos. O sistema deve detectar duplicatas depois de toda a preparação e preservar o valor original para auditoria e exibição.
Case folding com B.2 e B.3
map_table_b2() implementa um mapeamento de case folding pensado para uso com NFKC. map_table_b3() corresponde ao mapeamento sem normalização.
def mapear_b2(texto):
return "".join(stringprep.map_table_b2(c) for c in texto)O resultado de uma função pode conter mais de um caractere. Não suponha uma relação um-para-um nem use o comprimento original como limite depois do mapeamento.
Normalização NFKC
Muitos perfis históricos aplicam NFKC depois do mapeamento.
import unicodedata
def preparar_base(texto):
texto = aplicar_b1(texto)
texto = mapear_b2(texto)
return unicodedata.normalize("NFKC", texto)NFKC reduz diferenças de compatibilidade e pode alterar caracteres estilísticos, largura e alguns símbolos. Isso é adequado somente quando o perfil exige. Para texto de apresentação, preserve o original.
Espaços ASCII e não ASCII
As tabelas C.1.1 e C.1.2 identificam espaços ASCII e não ASCII. A função combinada in_table_c11_c12() verifica ambos.
espacos_especiais = [
c for c in texto
if stringprep.in_table_c11_c12(c)
]Um perfil pode mapear espaços para ASCII, proibi-los ou permitir apenas alguns. Não substitua automaticamente todos por espaço comum sem consultar a especificação.
Caracteres de controle
As tabelas C.2.1 e C.2.2 cobrem controles ASCII e não ASCII. in_table_c21_c22() testa a união.
def possui_controle(texto):
return any(
stringprep.in_table_c21_c22(c)
for c in texto
)Controles podem alterar logs, terminais, protocolos e apresentação. Mesmo quando um protocolo permite algum deles, interfaces e registros devem escapar caracteres invisíveis.
Uso privado, não caracteres e surrogates
As funções in_table_c3(), in_table_c4() e in_table_c5() identificam pontos de uso privado, noncharacters e códigos surrogate.
Strings Python válidas podem conter alguns valores problemáticos dependendo da origem e do tratamento de erros. Rejeite-os conforme o perfil e antes de serializar para sistemas que exigem Unicode escalar válido.
Caracteres inadequados
As tabelas C.6 e C.7 marcam caracteres inadequados para texto simples ou representação canônica. C.8 cobre caracteres que alteram propriedades de exibição ou estão obsoletos, e C.9 cobre tagging.
Essas categorias mostram por que uma simples chamada a strip() ou lower() não implementa preparação de protocolo.
Regras bidirecionais
As tabelas D.1 e D.2 identificam caracteres com propriedades bidi R/AL e L. Perfis baseados no RFC 3454 normalmente impõem regras quando há texto da direita para a esquerda.
def validar_bidi(texto):
tem_randal = any(stringprep.in_table_d1(c) for c in texto)
if not tem_randal:
return True
if any(stringprep.in_table_d2(c) for c in texto):
return False
return (
stringprep.in_table_d1(texto[0])
and stringprep.in_table_d1(texto[-1])
)O exemplo demonstra a lógica histórica básica, mas deve seguir exatamente o perfil adotado. Texto bidi pode ter ordem visual diferente da lógica e exige atenção em logs e interfaces.
Construir uma função orientada por perfil
import unicodedata
class StringPrepError(ValueError):
pass
def preparar_legado(texto):
mapeado = "".join(
"" if stringprep.in_table_b1(c)
else stringprep.map_table_b2(c)
for c in texto
)
normalizado = unicodedata.normalize("NFKC", mapeado)
proibidas = (
stringprep.in_table_c12,
stringprep.in_table_c21_c22,
stringprep.in_table_c3,
stringprep.in_table_c4,
stringprep.in_table_c5,
stringprep.in_table_c6,
stringprep.in_table_c7,
stringprep.in_table_c8,
stringprep.in_table_c9,
)
for c in normalizado:
if any(tabela(c) for tabela in proibidas):
raise StringPrepError(f"caractere proibido: U+{ord(c):04X}")
if not validar_bidi(normalizado):
raise StringPrepError("regra bidirecional inválida")
return normalizadoEsse código é apenas um exemplo educacional. A lista de tabelas, a política para A.1 e a ordem exata precisam vir da especificação do perfil.
Validação depois da transformação
Limites devem ser verificados depois de mapeamento e normalização, pois o tamanho pode aumentar ou diminuir. Confira também representação em bytes quando o protocolo define limite codificado.
Se a string preparada será uma chave única, aplique unicidade sobre o valor final dentro de uma transação. Duas entradas diferentes podem convergir.
IDNA e nomes de domínio
Não monte manualmente Nameprep para domínios modernos. Use a API ou biblioteca IDNA apropriada ao padrão exigido. Regras de domínio incluem processamento por labels, Punycode, validação e versões de IDNA.
stringprep pode ajudar a entender sistemas antigos, mas não substitui uma implementação atualizada do protocolo.
Senhas e nomes de usuário
Não aplique stringprep genérico a senhas sem uma especificação. Remover ou mapear caracteres pode reduzir entropia e mudar o segredo digitado. Protocolos modernos de autenticação definem perfis próprios, como PRECIS ou regras específicas.
Para nomes de usuário, preserve o original, defina uma chave preparada separada e monitore colisões e homógrafos.
Homógrafos
Case folding e NFKC não eliminam todos os caracteres visualmente semelhantes. Letras de scripts diferentes podem continuar distintas. Para identificadores públicos sensíveis, use políticas de scripts, detecção especializada e revisão.
Testes necessários
Crie vetores com controles, espaços não ASCII, caracteres removidos, expansão de case folding, texto RTL, misturas L/R, caracteres modernos e entradas vazias. Compare com vetores oficiais do protocolo.
Registre a versão da implementação e evite alterar a preparação de chaves existentes sem uma migração.
Erros frequentes
- Acreditar que o módulo oferece uma função completa de limpeza.
- Criar um perfil sem consultar um RFC.
- Ignorar que as tabelas usam Unicode 3.2.
- Validar tamanho antes do mapeamento.
- Não detectar colisões depois da preparação.
- Aplicar NFKC ou remoção a senhas arbitrariamente.
- Ignorar regras bidi e homógrafos.
Boas práticas
- Use uma biblioteca específica do protocolo.
- Preserve o texto original.
- Versione a função de preparação.
- Aplique etapas na ordem da especificação.
- Valide comprimento e unicidade no final.
- Escape caracteres invisíveis em logs.
- Teste com vetores oficiais e Unicode adversarial.
Conteúdos relacionados
Veja unicodedata, locale, textwrap, fnmatch e contextvars.
Consulte a documentação oficial do stringprep e o RFC 3454.
Conclusão
stringprep é uma caixa de ferramentas de baixo nível para perfis históricos de preparação Unicode. Ele deve ser usado sob uma especificação clara, com consciência da base Unicode 3.2, da possibilidade de colisões e das regras bidirecionais. Para protocolos modernos, prefira implementações especializadas e atualizadas.







