Repository: litestar-org/litestar

Issue:
Bug: Improve warning message for ambiguous defaults
### Description

We should enhance the warning messages generated from #3280 in 2.8.

As a user just now upgrading and hitting this, across probably ~40 instances of that warning, this isn't terribly helpful:

```py
.venv/lib/python3.12/site-packages/litestar/typing.py:597: LitestarWarning: Ambiguous default values for annotation 'typing.Annotated[str, ParameterKwarg(examples=None, external_docs=None, content_encoding=None, default='+a', title=None, description='A string expression to filter something.', const=None, gt=None, ge=None, lt=None, le=None, multiple_of=None, min_items=None, max_items=None, min_length=None, max_length=None, pattern=None, lower_case=None, upper_case=None, format=None, enum=None, read_only=None, schema_extra=None, annotation=<_EmptyEnum.EMPTY: 0>, header=None, cookie=None, query=None, required=True)]'. The default value ''+a'' set inside the parameter annotation differs from the parameter default value '<_EmptyEnum.EMPTY: 0>'
  return cls.from_annotation(
.venv/lib/python3.12/site-packages/litestar/typing.py:597: LitestarWarning: Ambiguous default values for annotation 'typing.Annotated[bool, ParameterKwarg(examples=None, external_docs=None, content_encoding=None, default=False, title=None, description='Return something', const=None, gt=None, ge=None, lt=None, le=None, multiple_of=None, min_items=None, max_items=None, min_length=None, max_length=None, pattern=None, lower_case=None, upper_case=None, format=None, enum=None, read_only=None, schema_extra=None, annotation=<_EmptyEnum.EMPTY: 0>, header=None, cookie=None, query=None, required=None)]'. The default value 'False' set inside the parameter annotation differs from the parameter default value '<_EmptyEnum.EMPTY: 0>'
  return cls.from_annotation(
```


### URL to code causing the issue

_No response_

### MCVE

_No response_

### Steps to reproduce

```bash
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
```


### Screenshots

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


### Logs

_No response_

### Litestar Version

2.8

### Platform

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



Discussion:

Resolving patch:
diff --git a/litestar/typing.py b/litestar/typing.py
--- a/litestar/typing.py
+++ b/litestar/typing.py
@@ -512,13 +512,11 @@ def from_annotation(cls, annotation: Any, **kwargs: Any) -> FieldDefinition:
         if not kwargs.get("kwarg_definition"):
             if isinstance(kwargs.get("default"), (KwargDefinition, DependencyKwarg)):
                 kwargs["kwarg_definition"] = kwargs.pop("default")
-            elif any(isinstance(v, (KwargDefinition, DependencyKwarg)) for v in metadata):
-                kwarg_definition = kwargs["kwarg_definition"] = next(  # pragma: no cover
-                    # see https://github.com/nedbat/coveragepy/issues/475
-                    v
-                    for v in metadata
-                    if isinstance(v, (KwargDefinition, DependencyKwarg))
-                )
+            elif kwarg_definition := next(
+                (v for v in metadata if isinstance(v, (KwargDefinition, DependencyKwarg))), None
+            ):
+                kwargs["kwarg_definition"] = kwarg_definition
+
                 if kwarg_definition.default is not Empty:
                     warnings.warn(
                         f"Deprecated default value specification for annotation '{annotation}'. Setting defaults "
@@ -529,7 +527,7 @@ def from_annotation(cls, annotation: Any, **kwargs: Any) -> FieldDefinition:
                         category=DeprecationWarning,
                         stacklevel=2,
                     )
-                    if "default" in kwargs and kwarg_definition.default != kwargs["default"]:
+                    if kwargs.get("default", Empty) is not Empty and kwarg_definition.default != kwargs["default"]:
                         warnings.warn(
                             f"Ambiguous default values for annotation '{annotation}'. The default value "
                             f"'{kwarg_definition.default!r}' set inside the parameter annotation differs from the "


Test patch:
diff --git a/tests/unit/test_typing.py b/tests/unit/test_typing.py
--- a/tests/unit/test_typing.py
+++ b/tests/unit/test_typing.py
@@ -9,6 +9,7 @@
 import pytest
 from typing_extensions import Annotated, NotRequired, Required, TypedDict, get_type_hints
 
+from litestar import get
 from litestar.exceptions import LitestarWarning
 from litestar.params import DependencyKwarg, KwargDefinition, Parameter, ParameterKwarg
 from litestar.typing import FieldDefinition, _unpack_predicate
@@ -461,3 +462,17 @@ def test_warn_ambiguous_default_values() -> None:
 def test_warn_defaults_inside_parameter_definition() -> None:
     with pytest.warns(DeprecationWarning, match="Deprecated default value specification"):
         FieldDefinition.from_annotation(Annotated[int, Parameter(default=1)], default=1)
+
+
+def test_warn_default_inside_kwarg_definition_and_default_empty() -> None:
+    with pytest.warns() as warnings:
+
+        @get(sync_to_thread=False)
+        def handler(foo: Annotated[int, Parameter(default=1)]) -> None:
+            pass
+
+        _ = handler.parsed_fn_signature
+
+    (record,) = warnings
+    assert record.category == DeprecationWarning
+    assert "Deprecated default value specification" in str(record.message)