Repository: encode/starlette

Issue:
mypy complains about `exception_handlers` dict with `HTTPException` key
### Checklist

<!-- Please make sure you check all these items before submitting your bug report. -->

- [x] The bug is reproducible against the latest release and/or `master`.
- [x] There are no similar issues or pull requests to fix it yet.

Related, but not the same:
* #1034 
* #1139 
* #949

### Describe the bug

<!-- A clear and concise description of what the bug is. -->

### To reproduce

Given an example.py: 

```python
from starlette.applications import Starlette
from starlette.exceptions import HTTPException
from starlette.requests import Request
from starlette.responses import Response
from starlette.routing import Route

def handle_http_exception(request: Request, exc: HTTPException) -> Response:
    return Response('foo')


exception_handlers = {
    HTTPException: handle_http_exception
}
app = Starlette(exception_handlers=exception_handlers)
```

### Expected behavior

Mypy does not present an error.

### Actual behavior

```console
$ mypy example.py
example.py:14: error: Argument "exception_handlers" to "Starlette" has incompatible type "Dict[Type[HTTPException], Callable[[Request, HTTPException], Response]]"; expected "Optional[Dict[Union[int, Type[Exception]], Callable[..., Any]]]"
Found 1 error in 1 file (checked 1 source file)
```

### Debugging material

None

### Environment

- OS: Debian (`Linux ... 4.19.0-14-amd64 #1 SMP Debian 4.19.171-2 (2021-01-30) x86_64 GNU/Linux`)
- Python version: `Python 3.9.1`
- Starlette version: `starlette==0.14.2`

### Additional context

For propriety:

https://github.com/encode/starlette/blob/master/docs/exceptions.md#L42

https://github.com/encode/starlette/blob/ed73b97c9b8f4aa527eed88032c04ce9fe109a3c/starlette/applications.py#L44

I think this is a Starlette issue and not a mypy issue; apologies if I'm wrong here.


Discussion:
Forgot to add that I'm observing this on `mypy==0.800`

Resolving patch:
diff --git a/starlette/applications.py b/starlette/applications.py
--- a/starlette/applications.py
+++ b/starlette/applications.py
@@ -5,6 +5,8 @@
 from starlette.middleware import Middleware
 from starlette.middleware.base import BaseHTTPMiddleware
 from starlette.middleware.errors import ServerErrorMiddleware
+from starlette.requests import Request
+from starlette.responses import Response
 from starlette.routing import BaseRoute, Router
 from starlette.types import ASGIApp, Receive, Scope, Send
 
@@ -23,7 +25,7 @@ class Starlette:
     any uncaught errors occurring anywhere in the entire stack.
     `ExceptionMiddleware` is added as the very innermost middleware, to deal
     with handled exception cases occurring in the routing or endpoints.
-    * **exception_handlers** - A dictionary mapping either integer status codes,
+    * **exception_handlers** - A mapping of either integer status codes,
     or exception class types onto callables which handle the exceptions.
     Exception handler callables should be of the form
     `handler(request, exc) -> response` and may be be either standard functions, or
@@ -41,8 +43,8 @@ def __init__(
         debug: bool = False,
         routes: typing.Sequence[BaseRoute] = None,
         middleware: typing.Sequence[Middleware] = None,
-        exception_handlers: typing.Dict[
-            typing.Union[int, typing.Type[Exception]], typing.Callable
+        exception_handlers: typing.Mapping[
+            typing.Any, typing.Callable[[Request, Exception], Response]
         ] = None,
         on_startup: typing.Sequence[typing.Callable] = None,
         on_shutdown: typing.Sequence[typing.Callable] = None,
@@ -68,7 +70,9 @@ def __init__(
     def build_middleware_stack(self) -> ASGIApp:
         debug = self.debug
         error_handler = None
-        exception_handlers = {}
+        exception_handlers: typing.Dict[
+            typing.Any, typing.Callable[[Request, Exception], Response]
+        ] = {}
 
         for key, value in self.exception_handlers.items():
             if key in (500, Exception):
diff --git a/starlette/exceptions.py b/starlette/exceptions.py
--- a/starlette/exceptions.py
+++ b/starlette/exceptions.py
@@ -22,7 +22,12 @@ def __repr__(self) -> str:
 
 class ExceptionMiddleware:
     def __init__(
-        self, app: ASGIApp, handlers: dict = None, debug: bool = False
+        self,
+        app: ASGIApp,
+        handlers: typing.Mapping[
+            typing.Any, typing.Callable[[Request, Exception], Response]
+        ] = None,
+        debug: bool = False,
     ) -> None:
         self.app = app
         self.debug = debug  # TODO: We ought to handle 404 cases if debug is set.
@@ -37,7 +42,7 @@ def __init__(
     def add_exception_handler(
         self,
         exc_class_or_status_code: typing.Union[int, typing.Type[Exception]],
-        handler: typing.Callable,
+        handler: typing.Callable[[Request, Exception], Response],
     ) -> None:
         if isinstance(exc_class_or_status_code, int):
             self._status_handlers[exc_class_or_status_code] = handler


Test patch: