Repository: litestar-org/litestar

Issue:
Enhancement: allow custom exception types in the `ExceptionHandlersMap` and `ExceptionHandler` type
### Summary

The current typing of the `ExceptionHandler` and `ExceptionHandlersMap` causes type checkers to complain if the type of the exception on an exception handler is a subclass of `Exception`. This can be resolved by making the exception type generic (bound to `Exception`).

### Basic Example

# Current Behavior

```python
from litestar import Litestar, Request, Response

class CustomException(Exception):
    ...

def handle_exc(req: Request, exc: CustomException) -> Response:
    ...

app = Litestar([], exception_handlers={Exception: handle_exc})
```

MyPy error:
```
error: Dict entry 0 has incompatible type "Type[CustomException]": "Callable[[Request[Any, Any, Any], CustomException], Response[Any]]"; expected "Union[int, Type[Exception]]": "Callable[[Request[Any, Any, Any], Exception], Response[Any]]"  [dict-item]
```

Expected Behavior:
No errors from type checkers.



### Drawbacks and Impact

_No response_

### Unresolved questions

_No response_

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



Discussion:

Resolving patch:
diff --git a/litestar/types/callable_types.py b/litestar/types/callable_types.py
--- a/litestar/types/callable_types.py
+++ b/litestar/types/callable_types.py
@@ -1,6 +1,6 @@
 from __future__ import annotations
 
-from typing import TYPE_CHECKING, Any, AsyncGenerator, Awaitable, Callable, Generator
+from typing import TYPE_CHECKING, Any, AsyncGenerator, Awaitable, Callable, Generator, TypeVar
 
 if TYPE_CHECKING:
     from typing_extensions import TypeAlias
@@ -17,8 +17,9 @@
     from litestar.types.internal_types import PathParameterDefinition
     from litestar.types.protocols import Logger
 
+ExceptionT = TypeVar("ExceptionT", bound=Exception)
 
-AfterExceptionHookHandler: TypeAlias = "Callable[[Exception, Scope], SyncOrAsyncUnion[None]]"
+AfterExceptionHookHandler: TypeAlias = "Callable[[ExceptionT, Scope], SyncOrAsyncUnion[None]]"
 AfterRequestHookHandler: TypeAlias = (
     "Callable[[ASGIApp], SyncOrAsyncUnion[ASGIApp]] | Callable[[Response], SyncOrAsyncUnion[Response]]"
 )
@@ -29,7 +30,7 @@
 BeforeMessageSendHookHandler: TypeAlias = "Callable[[Message, Scope], SyncOrAsyncUnion[None]]"
 BeforeRequestHookHandler: TypeAlias = "Callable[[Request], Any | Awaitable[Any]]"
 CacheKeyBuilder: TypeAlias = "Callable[[Request], str]"
-ExceptionHandler: TypeAlias = "Callable[[Request, Exception], Response]"
+ExceptionHandler: TypeAlias = "Callable[[Request, ExceptionT], Response]"
 ExceptionLoggingHandler: TypeAlias = "Callable[[Logger, Scope, list[str]], None]"
 GetLogger: TypeAlias = "Callable[..., Logger]"
 Guard: TypeAlias = "Callable[[ASGIConnection, BaseRouteHandler], SyncOrAsyncUnion[None]]"


Test patch: