getaddresses strict: valida listas de correo

Publicado el: 17/10/2026
Tempo de leitura: 5 minutos
Validación de direcciones de correo con getaddresses strict en Python

El parámetro strict de email.utils.getaddresses permite analizar listas de direcciones con reglas más rigurosas. Resulta especialmente útil cuando una aplicación procesa valores de To, Cc o Reply-To provenientes de mensajes externos, formularios, APIs o datos importados. En vez de aceptar estructuras malformadas y corregirlas de manera silenciosa, el modo estricto facilita detectar y rechazar entradas ambiguas.

Qué hace getaddresses

getaddresses recibe una secuencia de cadenas y devuelve pares con nombre visible y dirección. Interpreta comas, nombres entre comillas, corchetes angulares y la sintaxis habitual de buzones.

from email.utils import getaddresses

cabeceras = [
    'Ana <ana@example.com>, Bruno <bruno@example.com>'
]

for nombre, direccion in getaddresses(cabeceras, strict=True):
    print(nombre, direccion)

Con strict=True, una entrada inválida tiene menos posibilidades de convertirse en un resultado que solo parece correcto. Esto define una frontera más clara entre datos aceptados y rechazados.

Por qué importa el modo estricto

Un parser permisivo puede ocultar problemas. Una lista mal escrita puede interpretarse de manera distinta a la intención del usuario, provocar envíos equivocados, crear registros inconsistentes o facilitar evasiones de filtros. El modo estricto reduce esa ambigüedad.

Aun así, el parsing estricto es solo una capa. No demuestra que el dominio exista, que el buzón reciba mensajes ni que la dirección pertenezca a una persona concreta. Conviene combinarlo con reglas de dominio, confirmación por correo, límites y autorización propia de la aplicación.

Validar los valores obtenidos

No asumas que todos los pares devueltos son utilizables. Comprueba cadenas vacías y aplica reglas adicionales:

from email.utils import getaddresses


def extraer_destinatarios(valores: list[str]) -> list[str]:
    destinatarios = []

    for nombre, direccion in getaddresses(valores, strict=True):
        direccion = direccion.strip()
        if not direccion or '@' not in direccion:
            raise ValueError('Dirección inválida')
        destinatarios.append(direccion)

    if not destinatarios:
        raise ValueError('No hay destinatarios válidos')

    return destinatarios

En producción también puedes limitar la cantidad de destinatarios, rechazar caracteres de control y aplicar una lista permitida de dominios.

Evitar una normalización destructiva

Muchas aplicaciones convierten toda la dirección a minúsculas. Puede ser práctico para comparar, pero la parte local es técnicamente sensible a mayúsculas en algunos sistemas. Conserva el valor original y crea una clave normalizada aparte cuando sea necesario.

No elimines signos de suma, puntos, guiones o guiones bajos sin una razón específica del proveedor. Una limpieza agresiva puede transformar una dirección válida en otra distinta. Puedes ampliar conceptos en la sección de Python y en los artículos de Python avanzado.

Separar parsing y autorización

El parsing responde si la estructura parece una lista válida. La autorización decide si la dirección puede utilizarse en ese contexto. Mantén ambas responsabilidades separadas.

DOMINIOS_PERMITIDOS = {'example.com', 'empresa.com'}


def esta_permitido(direccion: str) -> bool:
    _, dominio = direccion.rsplit('@', 1)
    return dominio.lower() in DOMINIOS_PERMITIDOS

Este diseño facilita las pruebas y evita mezclar reglas de negocio con el análisis sintáctico.

Entradas procedentes de la web

Cuando las direcciones llegan desde un navegador o API pública, aplica límites antes del parsing. Restringe la longitud total del campo y el número máximo de destinatarios. Una entrada enorme puede consumir CPU, memoria y espacio de logs.

Rechaza retornos de carro y saltos de línea suministrados por usuarios. Los caracteres \r y \n pueden causar inyección de cabeceras. Es preferible usar las clases estructuradas del paquete email en lugar de concatenar cadenas manualmente.

Usar un modelo de datos

from dataclasses import dataclass
from email.utils import getaddresses

@dataclass(frozen=True)
class Destinatario:
    nombre: str
    direccion: str


def analizar_destinatarios(valores: list[str]) -> list[Destinatario]:
    elementos = []
    for nombre, direccion in getaddresses(valores, strict=True):
        if not direccion or '@' not in direccion:
            raise ValueError(f'Entrada inválida: {nombre!r}')
        elementos.append(Destinatario(nombre.strip(), direccion.strip()))
    return elementos

Un modelo inmutable evita cambios accidentales después de la validación y funciona bien en APIs, colas y servicios de envío.

