Repository: litestar-org/litestar

Issue:
Bug: App launching fails because of Pydantic version handling problem
### Description

Launching a Litestar app fails because of a variable that has a value of type `_EmptyEnum` and is expected to have an attribute `JsonValue`.

This is [a Pydantic version handling mechanism](https://github.com/litestar-org/litestar/blob/8d6782db0a176e3d327b42f3ed36e728e199a229/litestar/contrib/pydantic/pydantic_dto_factory.py#L50) that assigns a value of `Empty` ([here](https://github.com/litestar-org/litestar/blob/8d6782db0a176e3d327b42f3ed36e728e199a229/litestar/types/empty.py#L16)) to `pydantic_v2`.

### URL to code causing the issue

_No response_

### MCVE

```python
from litestar import Litestar, get


@get("/")
async def index() -> str:
    return "Hello, world!"


@get("/books/{book_id:int}")
async def get_book(book_id: int) -> dict[str, int]:
    return {"book_id": book_id}


app = Litestar([index, get_book])
```


### Steps to reproduce

```bash
1. Write the MCVE code in a file and name it `app.py`.
2. Run the app with `litestar --app app:app run  --host 0.0.0.0 --port 8000`
```


### Screenshots

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


### Logs

```bash
Using Litestar app from env: 'sample_app:app'
Traceback (most recent call last):
  File "/home/zahran/miniconda3/envs/sample_env/bin/litestar", line 8, in <module>
    sys.exit(run_cli())
  File "/home/zahran/miniconda3/envs/sample_env/lib/python3.10/site-packages/litestar/__main__.py", line 6, in run_cli
    litestar_group()
  File "/home/zahran/miniconda3/envs/sample_env/lib/python3.10/site-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
  File "/home/zahran/miniconda3/envs/sample_env/lib/python3.10/site-packages/rich_click/rich_command.py", line 125, in main
    with self.make_context(prog_name, args, **extra) as ctx:
  File "/home/zahran/miniconda3/envs/sample_env/lib/python3.10/site-packages/litestar/cli/_utils.py", line 218, in make_context
    self._prepare(ctx)
  File "/home/zahran/miniconda3/envs/sample_env/lib/python3.10/site-packages/litestar/cli/_utils.py", line 200, in _prepare
    env = ctx.obj = LitestarEnv.from_env(ctx.params.get("app_path"), ctx.params.get("app_dir"))
  File "/home/zahran/miniconda3/envs/sample_env/lib/python3.10/site-packages/litestar/cli/_utils.py", line 106, in from_env
    loaded_app = _load_app_from_path(app_path)
  File "/home/zahran/miniconda3/envs/sample_env/lib/python3.10/site-packages/litestar/cli/_utils.py", line 271, in _load_app_from_path
    module = importlib.import_module(module_path)
  File "/home/zahran/miniconda3/envs/sample_env/lib/python3.10/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/home/ahmedzahran/projects/repos/agolo/neural-qbs/sample_app.py", line 14, in <module>
    app = Litestar([index, get_book])
  File "/home/zahran/miniconda3/envs/sample_env/lib/python3.10/site-packages/litestar/app.py", line 361, in __init__
    plugins=self._get_default_plugins(list(plugins or [])),
  File "/home/zahran/miniconda3/envs/sample_env/lib/python3.10/site-packages/litestar/app.py", line 521, in _get_default_plugins
    from litestar.contrib.pydantic import (
  File "/home/zahran/miniconda3/envs/sample_env/lib/python3.10/site-packages/litestar/contrib/pydantic/__init__.py", line 8, in <module>
    from .pydantic_dto_factory import PydanticDTO
  File "/home/zahran/miniconda3/envs/sample_env/lib/python3.10/site-packages/litestar/contrib/pydantic/pydantic_dto_factory.py", line 50, in <module>
    pydantic_v2.JsonValue: Any,
AttributeError: '_EmptyEnum' object has no attribute 'JsonValue'
```


### Litestar Version

2.8.0

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



Discussion:
```
(.venv) peter@pop-os:~/PycharmProjects/litestar-import-test$ pip freeze
anyio==4.3.0
certifi==2024.2.2
click==8.1.7
Faker==24.7.1
h11==0.14.0
httpcore==1.0.5
httpx==0.27.0
idna==3.6
litestar==2.8.0
markdown-it-py==3.0.0
mdurl==0.1.2
msgspec==0.18.6
multidict==6.0.5
polyfactory==2.15.0
Pygments==2.17.2
python-dateutil==2.9.0.post0
PyYAML==6.0.1
rich==13.7.1
rich-click==1.7.4
six==1.16.0
sniffio==1.3.1
typing_extensions==4.11.0
uvicorn==0.29.0
```

```
(.venv) peter@pop-os:~/PycharmProjects/litestar-import-test$ litestar --app app:app run
Using Litestar app from env: 'app:app'
Starting server process ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
┌──────────────────────────────┬──────────────────────┐
│ Litestar version             │ 2.8.0                │
│ Debug mode                   │ Disabled             │
│ Python Debugger on exception │ Disabled             │
│ CORS                         │ Disabled             │
│ CSRF                         │ Disabled             │
│ OpenAPI                      │ Enabled path=/schema │
│ Compression                  │ Disabled             │
└──────────────────────────────┴──────────────────────┘
INFO:     Started server process [138512]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```

```
(.venv) peter@pop-os:~/PycharmProjects/litestar-import-test$ curl localhost:8000/
Hello, world!(.venv) peter@pop-os:~/PycharmProjects/litestar-import-test$ 
```

Can you provide output of `pip freeze` or similar to show us your environment? Although, I assume this is triggered by having pydantic v1 in there.

Thanks for reporting.
> Although, I assume this is triggered by having pydantic v1 in there.

```
(.venv) peter@pop-os:~/PycharmProjects/litestar-import-test$ python -m pip install pydantic==1.10
Collecting pydantic==1.10
  Downloading pydantic-1.10.0-py3-none-any.whl.metadata (138 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 138.4/138.4 kB 1.7 MB/s eta 0:00:00
Requirement already satisfied: typing-extensions>=4.1.0 in ./.venv/lib/python3.12/site-packages (from pydantic==1.10) (4.11.0)
Downloading pydantic-1.10.0-py3-none-any.whl (153 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 153.5/153.5 kB 5.0 MB/s eta 0:00:00
Installing collected packages: pydantic
Successfully installed pydantic-1.10.0
(.venv) peter@pop-os:~/PycharmProjects/litestar-import-test$ litestar --app app:app run
Using Litestar app from env: 'app:app'
Traceback (most recent call last):
  File "/home/peter/PycharmProjects/litestar-import-test/.venv/bin/litestar", line 8, in <module>
    sys.exit(run_cli())
             ^^^^^^^^^
  File "/home/peter/PycharmProjects/litestar-import-test/.venv/lib/python3.12/site-packages/litestar/__main__.py", line 6, in run_cli
    litestar_group()
  File "/home/peter/PycharmProjects/litestar-import-test/.venv/lib/python3.12/site-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/peter/PycharmProjects/litestar-import-test/.venv/lib/python3.12/site-packages/rich_click/rich_command.py", line 125, in main
    with self.make_context(prog_name, args, **extra) as ctx:
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/peter/PycharmProjects/litestar-import-test/.venv/lib/python3.12/site-packages/litestar/cli/_utils.py", line 218, in make_context
    self._prepare(ctx)
  File "/home/peter/PycharmProjects/litestar-import-test/.venv/lib/python3.12/site-packages/litestar/cli/_utils.py", line 200, in _prepare
    env = ctx.obj = LitestarEnv.from_env(ctx.params.get("app_path"), ctx.params.get("app_dir"))
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/peter/PycharmProjects/litestar-import-test/.venv/lib/python3.12/site-packages/litestar/cli/_utils.py", line 106, in from_env
    loaded_app = _load_app_from_path(app_path)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/peter/PycharmProjects/litestar-import-test/.venv/lib/python3.12/site-packages/litestar/cli/_utils.py", line 271, in _load_app_from_path
    module = importlib.import_module(module_path)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/peter/.pyenv/versions/3.12.2/lib/python3.12/importlib/__init__.py", line 90, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 995, in exec_module
  File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
  File "/home/peter/PycharmProjects/litestar-import-test/app.py", line 14, in <module>
    app = Litestar([index, get_book])
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/peter/PycharmProjects/litestar-import-test/.venv/lib/python3.12/site-packages/litestar/app.py", line 361, in __init__
    plugins=self._get_default_plugins(list(plugins or [])),
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/peter/PycharmProjects/litestar-import-test/.venv/lib/python3.12/site-packages/litestar/app.py", line 521, in _get_default_plugins
    from litestar.contrib.pydantic import (
  File "/home/peter/PycharmProjects/litestar-import-test/.venv/lib/python3.12/site-packages/litestar/contrib/pydantic/__init__.py", line 8, in <module>
    from .pydantic_dto_factory import PydanticDTO
  File "/home/peter/PycharmProjects/litestar-import-test/.venv/lib/python3.12/site-packages/litestar/contrib/pydantic/pydantic_dto_factory.py", line 50, in <module>
    pydantic_v2.JsonValue: Any,
    ^^^^^^^^^^^^^^^^^^^^^
AttributeError: '_EmptyEnum' object has no attribute 'JsonValue'
```
Nvm, confirmed.

Resolving patch:
diff --git a/litestar/contrib/pydantic/pydantic_dto_factory.py b/litestar/contrib/pydantic/pydantic_dto_factory.py
--- a/litestar/contrib/pydantic/pydantic_dto_factory.py
+++ b/litestar/contrib/pydantic/pydantic_dto_factory.py
@@ -46,18 +46,24 @@
 
 __all__ = ("PydanticDTO",)
 
-_down_types = {
-    pydantic_v2.JsonValue: Any,
+_down_types: dict[Any, Any] = {
     pydantic_v1.EmailStr: str,
-    pydantic_v2.EmailStr: str,
     pydantic_v1.IPvAnyAddress: str,
-    pydantic_v2.IPvAnyAddress: str,
     pydantic_v1.IPvAnyInterface: str,
-    pydantic_v2.IPvAnyInterface: str,
     pydantic_v1.IPvAnyNetwork: str,
-    pydantic_v2.IPvAnyNetwork: str,
 }
 
+if pydantic_v2 is not Empty:  # type: ignore[comparison-overlap]  # pragma: no cover
+    _down_types.update(
+        {
+            pydantic_v2.JsonValue: Any,
+            pydantic_v2.EmailStr: str,
+            pydantic_v2.IPvAnyAddress: str,
+            pydantic_v2.IPvAnyInterface: str,
+            pydantic_v2.IPvAnyNetwork: str,
+        }
+    )
+
 
 def convert_validation_error(validation_error: ValidationErrorV1 | ValidationErrorV2) -> list[dict[str, Any]]:
     error_list = validation_error.errors()


Test patch: