Repository: 0b01001001/spectree (language: python)

Issue:
Can the email validator dependency be optional?
`EmailStr` is used for the contact info, but the field is optional. It's a waste to install these additional dependencies if I don't plan on supplying a value for this field, and don't plan on using `EmailStr` for my own API's models. Or maybe I do want to supply an email in the contact info, but don't value validation of it enough to justify the additional dependencies.

Is there a nice way to make this dependency optional? Of course, it could just be an extra for spectree, but will that feel awkward or unintuitive for users?


Discussion / hints:
Not sure how to make it optional. Even if you don't install the `email-validator`, there won't be an error until you call the `EmailStr` validation. Deleting it from the requirements file also works for most users.

But `email-validator` is a single file library without any other dependencies. I guess it's still okay to keep it here.

Feel free to propose a nicer way to handle it.

> Deleting it from the requirements file also works for most users.

I'm not sure if I understood you. If I clone spectree and modify `requirements.txt` beforehand, then yes, that works. But it's not a practical solution.

> But email-validator is a single file library without any other dependencies. I guess it's still okay to keep it here.

It has two dependencies: https://github.com/JoshData/python-email-validator/blob/main/setup.cfg#L27-L29

> Feel free to propose a nicer way to handle it.

I'm not sure what to do about avoiding installing the email validator. If spectree stops installing it by default, then it will be a breaking change. Otherwise, the following code could be used.

```py
# spectree/config.py
from pydantic import EmailStr

try:
    EmailStr.validate("a@b.com")
except ImportError:
    EmailStr = str
```
Well, maybe not quite a breaking change, but it would be misleading for users that didn't get the memo on email validation no longer being enabled by default.
> > But email-validator is a single file library without any other dependencies. I guess it's still okay to keep it here.
> 
> It has two dependencies: https://github.com/JoshData/python-email-validator/blob/main/setup.cfg#L27-L29

Thanks for pointing it out. I forgot the `setup.cfg` file.

I guess most users won't be affected by this change.

Feel free to open a PR.
Sure, I can make a PR. But what exactly is the change you're agreeing to? That the email validator becomes optional along with the code above e.g. `pip install spectree[email]`, akin to `pip install pydantic[email]`?
Thanks for your help.

Make the `email-validator` optional with `pip install spectree[email]`.

Fix patch:
diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml
index dbb0b500..3bee6bab 100644
--- a/.github/workflows/pythonpackage.yml
+++ b/.github/workflows/pythonpackage.yml
@@ -41,6 +41,5 @@ jobs:
         run: |
           python -m pip install --upgrade pip
           pip install pytest
-          pip install -e .[flask,falcon,starlette]
       - name: Test
         run: make test
diff --git a/Makefile b/Makefile
index 5c105829..e0e2ff9a 100644
--- a/Makefile
+++ b/Makefile
@@ -3,14 +3,13 @@ check: lint test
 SOURCE_FILES=spectree tests examples setup.py
 
 install:
-	pip install -e .[flask,falcon,starlette,dev]
+	pip install -e .[email,flask,falcon,starlette,dev]
 
 test:
-	pip install falcon --upgrade
-	pytest tests -vv
-	pip uninstall falcon -y && pip install falcon==2.0.0
-	pytest tests -vv
-
+	pip install -U -e .[email,flask,falcon,starlette]
+	pytest tests -vv -rs
+	pip uninstall falcon email-validator -y && pip install falcon==2.0.0
+	pytest tests -vv -rs
 doc:
 	cd docs && make html
 
diff --git a/README.md b/README.md
index 4c7d11dd..829ff13a 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ Yet another library to generate OpenAPI documents and validate requests & respon
 
 ## Quick Start
 
-install with pip: `pip install spectree`
+Install with pip: `pip install spectree`. If you'd like for email fields to be validated, use `pip install spectree[email]`.
 
 ### Examples
 
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 1c5868e1..a36726e9 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -27,7 +27,8 @@ Features
 Quick Start
 -----------
 
-install with pip: ``pip install spectree``
+Install with pip: ``pip install spectree``. If you'd like for email fields to be validated, use ``pip install spectree[email]``.
+
 
 Examples
 ~~~~~~~~
diff --git a/requirements.txt b/requirements.txt
index 52c58186..8fa668ad 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,1 +1,1 @@
-pydantic[email]>=1.2
+pydantic>=1.2
diff --git a/setup.py b/setup.py
index 637a600c..7274605a 100644
--- a/setup.py
+++ b/setup.py
@@ -42,6 +42,7 @@
     python_requires=">=3.6",
     install_requires=requires,
     extras_require={
+        "email": ["pydantic[email]>=1.2"],
         "flask": ["flask"],
         "falcon": ["falcon"],
         "starlette": ["starlette[full]"],
diff --git a/spectree/config.py b/spectree/config.py
index bc7a11e2..e0f13d26 100644
--- a/spectree/config.py
+++ b/spectree/config.py
@@ -1,12 +1,22 @@
 import warnings
 from enum import Enum
-from typing import Any, Dict, List, Mapping, Optional
+from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional
 
 from pydantic import AnyUrl, BaseModel, BaseSettings, EmailStr, root_validator
 
 from .models import SecurityScheme, Server
 from .page import DEFAULT_PAGE_TEMPLATES
 
+# Fall back to a str field if email-validator is not installed.
+if TYPE_CHECKING:
+    EmailFieldType = str
+else:
+    try:
+        EmailStr.validate("a@b.com")
+        EmailFieldType = EmailStr
+    except ImportError:
+        EmailFieldType = str
+
 
 class ModeEnum(str, Enum):
     """the mode of the SpecTree validator"""
@@ -27,7 +37,7 @@ class Contact(BaseModel):
     #: contact url
     url: Optional[AnyUrl] = None
     #: contact email address
-    email: Optional[EmailStr] = None
+    email: Optional[EmailFieldType] = None
 
 
 class License(BaseModel):


Test patch:
diff --git a/tests/test_config.py b/tests/test_config.py
index 5eef9c44..80eb13fd 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -5,7 +5,7 @@
 from pydantic import ValidationError
 
 from spectree import SecurityScheme
-from spectree.config import Configuration
+from spectree.config import Configuration, EmailFieldType
 
 from .common import SECURITY_SCHEMAS, WRONG_SECURITY_SCHEMAS_DATA
 
@@ -39,8 +39,11 @@ def test_config_contact():
     with pytest.raises(ValidationError):
         config = Configuration(contact={"name": "John", "url": "url"})
 
+
+@pytest.mark.skipif(EmailFieldType == str, reason="email-validator is not installled")
+def test_config_contact_invalid_email():
     with pytest.raises(ValidationError):
-        config = Configuration(contact={"name": "John", "email": "hello"})
+        Configuration(contact={"name": "John", "email": "hello"})
 
 
 def test_config_case():