Skip to content

Skip the IDNA round trip for domains without Punycode labels - #182

Open
John-Cusack wants to merge 1 commit into
JoshData:mainfrom
John-Cusack:tier1-pure-python-speedup
Open

John-Cusack wants to merge 1 commit into
JoshData:mainfrom
John-Cusack:tier1-pure-python-speedup

Conversation

@John-Cusack

@John-Cusack John-Cusack commented Oct 29, 2026 •

Copy link
Copy Markdown

pydantic's EmailStr calls validate_email() with check_deliverability=False for every value it validates (networks.py), so a bulk import runs it once per record.

For a domain with no xn-- label, the idna.decode() and idna.encode() round trip at the end of validate_email_domain_name() returns it unchanged, and the character checks after it cannot fail on a domain that has already matched the hostname pattern and passed the length checks. This returns early in that case. Domains with Punycode labels take the existing path.

On the script below, validation goes from about 54 µs to 30 µs per address.

The test suite passes on Python 3.10 and 3.14 (on 3.12.3, test_domain_literal fails on main too), and results and error messages match main on about 52,000 addresses.

Benchmark

Ryzen 9 5950X, CPython 3.12.3, idna 3.15. Best of 21 runs over 2,000 addresses.

import random
import timeit

from email_validator import EmailNotValidError, validate_email

rng = random.Random(0)


def make(i):
    r = rng.random()
    if r < 0.2:
        return f"user{i}@bad_domain{i}.com"  # rejected
    if r < 0.3:
        return f"user{i}@münchen-{i % 50}.de"  # internationalized domain
    return f"first.last+{i}@mail{i % 200}.example.com"


addrs = [make(i) for i in range(2000)]


def run():
    for a in addrs:
        try:
            validate_email(a, check_deliverability=False)
        except EmailNotValidError:
            pass


run()
best = min(timeit.repeat(run, number=1, repeat=21))
print(f"{best / len(addrs) * 1e6:.1f} us per address")

@JoshData

Copy link
Copy Markdown
Owner

But why?

@John-Cusack
John-Cusack force-pushed the tier1-pure-python-speedup branch from 4e2ddc7 to b9b3fdd Compare October 29, 2026 15:00
@John-Cusack John-Cusack changed the title Pure-Python validation speedup (~3x mixed, ~5x valid-ASCII), no behavior change Skip the IDNA round trip for domains without Punycode labels Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants