We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Async context_value callable is not supported
Our ASGI GraphQL app supports callable `context_value`, but it doesnt support `async` callable, so it's impossible to do any async work in it like get user form database. 😂

Example:

```python
async def resolve_context(request: Request) -> Dict:
    return {"user": await get_user_for_request(request)}


graphql_app = GraphQL(schema, context_value=resolve_context)
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ariadne/asgi.py`
Content:
```
1 import asyncio
2 import json
3 from typing import (
4     Any,
5     AsyncGenerator,
6     Callable,
7     Dict,
8     List,
9     Optional,
10     Union,
11     Type,
12     cast,
13 )
14 
15 from graphql import GraphQLError, GraphQLSchema
16 from graphql.execution import MiddlewareManager
17 from starlette.datastructures import UploadFile
18 from starlette.requests import Request
19 from starlette.responses import HTMLResponse, JSONResponse, PlainTextResponse, Response
20 from starlette.types import Receive, Scope, Send
21 from starlette.websockets import WebSocket, WebSocketState, WebSocketDisconnect
22 
23 from .constants import DATA_TYPE_JSON, DATA_TYPE_MULTIPART, PLAYGROUND_HTML
24 from .exceptions import HttpBadRequestError, HttpError
25 from .file_uploads import combine_multipart_data
26 from .format_error import format_error
27 from .graphql import graphql, subscribe
28 from .logger import log_error
29 from .types import ContextValue, ErrorFormatter, Extension, RootValue
30 
31 GQL_CONNECTION_INIT = "connection_init"  # Client -> Server
32 GQL_CONNECTION_ACK = "connection_ack"  # Server -> Client
33 GQL_CONNECTION_ERROR = "connection_error"  # Server -> Client
34 
35 # NOTE: The keep alive message type does not follow the standard due to connection optimizations
36 GQL_CONNECTION_KEEP_ALIVE = "ka"  # Server -> Client
37 
38 GQL_CONNECTION_TERMINATE = "connection_terminate"  # Client -> Server
39 GQL_START = "start"  # Client -> Server
40 GQL_DATA = "data"  # Server -> Client
41 GQL_ERROR = "error"  # Server -> Client
42 GQL_COMPLETE = "complete"  # Server -> Client
43 GQL_STOP = "stop"  # Client -> Server
44 
45 ExtensionList = Optional[List[Type[Extension]]]
46 
47 
48 class GraphQL:
49     def __init__(
50         self,
51         schema: GraphQLSchema,
52         *,
53         context_value: Optional[ContextValue] = None,
54         root_value: Optional[RootValue] = None,
55         debug: bool = False,
56         logger: Optional[str] = None,
57         error_formatter: ErrorFormatter = format_error,
58         extensions: Union[Callable[[Any], ExtensionList], ExtensionList] = None,
59         middleware: Optional[MiddlewareManager] = None,
60         keepalive: float = None,
61     ):
62         self.context_value = context_value
63         self.root_value = root_value
64         self.debug = debug
65         self.logger = logger
66         self.error_formatter = error_formatter
67         self.extensions = extensions
68         self.middleware = middleware
69         self.keepalive = keepalive
70         self.schema = schema
71 
72     async def __call__(self, scope: Scope, receive: Receive, send: Send):
73         if scope["type"] == "http":
74             await self.handle_http(scope=scope, receive=receive, send=send)
75         elif scope["type"] == "websocket":
76             await self.handle_websocket(scope=scope, receive=receive, send=send)
77         else:
78             raise ValueError("Unknown scope type: %r" % (scope["type"],))
79 
80     async def get_context_for_request(self, request: Any) -> Any:
81         if callable(self.context_value):
82             return self.context_value(request)
83         return self.context_value or {"request": request}
84 
85     async def get_extensions_for_request(self, request: Any) -> ExtensionList:
86         if callable(self.extensions):
87             return self.extensions(request)
88         return self.extensions
89 
90     async def handle_http(self, scope: Scope, receive: Receive, send: Send):
91         request = Request(scope=scope, receive=receive)
92         if request.method == "GET":
93             response = await self.render_playground(request)
94         elif request.method == "POST":
95             response = await self.graphql_http_server(request)
96         else:
97             response = Response(status_code=405)
98         await response(scope, receive, send)
99 
100     async def handle_websocket(self, scope: Scope, receive: Receive, send: Send):
101         websocket = WebSocket(scope=scope, receive=receive, send=send)
102         await self.websocket_server(websocket)
103 
104     async def render_playground(  # pylint: disable=unused-argument
105         self, request: Request
106     ) -> Response:
107         return HTMLResponse(PLAYGROUND_HTML)
108 
109     async def graphql_http_server(self, request: Request) -> Response:
110         try:
111             data = await self.extract_data_from_request(request)
112         except HttpError as error:
113             return PlainTextResponse(error.message or error.status, status_code=400)
114 
115         context_value = await self.get_context_for_request(request)
116         extensions = await self.get_extensions_for_request(request)
117 
118         success, response = await graphql(
119             self.schema,
120             data,
121             context_value=context_value,
122             root_value=self.root_value,
123             debug=self.debug,
124             logger=self.logger,
125             error_formatter=self.error_formatter,
126             extensions=extensions,
127         )
128         status_code = 200 if success else 400
129         return JSONResponse(response, status_code=status_code)
130 
131     async def extract_data_from_request(self, request: Request):
132         content_type = request.headers.get("Content-Type", "")
133         content_type = content_type.split(";")[0]
134 
135         if content_type == DATA_TYPE_JSON:
136             return await self.extract_data_from_json_request(request)
137         if content_type == DATA_TYPE_MULTIPART:
138             return await self.extract_data_from_multipart_request(request)
139 
140         raise HttpBadRequestError(
141             "Posted content must be of type {} or {}".format(
142                 DATA_TYPE_JSON, DATA_TYPE_MULTIPART
143             )
144         )
145 
146     async def extract_data_from_json_request(self, request: Request):
147         try:
148             return await request.json()
149         except (TypeError, ValueError):
150             raise HttpBadRequestError("Request body is not a valid JSON")
151 
152     async def extract_data_from_multipart_request(self, request: Request):
153         try:
154             request_body = await request.form()
155         except ValueError:
156             raise HttpBadRequestError("Request body is not a valid multipart/form-data")
157 
158         try:
159             operations = json.loads(request_body.get("operations"))
160         except (TypeError, ValueError):
161             raise HttpBadRequestError(
162                 "Request 'operations' multipart field is not a valid JSON"
163             )
164         try:
165             files_map = json.loads(request_body.get("map"))
166         except (TypeError, ValueError):
167             raise HttpBadRequestError(
168                 "Request 'map' multipart field is not a valid JSON"
169             )
170 
171         request_files = {
172             key: value
173             for key, value in request_body.items()
174             if isinstance(value, UploadFile)
175         }
176 
177         return combine_multipart_data(operations, files_map, request_files)
178 
179     async def websocket_server(self, websocket: WebSocket) -> None:
180         subscriptions: Dict[str, AsyncGenerator] = {}
181         await websocket.accept("graphql-ws")
182         try:
183             while (
184                 websocket.client_state != WebSocketState.DISCONNECTED
185                 and websocket.application_state != WebSocketState.DISCONNECTED
186             ):
187                 message = await websocket.receive_json()
188                 await self.handle_websocket_message(message, websocket, subscriptions)
189         except WebSocketDisconnect:
190             pass
191         finally:
192             for operation_id in subscriptions:
193                 await subscriptions[operation_id].aclose()
194 
195     async def handle_websocket_message(
196         self,
197         message: dict,
198         websocket: WebSocket,
199         subscriptions: Dict[str, AsyncGenerator],
200     ):
201         operation_id = cast(str, message.get("id"))
202         message_type = cast(str, message.get("type"))
203 
204         if message_type == GQL_CONNECTION_INIT:
205             await websocket.send_json({"type": GQL_CONNECTION_ACK})
206             asyncio.ensure_future(self.keep_websocket_alive(websocket))
207         elif message_type == GQL_CONNECTION_TERMINATE:
208             await websocket.close()
209         elif message_type == GQL_START:
210             await self.start_websocket_subscription(
211                 message.get("payload"), operation_id, websocket, subscriptions
212             )
213         elif message_type == GQL_STOP:
214             if operation_id in subscriptions:
215                 await subscriptions[operation_id].aclose()
216                 del subscriptions[operation_id]
217 
218     async def keep_websocket_alive(self, websocket: WebSocket):
219         if not self.keepalive:
220             return
221         while websocket.application_state != WebSocketState.DISCONNECTED:
222             try:
223                 await websocket.send_json({"type": GQL_CONNECTION_KEEP_ALIVE})
224             except WebSocketDisconnect:
225                 return
226             await asyncio.sleep(self.keepalive)
227 
228     async def start_websocket_subscription(
229         self,
230         data: Any,
231         operation_id: str,
232         websocket: WebSocket,
233         subscriptions: Dict[str, AsyncGenerator],
234     ):
235         context_value = await self.get_context_for_request(websocket)
236         success, results = await subscribe(
237             self.schema,
238             data,
239             context_value=context_value,
240             root_value=self.root_value,
241             debug=self.debug,
242             logger=self.logger,
243             error_formatter=self.error_formatter,
244         )
245         if not success:
246             results = cast(List[dict], results)
247             await websocket.send_json(
248                 {"type": GQL_ERROR, "id": operation_id, "payload": results[0]}
249             )
250         else:
251             results = cast(AsyncGenerator, results)
252             subscriptions[operation_id] = results
253             asyncio.ensure_future(
254                 self.observe_async_results(results, operation_id, websocket)
255             )
256 
257     async def observe_async_results(
258         self, results: AsyncGenerator, operation_id: str, websocket: WebSocket
259     ) -> None:
260         try:
261             async for result in results:
262                 payload = {}
263                 if result.data:
264                     payload["data"] = result.data
265                 if result.errors:
266                     for error in result.errors:
267                         log_error(error, self.logger)
268                     payload["errors"] = [
269                         self.error_formatter(error, self.debug)
270                         for error in result.errors
271                     ]
272                 await websocket.send_json(
273                     {"type": GQL_DATA, "id": operation_id, "payload": payload}
274                 )
275         except Exception as error:
276             if not isinstance(error, GraphQLError):
277                 error = GraphQLError(str(error), original_error=error)
278             log_error(error, self.logger)
279             payload = {"errors": [self.error_formatter(error, self.debug)]}
280             await websocket.send_json(
281                 {"type": GQL_DATA, "id": operation_id, "payload": payload}
282             )
283 
284         if (
285             websocket.client_state != WebSocketState.DISCONNECTED
286             and websocket.application_state != WebSocketState.DISCONNECTED
287         ):
288             await websocket.send_json({"type": GQL_COMPLETE, "id": operation_id})
289
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/ariadne/asgi.py b/ariadne/asgi.py
--- a/ariadne/asgi.py
+++ b/ariadne/asgi.py
@@ -1,5 +1,6 @@
 import asyncio
 import json
+from inspect import isawaitable
 from typing import (
     Any,
     AsyncGenerator,
@@ -79,7 +80,11 @@
 
     async def get_context_for_request(self, request: Any) -> Any:
         if callable(self.context_value):
-            return self.context_value(request)
+            context = self.context_value(request)
+            if isawaitable(context):
+                context = await context
+            return context
+
         return self.context_value or {"request": request}
 
     async def get_extensions_for_request(self, request: Any) -> ExtensionList:

{"golden_diff": "diff --git a/ariadne/asgi.py b/ariadne/asgi.py\n--- a/ariadne/asgi.py\n+++ b/ariadne/asgi.py\n@@ -1,5 +1,6 @@\n import asyncio\n import json\n+from inspect import isawaitable\n from typing import (\n     Any,\n     AsyncGenerator,\n@@ -79,7 +80,11 @@\n \n     async def get_context_for_request(self, request: Any) -> Any:\n         if callable(self.context_value):\n-            return self.context_value(request)\n+            context = self.context_value(request)\n+            if isawaitable(context):\n+                context = await context\n+            return context\n+\n         return self.context_value or {\"request\": request}\n \n     async def get_extensions_for_request(self, request: Any) -> ExtensionList:\n", "issue": "Async context_value callable is not supported\nOur ASGI GraphQL app supports callable `context_value`, but it doesnt support `async` callable, so it's impossible to do any async work in it like get user form database. \ud83d\ude02\r\n\r\nExample:\r\n\r\n```python\r\nasync def resolve_context(request: Request) -> Dict:\r\n    return {\"user\": await get_user_for_request(request)}\r\n\r\n\r\ngraphql_app = GraphQL(schema, context_value=resolve_context)\r\n```\n", "before_files": [{"content": "import asyncio\nimport json\nfrom typing import (\n    Any,\n    AsyncGenerator,\n    Callable,\n    Dict,\n    List,\n    Optional,\n    Union,\n    Type,\n    cast,\n)\n\nfrom graphql import GraphQLError, GraphQLSchema\nfrom graphql.execution import MiddlewareManager\nfrom starlette.datastructures import UploadFile\nfrom starlette.requests import Request\nfrom starlette.responses import HTMLResponse, JSONResponse, PlainTextResponse, Response\nfrom starlette.types import Receive, Scope, Send\nfrom starlette.websockets import WebSocket, WebSocketState, WebSocketDisconnect\n\nfrom .constants import DATA_TYPE_JSON, DATA_TYPE_MULTIPART, PLAYGROUND_HTML\nfrom .exceptions import HttpBadRequestError, HttpError\nfrom .file_uploads import combine_multipart_data\nfrom .format_error import format_error\nfrom .graphql import graphql, subscribe\nfrom .logger import log_error\nfrom .types import ContextValue, ErrorFormatter, Extension, RootValue\n\nGQL_CONNECTION_INIT = \"connection_init\"  # Client -> Server\nGQL_CONNECTION_ACK = \"connection_ack\"  # Server -> Client\nGQL_CONNECTION_ERROR = \"connection_error\"  # Server -> Client\n\n# NOTE: The keep alive message type does not follow the standard due to connection optimizations\nGQL_CONNECTION_KEEP_ALIVE = \"ka\"  # Server -> Client\n\nGQL_CONNECTION_TERMINATE = \"connection_terminate\"  # Client -> Server\nGQL_START = \"start\"  # Client -> Server\nGQL_DATA = \"data\"  # Server -> Client\nGQL_ERROR = \"error\"  # Server -> Client\nGQL_COMPLETE = \"complete\"  # Server -> Client\nGQL_STOP = \"stop\"  # Client -> Server\n\nExtensionList = Optional[List[Type[Extension]]]\n\n\nclass GraphQL:\n    def __init__(\n        self,\n        schema: GraphQLSchema,\n        *,\n        context_value: Optional[ContextValue] = None,\n        root_value: Optional[RootValue] = None,\n        debug: bool = False,\n        logger: Optional[str] = None,\n        error_formatter: ErrorFormatter = format_error,\n        extensions: Union[Callable[[Any], ExtensionList], ExtensionList] = None,\n        middleware: Optional[MiddlewareManager] = None,\n        keepalive: float = None,\n    ):\n        self.context_value = context_value\n        self.root_value = root_value\n        self.debug = debug\n        self.logger = logger\n        self.error_formatter = error_formatter\n        self.extensions = extensions\n        self.middleware = middleware\n        self.keepalive = keepalive\n        self.schema = schema\n\n    async def __call__(self, scope: Scope, receive: Receive, send: Send):\n        if scope[\"type\"] == \"http\":\n            await self.handle_http(scope=scope, receive=receive, send=send)\n        elif scope[\"type\"] == \"websocket\":\n            await self.handle_websocket(scope=scope, receive=receive, send=send)\n        else:\n            raise ValueError(\"Unknown scope type: %r\" % (scope[\"type\"],))\n\n    async def get_context_for_request(self, request: Any) -> Any:\n        if callable(self.context_value):\n            return self.context_value(request)\n        return self.context_value or {\"request\": request}\n\n    async def get_extensions_for_request(self, request: Any) -> ExtensionList:\n        if callable(self.extensions):\n            return self.extensions(request)\n        return self.extensions\n\n    async def handle_http(self, scope: Scope, receive: Receive, send: Send):\n        request = Request(scope=scope, receive=receive)\n        if request.method == \"GET\":\n            response = await self.render_playground(request)\n        elif request.method == \"POST\":\n            response = await self.graphql_http_server(request)\n        else:\n            response = Response(status_code=405)\n        await response(scope, receive, send)\n\n    async def handle_websocket(self, scope: Scope, receive: Receive, send: Send):\n        websocket = WebSocket(scope=scope, receive=receive, send=send)\n        await self.websocket_server(websocket)\n\n    async def render_playground(  # pylint: disable=unused-argument\n        self, request: Request\n    ) -> Response:\n        return HTMLResponse(PLAYGROUND_HTML)\n\n    async def graphql_http_server(self, request: Request) -> Response:\n        try:\n            data = await self.extract_data_from_request(request)\n        except HttpError as error:\n            return PlainTextResponse(error.message or error.status, status_code=400)\n\n        context_value = await self.get_context_for_request(request)\n        extensions = await self.get_extensions_for_request(request)\n\n        success, response = await graphql(\n            self.schema,\n            data,\n            context_value=context_value,\n            root_value=self.root_value,\n            debug=self.debug,\n            logger=self.logger,\n            error_formatter=self.error_formatter,\n            extensions=extensions,\n        )\n        status_code = 200 if success else 400\n        return JSONResponse(response, status_code=status_code)\n\n    async def extract_data_from_request(self, request: Request):\n        content_type = request.headers.get(\"Content-Type\", \"\")\n        content_type = content_type.split(\";\")[0]\n\n        if content_type == DATA_TYPE_JSON:\n            return await self.extract_data_from_json_request(request)\n        if content_type == DATA_TYPE_MULTIPART:\n            return await self.extract_data_from_multipart_request(request)\n\n        raise HttpBadRequestError(\n            \"Posted content must be of type {} or {}\".format(\n                DATA_TYPE_JSON, DATA_TYPE_MULTIPART\n            )\n        )\n\n    async def extract_data_from_json_request(self, request: Request):\n        try:\n            return await request.json()\n        except (TypeError, ValueError):\n            raise HttpBadRequestError(\"Request body is not a valid JSON\")\n\n    async def extract_data_from_multipart_request(self, request: Request):\n        try:\n            request_body = await request.form()\n        except ValueError:\n            raise HttpBadRequestError(\"Request body is not a valid multipart/form-data\")\n\n        try:\n            operations = json.loads(request_body.get(\"operations\"))\n        except (TypeError, ValueError):\n            raise HttpBadRequestError(\n                \"Request 'operations' multipart field is not a valid JSON\"\n            )\n        try:\n            files_map = json.loads(request_body.get(\"map\"))\n        except (TypeError, ValueError):\n            raise HttpBadRequestError(\n                \"Request 'map' multipart field is not a valid JSON\"\n            )\n\n        request_files = {\n            key: value\n            for key, value in request_body.items()\n            if isinstance(value, UploadFile)\n        }\n\n        return combine_multipart_data(operations, files_map, request_files)\n\n    async def websocket_server(self, websocket: WebSocket) -> None:\n        subscriptions: Dict[str, AsyncGenerator] = {}\n        await websocket.accept(\"graphql-ws\")\n        try:\n            while (\n                websocket.client_state != WebSocketState.DISCONNECTED\n                and websocket.application_state != WebSocketState.DISCONNECTED\n            ):\n                message = await websocket.receive_json()\n                await self.handle_websocket_message(message, websocket, subscriptions)\n        except WebSocketDisconnect:\n            pass\n        finally:\n            for operation_id in subscriptions:\n                await subscriptions[operation_id].aclose()\n\n    async def handle_websocket_message(\n        self,\n        message: dict,\n        websocket: WebSocket,\n        subscriptions: Dict[str, AsyncGenerator],\n    ):\n        operation_id = cast(str, message.get(\"id\"))\n        message_type = cast(str, message.get(\"type\"))\n\n        if message_type == GQL_CONNECTION_INIT:\n            await websocket.send_json({\"type\": GQL_CONNECTION_ACK})\n            asyncio.ensure_future(self.keep_websocket_alive(websocket))\n        elif message_type == GQL_CONNECTION_TERMINATE:\n            await websocket.close()\n        elif message_type == GQL_START:\n            await self.start_websocket_subscription(\n                message.get(\"payload\"), operation_id, websocket, subscriptions\n            )\n        elif message_type == GQL_STOP:\n            if operation_id in subscriptions:\n                await subscriptions[operation_id].aclose()\n                del subscriptions[operation_id]\n\n    async def keep_websocket_alive(self, websocket: WebSocket):\n        if not self.keepalive:\n            return\n        while websocket.application_state != WebSocketState.DISCONNECTED:\n            try:\n                await websocket.send_json({\"type\": GQL_CONNECTION_KEEP_ALIVE})\n            except WebSocketDisconnect:\n                return\n            await asyncio.sleep(self.keepalive)\n\n    async def start_websocket_subscription(\n        self,\n        data: Any,\n        operation_id: str,\n        websocket: WebSocket,\n        subscriptions: Dict[str, AsyncGenerator],\n    ):\n        context_value = await self.get_context_for_request(websocket)\n        success, results = await subscribe(\n            self.schema,\n            data,\n            context_value=context_value,\n            root_value=self.root_value,\n            debug=self.debug,\n            logger=self.logger,\n            error_formatter=self.error_formatter,\n        )\n        if not success:\n            results = cast(List[dict], results)\n            await websocket.send_json(\n                {\"type\": GQL_ERROR, \"id\": operation_id, \"payload\": results[0]}\n            )\n        else:\n            results = cast(AsyncGenerator, results)\n            subscriptions[operation_id] = results\n            asyncio.ensure_future(\n                self.observe_async_results(results, operation_id, websocket)\n            )\n\n    async def observe_async_results(\n        self, results: AsyncGenerator, operation_id: str, websocket: WebSocket\n    ) -> None:\n        try:\n            async for result in results:\n                payload = {}\n                if result.data:\n                    payload[\"data\"] = result.data\n                if result.errors:\n                    for error in result.errors:\n                        log_error(error, self.logger)\n                    payload[\"errors\"] = [\n                        self.error_formatter(error, self.debug)\n                        for error in result.errors\n                    ]\n                await websocket.send_json(\n                    {\"type\": GQL_DATA, \"id\": operation_id, \"payload\": payload}\n                )\n        except Exception as error:\n            if not isinstance(error, GraphQLError):\n                error = GraphQLError(str(error), original_error=error)\n            log_error(error, self.logger)\n            payload = {\"errors\": [self.error_formatter(error, self.debug)]}\n            await websocket.send_json(\n                {\"type\": GQL_DATA, \"id\": operation_id, \"payload\": payload}\n            )\n\n        if (\n            websocket.client_state != WebSocketState.DISCONNECTED\n            and websocket.application_state != WebSocketState.DISCONNECTED\n        ):\n            await websocket.send_json({\"type\": GQL_COMPLETE, \"id\": operation_id})\n", "path": "ariadne/asgi.py"}], "after_files": [{"content": "import asyncio\nimport json\nfrom inspect import isawaitable\nfrom typing import (\n    Any,\n    AsyncGenerator,\n    Callable,\n    Dict,\n    List,\n    Optional,\n    Union,\n    Type,\n    cast,\n)\n\nfrom graphql import GraphQLError, GraphQLSchema\nfrom graphql.execution import MiddlewareManager\nfrom starlette.datastructures import UploadFile\nfrom starlette.requests import Request\nfrom starlette.responses import HTMLResponse, JSONResponse, PlainTextResponse, Response\nfrom starlette.types import Receive, Scope, Send\nfrom starlette.websockets import WebSocket, WebSocketState, WebSocketDisconnect\n\nfrom .constants import DATA_TYPE_JSON, DATA_TYPE_MULTIPART, PLAYGROUND_HTML\nfrom .exceptions import HttpBadRequestError, HttpError\nfrom .file_uploads import combine_multipart_data\nfrom .format_error import format_error\nfrom .graphql import graphql, subscribe\nfrom .logger import log_error\nfrom .types import ContextValue, ErrorFormatter, Extension, RootValue\n\nGQL_CONNECTION_INIT = \"connection_init\"  # Client -> Server\nGQL_CONNECTION_ACK = \"connection_ack\"  # Server -> Client\nGQL_CONNECTION_ERROR = \"connection_error\"  # Server -> Client\n\n# NOTE: The keep alive message type does not follow the standard due to connection optimizations\nGQL_CONNECTION_KEEP_ALIVE = \"ka\"  # Server -> Client\n\nGQL_CONNECTION_TERMINATE = \"connection_terminate\"  # Client -> Server\nGQL_START = \"start\"  # Client -> Server\nGQL_DATA = \"data\"  # Server -> Client\nGQL_ERROR = \"error\"  # Server -> Client\nGQL_COMPLETE = \"complete\"  # Server -> Client\nGQL_STOP = \"stop\"  # Client -> Server\n\nExtensionList = Optional[List[Type[Extension]]]\n\n\nclass GraphQL:\n    def __init__(\n        self,\n        schema: GraphQLSchema,\n        *,\n        context_value: Optional[ContextValue] = None,\n        root_value: Optional[RootValue] = None,\n        debug: bool = False,\n        logger: Optional[str] = None,\n        error_formatter: ErrorFormatter = format_error,\n        extensions: Union[Callable[[Any], ExtensionList], ExtensionList] = None,\n        middleware: Optional[MiddlewareManager] = None,\n        keepalive: float = None,\n    ):\n        self.context_value = context_value\n        self.root_value = root_value\n        self.debug = debug\n        self.logger = logger\n        self.error_formatter = error_formatter\n        self.extensions = extensions\n        self.middleware = middleware\n        self.keepalive = keepalive\n        self.schema = schema\n\n    async def __call__(self, scope: Scope, receive: Receive, send: Send):\n        if scope[\"type\"] == \"http\":\n            await self.handle_http(scope=scope, receive=receive, send=send)\n        elif scope[\"type\"] == \"websocket\":\n            await self.handle_websocket(scope=scope, receive=receive, send=send)\n        else:\n            raise ValueError(\"Unknown scope type: %r\" % (scope[\"type\"],))\n\n    async def get_context_for_request(self, request: Any) -> Any:\n        if callable(self.context_value):\n            context = self.context_value(request)\n            if isawaitable(context):\n                context = await context\n            return context\n\n        return self.context_value or {\"request\": request}\n\n    async def get_extensions_for_request(self, request: Any) -> ExtensionList:\n        if callable(self.extensions):\n            return self.extensions(request)\n        return self.extensions\n\n    async def handle_http(self, scope: Scope, receive: Receive, send: Send):\n        request = Request(scope=scope, receive=receive)\n        if request.method == \"GET\":\n            response = await self.render_playground(request)\n        elif request.method == \"POST\":\n            response = await self.graphql_http_server(request)\n        else:\n            response = Response(status_code=405)\n        await response(scope, receive, send)\n\n    async def handle_websocket(self, scope: Scope, receive: Receive, send: Send):\n        websocket = WebSocket(scope=scope, receive=receive, send=send)\n        await self.websocket_server(websocket)\n\n    async def render_playground(  # pylint: disable=unused-argument\n        self, request: Request\n    ) -> Response:\n        return HTMLResponse(PLAYGROUND_HTML)\n\n    async def graphql_http_server(self, request: Request) -> Response:\n        try:\n            data = await self.extract_data_from_request(request)\n        except HttpError as error:\n            return PlainTextResponse(error.message or error.status, status_code=400)\n\n        context_value = await self.get_context_for_request(request)\n        extensions = await self.get_extensions_for_request(request)\n\n        success, response = await graphql(\n            self.schema,\n            data,\n            context_value=context_value,\n            root_value=self.root_value,\n            debug=self.debug,\n            logger=self.logger,\n            error_formatter=self.error_formatter,\n            extensions=extensions,\n        )\n        status_code = 200 if success else 400\n        return JSONResponse(response, status_code=status_code)\n\n    async def extract_data_from_request(self, request: Request):\n        content_type = request.headers.get(\"Content-Type\", \"\")\n        content_type = content_type.split(\";\")[0]\n\n        if content_type == DATA_TYPE_JSON:\n            return await self.extract_data_from_json_request(request)\n        if content_type == DATA_TYPE_MULTIPART:\n            return await self.extract_data_from_multipart_request(request)\n\n        raise HttpBadRequestError(\n            \"Posted content must be of type {} or {}\".format(\n                DATA_TYPE_JSON, DATA_TYPE_MULTIPART\n            )\n        )\n\n    async def extract_data_from_json_request(self, request: Request):\n        try:\n            return await request.json()\n        except (TypeError, ValueError):\n            raise HttpBadRequestError(\"Request body is not a valid JSON\")\n\n    async def extract_data_from_multipart_request(self, request: Request):\n        try:\n            request_body = await request.form()\n        except ValueError:\n            raise HttpBadRequestError(\"Request body is not a valid multipart/form-data\")\n\n        try:\n            operations = json.loads(request_body.get(\"operations\"))\n        except (TypeError, ValueError):\n            raise HttpBadRequestError(\n                \"Request 'operations' multipart field is not a valid JSON\"\n            )\n        try:\n            files_map = json.loads(request_body.get(\"map\"))\n        except (TypeError, ValueError):\n            raise HttpBadRequestError(\n                \"Request 'map' multipart field is not a valid JSON\"\n            )\n\n        request_files = {\n            key: value\n            for key, value in request_body.items()\n            if isinstance(value, UploadFile)\n        }\n\n        return combine_multipart_data(operations, files_map, request_files)\n\n    async def websocket_server(self, websocket: WebSocket) -> None:\n        subscriptions: Dict[str, AsyncGenerator] = {}\n        await websocket.accept(\"graphql-ws\")\n        try:\n            while (\n                websocket.client_state != WebSocketState.DISCONNECTED\n                and websocket.application_state != WebSocketState.DISCONNECTED\n            ):\n                message = await websocket.receive_json()\n                await self.handle_websocket_message(message, websocket, subscriptions)\n        except WebSocketDisconnect:\n            pass\n        finally:\n            for operation_id in subscriptions:\n                await subscriptions[operation_id].aclose()\n\n    async def handle_websocket_message(\n        self,\n        message: dict,\n        websocket: WebSocket,\n        subscriptions: Dict[str, AsyncGenerator],\n    ):\n        operation_id = cast(str, message.get(\"id\"))\n        message_type = cast(str, message.get(\"type\"))\n\n        if message_type == GQL_CONNECTION_INIT:\n            await websocket.send_json({\"type\": GQL_CONNECTION_ACK})\n            asyncio.ensure_future(self.keep_websocket_alive(websocket))\n        elif message_type == GQL_CONNECTION_TERMINATE:\n            await websocket.close()\n        elif message_type == GQL_START:\n            await self.start_websocket_subscription(\n                message.get(\"payload\"), operation_id, websocket, subscriptions\n            )\n        elif message_type == GQL_STOP:\n            if operation_id in subscriptions:\n                await subscriptions[operation_id].aclose()\n                del subscriptions[operation_id]\n\n    async def keep_websocket_alive(self, websocket: WebSocket):\n        if not self.keepalive:\n            return\n        while websocket.application_state != WebSocketState.DISCONNECTED:\n            try:\n                await websocket.send_json({\"type\": GQL_CONNECTION_KEEP_ALIVE})\n            except WebSocketDisconnect:\n                return\n            await asyncio.sleep(self.keepalive)\n\n    async def start_websocket_subscription(\n        self,\n        data: Any,\n        operation_id: str,\n        websocket: WebSocket,\n        subscriptions: Dict[str, AsyncGenerator],\n    ):\n        context_value = await self.get_context_for_request(websocket)\n        success, results = await subscribe(\n            self.schema,\n            data,\n            context_value=context_value,\n            root_value=self.root_value,\n            debug=self.debug,\n            logger=self.logger,\n            error_formatter=self.error_formatter,\n        )\n        if not success:\n            results = cast(List[dict], results)\n            await websocket.send_json(\n                {\"type\": GQL_ERROR, \"id\": operation_id, \"payload\": results[0]}\n            )\n        else:\n            results = cast(AsyncGenerator, results)\n            subscriptions[operation_id] = results\n            asyncio.ensure_future(\n                self.observe_async_results(results, operation_id, websocket)\n            )\n\n    async def observe_async_results(\n        self, results: AsyncGenerator, operation_id: str, websocket: WebSocket\n    ) -> None:\n        try:\n            async for result in results:\n                payload = {}\n                if result.data:\n                    payload[\"data\"] = result.data\n                if result.errors:\n                    for error in result.errors:\n                        log_error(error, self.logger)\n                    payload[\"errors\"] = [\n                        self.error_formatter(error, self.debug)\n                        for error in result.errors\n                    ]\n                await websocket.send_json(\n                    {\"type\": GQL_DATA, \"id\": operation_id, \"payload\": payload}\n                )\n        except Exception as error:\n            if not isinstance(error, GraphQLError):\n                error = GraphQLError(str(error), original_error=error)\n            log_error(error, self.logger)\n            payload = {\"errors\": [self.error_formatter(error, self.debug)]}\n            await websocket.send_json(\n                {\"type\": GQL_DATA, \"id\": operation_id, \"payload\": payload}\n            )\n\n        if (\n            websocket.client_state != WebSocketState.DISCONNECTED\n            and websocket.application_state != WebSocketState.DISCONNECTED\n        ):\n            await websocket.send_json({\"type\": GQL_COMPLETE, \"id\": operation_id})\n", "path": "ariadne/asgi.py"}]}