Pruebas importantes

Prueba direcciones simples, nombres entre comillas, múltiples destinatarios, valores vacíos, comas adicionales, corchetes mal formados, duplicados, nombres Unicode y límites máximos.

def test_destinatario_valido():
    elementos = analizar_destinatarios(['Ana <ana@example.com>'])
    assert elementos[0].direccion == 'ana@example.com'


def test_destinatario_invalido():
    try:
        analizar_destinatarios(['no es una direccion'])
    except ValueError:
        pass
    else:
        raise AssertionError('Se esperaba ValueError')

Consulta también los contenidos de pruebas y las guías de automatización.

Compatibilidad entre versiones

Confirma la versión mínima de Python admitida antes de usar strict. Si una biblioteca debe funcionar en intérpretes antiguos, aísla la compatibilidad en una función documentada y pruébala en integración continua.

No captures cualquier TypeError para repetir automáticamente sin modo estricto. Ese patrón puede ocultar errores de programación que no tienen relación con la versión. Es mejor usar comprobaciones explícitas o un adaptador bien probado.

Límites de seguridad

El parsing estricto no vuelve confiable una dirección. No uses el texto analizado como comando, ruta, fragmento SQL ni token de autorización. Cada destino necesita su propia validación.

Los logs también requieren cuidado. Registrar listas completas puede exponer datos personales. Cuando no sea imprescindible, guarda un identificador de solicitud, la cantidad de direcciones, un resumen de dominios o valores parcialmente ocultos.

Eliminar duplicados

Es común eliminar duplicados conservando el orden:

def destinatarios_unicos(direcciones: list[str]) -> list[str]:
    vistos = set()
    resultado = []
    for direccion in direcciones:
        clave = direccion.casefold()
        if clave not in vistos:
            vistos.add(clave)
            resultado.append(direccion)
    return resultado

Decide si la deduplicación se aplica entre To, Cc y Bcc. Conserva las categorías originales cuando la privacidad o la auditoría dependan de ellas.

Mensajes de error

Los mensajes públicos deben ser útiles sin revelar detalles internos del parser. “Una o más direcciones no son válidas” suele ser más seguro que repetir la entrada completa. Los logs internos pueden guardar diagnósticos estructurados con los datos sensibles ocultos.

Referencias oficiales

La documentación de email.utils explica las funciones auxiliares y el comportamiento por versión. El formato completo de mensajes de Internet está definido en la RFC 5322.

Conclusión

email.utils.getaddresses(..., strict=True) ofrece un punto de partida más sólido para analizar destinatarios. Úsalo junto con comprobaciones explícitas, límites de tamaño, modelos inmutables, reglas de seguridad y pruebas completas. El modo estricto reduce la ambigüedad, mientras que la arquitectura que lo rodea determina la fiabilidad final del sistema.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Código Python que representa functools.Placeholder y argumentos posicionales
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    functools.Placeholder: argumentos flexibles en partial

    Aprende functools.Placeholder en Python 3.14 para crear llamadas parciales flexibles con argumentos posicionales abiertos.

    Ler mais

    Tempo de leitura: 6 minutos
    17/10/2026
    Código en una pantalla que representa t-strings en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    T-strings en Python 3.14: templates estructurados

    Aprende t-strings en Python 3.14, explora Template e Interpolation y crea renderizadores estructurados, extensibles y seguros.

    Ler mais

    Tempo de leitura: 6 minutos
    16/10/2026
    CPU y módulos de memoria que representan shared_memory track en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    shared_memory track: controla el resource tracker

    Aprende shared_memory track en Python para controlar el resource tracker, la propiedad y la limpieza entre procesos.

    Ler mais

    Tempo de leitura: 5 minutos
    16/10/2026
    Unidad de almacenamiento de datos que representa compression.zstd en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    compression.zstd: streaming y compresión segura

    Aprende compression.zstd en Python para comprimir archivos y datos con velocidad, streaming, diccionarios y límites seguros.

    Ler mais

    Tempo de leitura: 7 minutos
    15/10/2026
    Código en pantalla para diagnosticar procesos con sys.remote_exec en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    sys.remote_exec: diagnostica procesos Python

    Aprende sys.remote_exec en Python para diagnosticar procesos activos con scripts controlados, permisos seguros y buenas prácticas.

    Ler mais

    Tempo de leitura: 7 minutos
    15/10/2026
    Código Python y configuración de variables de entorno con os.reload_environ
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    os.reload_environ: threads y variables de entorno

    Aprende os.reload_environ en Python para actualizar variables de entorno externas con validación, compatibilidad y precauciones de concurrencia.

    Ler mais

    Tempo de leitura: 5 minutos
    14/10/2026