Repository: litestar-org/litestar

Issue:
Bug: OpenAPI schema generation fails with pydantic < 2.0.0
### Description

When pydantic 1.* is installed, interactive API docs cannot be loaded.

It seems the problem is that a helper function is trying to access a `pydantic_v2` attribute without checking if it was imported correctly, the stack trace below provides more info.

### URL to code causing the issue

_No response_

### MCVE

```python
from litestar import Litestar, post
from litestar.testing import TestClient
from pydantic import BaseModel


class Order(BaseModel):
    code: str


@post()
async def test_handler(data: Order) -> Order:
    return data


app = Litestar(route_handlers=[test_handler], debug=True)


def test() -> None:
    with TestClient(app=app) as client:
        response = client.get(
            "/schema/swagger",
        )
        assert response.is_success


if __name__ == "__main__":
    test()
```


### Steps to reproduce

```bash
1. Install Litestar with Pydantic v1
2. Start up a server
3. Perform a request to /schema/swagger
```


### Screenshots

```bash
"![SCREENSHOT_DESCRIPTION](SCREENSHOT_LINK.png)"
```


### Logs

```bash
Traceback (most recent call last):
  File "/home/thunder/work/litestar_mvce/litestar_venv/lib/python3.11/site-packages/litestar/middleware/exceptions/middleware.py", line 191, in __call__
    await self.app(scope, receive, send)
  File "/home/thunder/work/litestar_mvce/litestar_venv/lib/python3.11/site-packages/litestar/routes/http.py", line 81, in handle
    response = await self._get_response_for_request(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/thunder/work/litestar_mvce/litestar_venv/lib/python3.11/site-packages/litestar/routes/http.py", line 133, in _get_response_for_request
    return await self._call_handler_function(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/thunder/work/litestar_mvce/litestar_venv/lib/python3.11/site-packages/litestar/routes/http.py", line 153, in _call_handler_function
    response_data, cleanup_group = await self._get_response_data(
                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/thunder/work/litestar_mvce/litestar_venv/lib/python3.11/site-packages/litestar/routes/http.py", line 199, in _get_response_data
    data = route_handler.fn(**parsed_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/thunder/work/litestar_mvce/litestar_venv/lib/python3.11/site-packages/litestar/openapi/controller.py", line 231, in swagger_ui
    return ASGIResponse(body=self.render_swagger_ui(request), media_type=MediaType.HTML)
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/thunder/work/litestar_mvce/litestar_venv/lib/python3.11/site-packages/litestar/openapi/controller.py", line 391, in render_swagger_ui
    schema = self.get_schema_from_request(request)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/thunder/work/litestar_mvce/litestar_venv/lib/python3.11/site-packages/litestar/openapi/controller.py", line 96, in get_schema_from_request
    return request.app.openapi_schema
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/thunder/work/litestar_mvce/litestar_venv/lib/python3.11/site-packages/litestar/app.py", line 586, in openapi_schema
    self.update_openapi_schema()
  File "/home/thunder/work/litestar_mvce/litestar_venv/lib/python3.11/site-packages/litestar/app.py", line 836, in update_openapi_schema
    path_item, created_operation_ids = create_path_item(
                                       ^^^^^^^^^^^^^^^^^
  File "/home/thunder/work/litestar_mvce/litestar_venv/lib/python3.11/site-packages/litestar/_openapi/path_item.py", line 109, in create_path_item
    request_body = create_request_body(
                   ^^^^^^^^^^^^^^^^^^^^
  File "/home/thunder/work/litestar_mvce/litestar_venv/lib/python3.11/site-packages/litestar/_openapi/request_body.py", line 32, in create_request_body
    schema = schema_creator.for_field_definition(field_definition)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/thunder/work/litestar_mvce/litestar_venv/lib/python3.11/site-packages/litestar/_openapi/schema_generation/schema.py", line 301, in for_field_definition
    result = self.for_plugin(field_definition, plugin_for_annotation)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/thunder/work/litestar_mvce/litestar_venv/lib/python3.11/site-packages/litestar/_openapi/schema_generation/schema.py", line 480, in for_plugin
    schema = plugin.to_openapi_schema(field_definition=field_definition, schema_creator=self)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/thunder/work/litestar_mvce/litestar_venv/lib/python3.11/site-packages/litestar/contrib/pydantic/pydantic_schema_plugin.py", line 230, in to_openapi_schema
    return self.for_pydantic_model(field_definition=field_definition, schema_creator=schema_creator)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/thunder/work/litestar_mvce/litestar_venv/lib/python3.11/site-packages/litestar/contrib/pydantic/pydantic_schema_plugin.py", line 250, in for_pydantic_model
    if is_pydantic_2_model(annotation):
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/thunder/work/litestar_mvce/litestar_venv/lib/python3.11/site-packages/litestar/contrib/pydantic/utils.py", line 161, in is_pydantic_2_model
    return issubclass(obj, pydantic_v2.BaseModel)  # pyright: ignore
                           ^^^^^^^^^^^^^^^^^^^^^
AttributeError: type object 'Empty' has no attribute 'BaseModel'
```


### Litestar Version

2.3.2

### Platform

- [X] Linux
- [ ] Mac
- [ ] Windows
- [ ] Other (Please specify in the description above)

<!-- POLAR PLEDGE BADGE START -->
---
> [!NOTE]  
> While we are open for sponsoring on [GitHub Sponsors](https://github.com/sponsors/litestar-org/) and 
> [OpenCollective](https://opencollective.com/litestar), we also utilize [Polar.sh](https://polar.sh/) to engage in pledge-based sponsorship.
>
> Check out all issues funded or available for funding [on our Polar.sh Litestar dashboard](https://polar.sh/litestar-org)
> * If you would like to see an issue prioritized, make a pledge towards it!
> * We receive the pledge once the issue is completed & verified
> * This, along with engagement in the community, helps us know which features are a priority to our users.

<a href="https://polar.sh/litestar-org/litestar/issues/2672">
<picture>
  <source media="(prefers-color-scheme: dark)" srcset="https://polar.sh/api/github/litestar-org/litestar/issues/2672/pledge.svg?darkmode=1">
  <img alt="Fund with Polar" src="https://polar.sh/api/github/litestar-org/litestar/issues/2672/pledge.svg">
</picture>
</a>
<!-- POLAR PLEDGE BADGE END -->



Discussion:

Resolving patch:
diff --git a/litestar/contrib/pydantic/utils.py b/litestar/contrib/pydantic/utils.py
--- a/litestar/contrib/pydantic/utils.py
+++ b/litestar/contrib/pydantic/utils.py
@@ -158,7 +158,7 @@ def pydantic_get_unwrapped_annotation_and_type_hints(annotation: Any) -> tuple[A
 def is_pydantic_2_model(
     obj: type[pydantic_v1.BaseModel | pydantic_v2.BaseModel],  # pyright: ignore
 ) -> TypeGuard[pydantic_v2.BaseModel]:  # pyright: ignore
-    return issubclass(obj, pydantic_v2.BaseModel)  # pyright: ignore
+    return pydantic_v2 is not Empty and issubclass(obj, pydantic_v2.BaseModel)  # type: ignore[comparison-overlap]
 
 
 def is_pydantic_undefined(value: Any) -> bool:


Test patch: