Repository: pydantic/pydantic
Pull request: Improve email regexp on edge cases
- Drastically improves performance on cases like `"<" + " " * N`
- Last spaces are not needed anyway because this group is stripped later. Also spaces will be caught by `.` anyway.

<!-- Thank you for your contribution! -->
<!-- Unless your change is trivial, please create an issue to discuss the change before creating a PR -->

## Change Summary

I found that one single change in email regexp solves slowdowns on special invalid email strings. See related issue for details

## Related issue number

Fixes #10600 

## Checklist

* [ ] The pull request title is a good summary of the changes - it will be used in the changelog
* [x] Unit tests for the changes exist
* [x] Tests pass on CI
* [x] Documentation reflects the changes where applicable
* [x] My PR is ready to review, **please add a comment including the phrase "please review" to assign reviewers**


Selected Reviewer: @sydney-runkle

Problem statement:
Email parsing slowdown on edgecases
### Initial Checks

- [X] I confirm that I'm using Pydantic V2

### Description

I found that current pydantic-level regexp is not optimal and may cause unexpected slowdowns on some edgecases. Email strings like `<        ` (+many spaces). 

POC below shows it. You can run it  with command like `/usr/bin/time python pydantic-poc.py 500`.
The third value in a line should be near constant which shows that approximate complexity of one operation is ~ O(N^3)

I believe it is not a security issue because it runs fast enough even for largest possible sequences however.

### Example Code

```Python
import sys

from pydantic import BaseModel, EmailStr, ValidationError
import time


class TestModel(BaseModel):
    email: EmailStr


def main(max_ind: int):
    for ind in range(1, max_ind):
        begin_at = time.time()
        try:
            TestModel.parse_obj({"email": "<" + " " * ind})
        except ValidationError:
            pass
        delta = time.time() - begin_at
        print(
            f"{ind} takes: {delta:0.6f}, delta**1/3 / ind is {delta**(1/3) / ind:0.6f}"
        )
    return


if __name__ == "__main__":
    main(int(sys.argv[1]))
```


### Python, Pydantic & OS Version

```Text
pydantic version: 2.9.2
        pydantic-core version: 2.23.4
          pydantic-core build: profile=release pgo=false
                 install path: /home/alex/Projects/re-conference/venv/lib/python3.12/site-packages/pydantic
               python version: 3.12.3 (main, Sep 11 2024, 14:17:37) [GCC 13.2.0]
                     platform: Linux-6.8.0-45-generic-x86_64-with-glibc2.39
             related packages: typing_extensions-4.12.2
                       commit: unknown
```

Hints from issue discussion:

Commit message: Improve email regexp on edge cases

- Drastically improves performance on cases like `"<" + " " * N`
- Last spaces are not needed anyway because this group is
stripped later. Also spaces will be caught by `.` anyway.

Patch to review:
diff --git a/pydantic/networks.py b/pydantic/networks.py
index 256f2bf1e4d..80e491472b1 100644
--- a/pydantic/networks.py
+++ b/pydantic/networks.py
@@ -722,7 +722,7 @@ def _build_pretty_email_regex() -> re.Pattern[str]:
     name_chars = r'[\w!#$%&\'*+\-/=?^_`{|}~]'
     unquoted_name_group = rf'((?:{name_chars}+\s+)*{name_chars}+)'
     quoted_name_group = r'"((?:[^"]|\")+)"'
-    email_group = r'<\s*(.+)\s*>'
+    email_group = r'<\s*(.+)>'
     return re.compile(rf'\s*(?:{unquoted_name_group}|{quoted_name_group})?\s*{email_group}\s*')
 
 


Code review comments:
diff_hunk: @@ -722,7 +722,7 @@ def _build_pretty_email_regex() -> re.Pattern[str]:
     name_chars = r'[\w!#$%&\'*+\-/=?^_`{|}~]'
     unquoted_name_group = rf'((?:{name_chars}+\s+)*{name_chars}+)'
     quoted_name_group = r'"((?:[^"]|\")+)"'
-    email_group = r'<\s*(.+)\s*>'
+    email_group = r'<\s*(.+)>'
path: pydantic/networks.py
text: @user1:
```suggestion
    email_group = r'<(.+)>'
```

The `.strip()` call also removes leading whitespaces.