♻️ Refactor parts that use optional requirements to make them compatible with installations without them
diff --git a/fastapi/openapi/models.py b/fastapi/openapi/models.py
index 7284fa5ba8e56..7176e59dda905 100644
--- a/fastapi/openapi/models.py
+++ b/fastapi/openapi/models.py
@@ -1,7 +1,14 @@
 from enum import Enum
-from typing import Any, Callable, Dict, Iterable, List, Optional, Union
-
-from fastapi._compat import PYDANTIC_V2, _model_rebuild
+from typing import Any, Callable, Dict, Iterable, List, Optional, Type, Union
+
+from fastapi._compat import (
+    PYDANTIC_V2,
+    CoreSchema,
+    GetJsonSchemaHandler,
+    JsonSchemaValue,
+    _model_rebuild,
+    general_plain_validator_function,
+)
 from fastapi.logger import logger
 from pydantic import AnyUrl, BaseModel, Field
 from typing_extensions import Literal
@@ -26,6 +33,26 @@ def validate(cls, v: Any) -> str:
             )
             return str(v)
 
+        @classmethod
+        def _validate(cls, __input_value: Any, _: Any) -> str:
+            logger.warning(
+                "email-validator not installed, email fields will be treated as str.\n"
+                "To install, run: pip install email-validator"
+            )
+            return str(__input_value)
+
+        @classmethod
+        def __get_pydantic_json_schema__(
+            cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler
+        ) -> JsonSchemaValue:
+            return {"type": "string", "format": "email"}
+
+        @classmethod
+        def __get_pydantic_core_schema__(
+            cls, source: Type[Any], handler: Callable[[Any], CoreSchema]
+        ) -> CoreSchema:
+            return general_plain_validator_function(cls._validate)
+
 
 class Contact(BaseModel):
     name: Optional[str] = None
diff --git a/fastapi/utils.py b/fastapi/utils.py
index 18ae2365db802..2efe7f15a9232 100644
--- a/fastapi/utils.py
+++ b/fastapi/utils.py
@@ -15,7 +15,6 @@
 from weakref import WeakKeyDictionary
 
 import fastapi
-from dirty_equals import IsStr
 from fastapi._compat import (
     PYDANTIC_V2,
     BaseConfig,
@@ -219,5 +218,7 @@ def get_value_or_default(
     return first_item
 
 
-def match_pydantic_error_url(error_type: str) -> IsStr:
+def match_pydantic_error_url(error_type: str) -> Any:
+    from dirty_equals import IsStr
+
     return IsStr(regex=rf"^https://errors\.pydantic\.dev/.*/v/{error_type}")

♻️ Refactor parts that use optional requirements to make them compatible with installations without them