Email Regex

mediumagents_retrieval

Write the regex that finds email addresses — the metadata step of a retrieval pipeline, where you pull the entities out of a chunk before indexing it. A keyword index is no help here: the tokenizer from BM25 Index shreds alice@example.com into three unrelated terms, so an address has to be recognized by its shape.

email_pattern() returns the pattern source as a string; is_email is given and just runs it with fullmatch. "Valid" is not RFC 5322 — nobody wants that regex — it is exactly this:

  • local part — one or more chunks joined by single dots, each chunk one or more of A-Za-z0-9_%+-. So a.b@… passes; .a@…, a.@… and a..b@… do not.
  • domain — two or more labels joined by single dots. A label starts and ends with a letter or digit and may carry hyphens inside: my-host yes, -host and host- no. The last label is the TLD: letters only, at least two. So example.com and sub.example.co.uk pass; example, example.c and example.c0m do not.
  • Upper case is allowed wherever a letter is.

Then find_emails(text) returns every address in a document, lower-cased, without duplicates, in order of first appearance — addresses are case-insensitive in practice, and what you want on a chunk is the set of contacts it mentions.

That second function is why the pattern needs one more rule, and why this is a task rather than a one-liner: an address may not be followed immediately by a letter, digit or hyphen. Without that, scanning alice@example.com123 reports alice@example.com — an address that is not in that text. A pattern that looks perfect under fullmatch is still wrong under finditer until it refuses to end in the middle of a token.

Constraints
  • no email, email_validator, validators, pydantic

Your solution

Edit email_pattern and run the real pytest suite in your browser — no install required. Your code is saved locally.

⌘/Ctrl + ↵

Loading the Python runtime (first run only)…

Hints

Hint 1

Build the pattern from named pieces instead of one long string: a chunk class, `chunk(?:\.chunk)*` for the local part, a label, `(?:label\.)+` and then the TLD. An f-string keeps it readable — remember `{2,}` has to be written `{{2,}}` inside one.

Hint 2

A label that may not start or end with a hyphen is the two-part idiom `[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?` — the middle is optional, so a single character still matches. And the scanning rule is a negative lookahead `(?![A-Za-z0-9-])` at the very end.

Stuck? Try these first

mediumBM25 Index