We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Bug: Quart 驱动器 WebSocket 上下文错误
### 操作系统

Windows

### Python 版本

Any

### NoneBot 版本

2.0.0

### 适配器

OneBot v11

### 协议端

/

### 描述问题

Related to https://github.com/nonebot/adapter-onebot/issues/68

### 复现步骤

在 Apscheduler 的 job 中调用 bot api 通过 websocket 发送内容

### 期望的结果

_No response_

### 截图或日志

_No response_

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `nonebot/drivers/quart.py`
Content:
```
1 """[Quart](https://pgjones.gitlab.io/quart/index.html) 驱动适配
2 
3 ```bash
4 nb driver install quart
5 # 或者
6 pip install nonebot2[quart]
7 ```
8 
9 :::tip 提示
10 本驱动仅支持服务端连接
11 :::
12 
13 FrontMatter:
14     sidebar_position: 5
15     description: nonebot.drivers.quart 模块
16 """
17 
18 import asyncio
19 from functools import wraps
20 from typing import Any, Dict, List, Tuple, Union, TypeVar, Callable, Optional, Coroutine
21 
22 from pydantic import BaseSettings
23 
24 from nonebot.config import Env
25 from nonebot.typing import overrides
26 from nonebot.exception import WebSocketClosed
27 from nonebot.internal.driver import FileTypes
28 from nonebot.config import Config as NoneBotConfig
29 from nonebot.drivers import Request as BaseRequest
30 from nonebot.drivers import WebSocket as BaseWebSocket
31 from nonebot.drivers import ReverseDriver, HTTPServerSetup, WebSocketServerSetup
32 
33 try:
34     import uvicorn
35     from quart import request as _request
36     from quart import websocket as _websocket
37     from quart import Quart, Request, Response
38     from quart.datastructures import FileStorage
39     from quart import Websocket as QuartWebSocket
40 except ModuleNotFoundError as e:  # pragma: no cover
41     raise ImportError(
42         "Please install Quart first to use this driver. "
43         "Install with pip: `pip install nonebot2[quart]`"
44     ) from e
45 
46 _AsyncCallable = TypeVar("_AsyncCallable", bound=Callable[..., Coroutine])
47 
48 
49 def catch_closed(func):
50     @wraps(func)
51     async def decorator(*args, **kwargs):
52         try:
53             return await func(*args, **kwargs)
54         except asyncio.CancelledError:
55             raise WebSocketClosed(1000)
56 
57     return decorator
58 
59 
60 class Config(BaseSettings):
61     """Quart 驱动框架设置"""
62 
63     quart_reload: bool = False
64     """开启/关闭冷重载"""
65     quart_reload_dirs: Optional[List[str]] = None
66     """重载监控文件夹列表，默认为 uvicorn 默认值"""
67     quart_reload_delay: float = 0.25
68     """重载延迟，默认为 uvicorn 默认值"""
69     quart_reload_includes: Optional[List[str]] = None
70     """要监听的文件列表，支持 glob pattern，默认为 uvicorn 默认值"""
71     quart_reload_excludes: Optional[List[str]] = None
72     """不要监听的文件列表，支持 glob pattern，默认为 uvicorn 默认值"""
73     quart_extra: Dict[str, Any] = {}
74     """传递给 `Quart` 的其他参数。"""
75 
76     class Config:
77         extra = "ignore"
78 
79 
80 class Driver(ReverseDriver):
81     """Quart 驱动框架"""
82 
83     def __init__(self, env: Env, config: NoneBotConfig):
84         super().__init__(env, config)
85 
86         self.quart_config = Config(**config.dict())
87 
88         self._server_app = Quart(
89             self.__class__.__qualname__, **self.quart_config.quart_extra
90         )
91 
92     @property
93     @overrides(ReverseDriver)
94     def type(self) -> str:
95         """驱动名称: `quart`"""
96         return "quart"
97 
98     @property
99     @overrides(ReverseDriver)
100     def server_app(self) -> Quart:
101         """`Quart` 对象"""
102         return self._server_app
103 
104     @property
105     @overrides(ReverseDriver)
106     def asgi(self):
107         """`Quart` 对象"""
108         return self._server_app
109 
110     @property
111     @overrides(ReverseDriver)
112     def logger(self):
113         """Quart 使用的 logger"""
114         return self._server_app.logger
115 
116     @overrides(ReverseDriver)
117     def setup_http_server(self, setup: HTTPServerSetup):
118         async def _handle() -> Response:
119             return await self._handle_http(setup)
120 
121         self._server_app.add_url_rule(
122             setup.path.path,
123             endpoint=setup.name,
124             methods=[setup.method],
125             view_func=_handle,
126         )
127 
128     @overrides(ReverseDriver)
129     def setup_websocket_server(self, setup: WebSocketServerSetup) -> None:
130         async def _handle() -> None:
131             return await self._handle_ws(setup)
132 
133         self._server_app.add_websocket(
134             setup.path.path,
135             endpoint=setup.name,
136             view_func=_handle,
137         )
138 
139     @overrides(ReverseDriver)
140     def on_startup(self, func: _AsyncCallable) -> _AsyncCallable:
141         """参考文档: [`Startup and Shutdown`](https://pgjones.gitlab.io/quart/how_to_guides/startup_shutdown.html)"""
142         return self.server_app.before_serving(func)  # type: ignore
143 
144     @overrides(ReverseDriver)
145     def on_shutdown(self, func: _AsyncCallable) -> _AsyncCallable:
146         """参考文档: [`Startup and Shutdown`](https://pgjones.gitlab.io/quart/how_to_guides/startup_shutdown.html)"""
147         return self.server_app.after_serving(func)  # type: ignore
148 
149     @overrides(ReverseDriver)
150     def run(
151         self,
152         host: Optional[str] = None,
153         port: Optional[int] = None,
154         *,
155         app: Optional[str] = None,
156         **kwargs,
157     ):
158         """使用 `uvicorn` 启动 Quart"""
159         super().run(host, port, app, **kwargs)
160         LOGGING_CONFIG = {
161             "version": 1,
162             "disable_existing_loggers": False,
163             "handlers": {
164                 "default": {
165                     "class": "nonebot.log.LoguruHandler",
166                 },
167             },
168             "loggers": {
169                 "uvicorn.error": {"handlers": ["default"], "level": "INFO"},
170                 "uvicorn.access": {
171                     "handlers": ["default"],
172                     "level": "INFO",
173                 },
174             },
175         }
176         uvicorn.run(
177             app or self.server_app,  # type: ignore
178             host=host or str(self.config.host),
179             port=port or self.config.port,
180             reload=self.quart_config.quart_reload,
181             reload_dirs=self.quart_config.quart_reload_dirs,
182             reload_delay=self.quart_config.quart_reload_delay,
183             reload_includes=self.quart_config.quart_reload_includes,
184             reload_excludes=self.quart_config.quart_reload_excludes,
185             log_config=LOGGING_CONFIG,
186             **kwargs,
187         )
188 
189     async def _handle_http(self, setup: HTTPServerSetup) -> Response:
190         request: Request = _request
191 
192         json = await request.get_json() if request.is_json else None
193 
194         data = await request.form
195         files_dict = await request.files
196         files: List[Tuple[str, FileTypes]] = []
197         key: str
198         value: FileStorage
199         for key, value in files_dict.items():
200             files.append((key, (value.filename, value.stream, value.content_type)))
201 
202         http_request = BaseRequest(
203             request.method,
204             request.url,
205             headers=list(request.headers.items()),
206             cookies=list(request.cookies.items()),
207             content=await request.get_data(
208                 cache=False, as_text=False, parse_form_data=False
209             ),
210             data=data or None,
211             json=json,
212             files=files or None,
213             version=request.http_version,
214         )
215 
216         response = await setup.handle_func(http_request)
217 
218         return Response(
219             response.content or "",
220             response.status_code or 200,
221             headers=dict(response.headers),
222         )
223 
224     async def _handle_ws(self, setup: WebSocketServerSetup) -> None:
225         websocket: QuartWebSocket = _websocket
226 
227         http_request = BaseRequest(
228             websocket.method,
229             websocket.url,
230             headers=list(websocket.headers.items()),
231             cookies=list(websocket.cookies.items()),
232             version=websocket.http_version,
233         )
234 
235         ws = WebSocket(request=http_request, websocket=websocket)
236 
237         await setup.handle_func(ws)
238 
239 
240 class WebSocket(BaseWebSocket):
241     """Quart WebSocket Wrapper"""
242 
243     def __init__(self, *, request: BaseRequest, websocket: QuartWebSocket):
244         super().__init__(request=request)
245         self.websocket = websocket
246 
247     @property
248     @overrides(BaseWebSocket)
249     def closed(self):
250         # FIXME
251         return True
252 
253     @overrides(BaseWebSocket)
254     async def accept(self):
255         await self.websocket.accept()
256 
257     @overrides(BaseWebSocket)
258     async def close(self, code: int = 1000, reason: str = ""):
259         await self.websocket.close(code, reason)
260 
261     @overrides(BaseWebSocket)
262     @catch_closed
263     async def receive(self) -> Union[str, bytes]:
264         return await self.websocket.receive()
265 
266     @overrides(BaseWebSocket)
267     @catch_closed
268     async def receive_text(self) -> str:
269         msg = await self.websocket.receive()
270         if isinstance(msg, bytes):
271             raise TypeError("WebSocket received unexpected frame type: bytes")
272         return msg
273 
274     @overrides(BaseWebSocket)
275     @catch_closed
276     async def receive_bytes(self) -> bytes:
277         msg = await self.websocket.receive()
278         if isinstance(msg, str):
279             raise TypeError("WebSocket received unexpected frame type: str")
280         return msg
281 
282     @overrides(BaseWebSocket)
283     async def send_text(self, data: str):
284         await self.websocket.send(data)
285 
286     @overrides(BaseWebSocket)
287     async def send_bytes(self, data: bytes):
288         await self.websocket.send(data)
289 
290 
291 __autodoc__ = {"catch_closed": False}
292
```
--- 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/nonebot/drivers/quart.py b/nonebot/drivers/quart.py
--- a/nonebot/drivers/quart.py
+++ b/nonebot/drivers/quart.py
@@ -17,7 +17,18 @@
 
 import asyncio
 from functools import wraps
-from typing import Any, Dict, List, Tuple, Union, TypeVar, Callable, Optional, Coroutine
+from typing import (
+    Any,
+    Dict,
+    List,
+    Tuple,
+    Union,
+    TypeVar,
+    Callable,
+    Optional,
+    Coroutine,
+    cast,
+)
 
 from pydantic import BaseSettings
 
@@ -33,7 +44,8 @@
 try:
     import uvicorn
     from quart import request as _request
-    from quart import websocket as _websocket
+    from quart.ctx import WebsocketContext
+    from quart.globals import websocket_ctx
     from quart import Quart, Request, Response
     from quart.datastructures import FileStorage
     from quart import Websocket as QuartWebSocket
@@ -222,7 +234,8 @@
         )
 
     async def _handle_ws(self, setup: WebSocketServerSetup) -> None:
-        websocket: QuartWebSocket = _websocket
+        ctx = cast(WebsocketContext, websocket_ctx.copy())
+        websocket = websocket_ctx.websocket
 
         http_request = BaseRequest(
             websocket.method,
@@ -232,7 +245,7 @@
             version=websocket.http_version,
         )
 
-        ws = WebSocket(request=http_request, websocket=websocket)
+        ws = WebSocket(request=http_request, websocket_ctx=ctx)
 
         await setup.handle_func(ws)
 
@@ -240,9 +253,13 @@
 class WebSocket(BaseWebSocket):
     """Quart WebSocket Wrapper"""
 
-    def __init__(self, *, request: BaseRequest, websocket: QuartWebSocket):
+    def __init__(self, *, request: BaseRequest, websocket_ctx: WebsocketContext):
         super().__init__(request=request)
-        self.websocket = websocket
+        self.websocket_ctx = websocket_ctx
+
+    @property
+    def websocket(self) -> QuartWebSocket:
+        return self.websocket_ctx.websocket
 
     @property
     @overrides(BaseWebSocket)

{"golden_diff": "diff --git a/nonebot/drivers/quart.py b/nonebot/drivers/quart.py\n--- a/nonebot/drivers/quart.py\n+++ b/nonebot/drivers/quart.py\n@@ -17,7 +17,18 @@\n \n import asyncio\n from functools import wraps\n-from typing import Any, Dict, List, Tuple, Union, TypeVar, Callable, Optional, Coroutine\n+from typing import (\n+    Any,\n+    Dict,\n+    List,\n+    Tuple,\n+    Union,\n+    TypeVar,\n+    Callable,\n+    Optional,\n+    Coroutine,\n+    cast,\n+)\n \n from pydantic import BaseSettings\n \n@@ -33,7 +44,8 @@\n try:\n     import uvicorn\n     from quart import request as _request\n-    from quart import websocket as _websocket\n+    from quart.ctx import WebsocketContext\n+    from quart.globals import websocket_ctx\n     from quart import Quart, Request, Response\n     from quart.datastructures import FileStorage\n     from quart import Websocket as QuartWebSocket\n@@ -222,7 +234,8 @@\n         )\n \n     async def _handle_ws(self, setup: WebSocketServerSetup) -> None:\n-        websocket: QuartWebSocket = _websocket\n+        ctx = cast(WebsocketContext, websocket_ctx.copy())\n+        websocket = websocket_ctx.websocket\n \n         http_request = BaseRequest(\n             websocket.method,\n@@ -232,7 +245,7 @@\n             version=websocket.http_version,\n         )\n \n-        ws = WebSocket(request=http_request, websocket=websocket)\n+        ws = WebSocket(request=http_request, websocket_ctx=ctx)\n \n         await setup.handle_func(ws)\n \n@@ -240,9 +253,13 @@\n class WebSocket(BaseWebSocket):\n     \"\"\"Quart WebSocket Wrapper\"\"\"\n \n-    def __init__(self, *, request: BaseRequest, websocket: QuartWebSocket):\n+    def __init__(self, *, request: BaseRequest, websocket_ctx: WebsocketContext):\n         super().__init__(request=request)\n-        self.websocket = websocket\n+        self.websocket_ctx = websocket_ctx\n+\n+    @property\n+    def websocket(self) -> QuartWebSocket:\n+        return self.websocket_ctx.websocket\n \n     @property\n     @overrides(BaseWebSocket)\n", "issue": "Bug: Quart \u9a71\u52a8\u5668 WebSocket \u4e0a\u4e0b\u6587\u9519\u8bef\n### \u64cd\u4f5c\u7cfb\u7edf\n\nWindows\n\n### Python \u7248\u672c\n\nAny\n\n### NoneBot \u7248\u672c\n\n2.0.0\n\n### \u9002\u914d\u5668\n\nOneBot v11\n\n### \u534f\u8bae\u7aef\n\n/\n\n### \u63cf\u8ff0\u95ee\u9898\n\nRelated to https://github.com/nonebot/adapter-onebot/issues/68\n\n### \u590d\u73b0\u6b65\u9aa4\n\n\u5728 Apscheduler \u7684 job \u4e2d\u8c03\u7528 bot api \u901a\u8fc7 websocket \u53d1\u9001\u5185\u5bb9\n\n### \u671f\u671b\u7684\u7ed3\u679c\n\n_No response_\n\n### \u622a\u56fe\u6216\u65e5\u5fd7\n\n_No response_\n", "before_files": [{"content": "\"\"\"[Quart](https://pgjones.gitlab.io/quart/index.html) \u9a71\u52a8\u9002\u914d\n\n```bash\nnb driver install quart\n# \u6216\u8005\npip install nonebot2[quart]\n```\n\n:::tip \u63d0\u793a\n\u672c\u9a71\u52a8\u4ec5\u652f\u6301\u670d\u52a1\u7aef\u8fde\u63a5\n:::\n\nFrontMatter:\n    sidebar_position: 5\n    description: nonebot.drivers.quart \u6a21\u5757\n\"\"\"\n\nimport asyncio\nfrom functools import wraps\nfrom typing import Any, Dict, List, Tuple, Union, TypeVar, Callable, Optional, Coroutine\n\nfrom pydantic import BaseSettings\n\nfrom nonebot.config import Env\nfrom nonebot.typing import overrides\nfrom nonebot.exception import WebSocketClosed\nfrom nonebot.internal.driver import FileTypes\nfrom nonebot.config import Config as NoneBotConfig\nfrom nonebot.drivers import Request as BaseRequest\nfrom nonebot.drivers import WebSocket as BaseWebSocket\nfrom nonebot.drivers import ReverseDriver, HTTPServerSetup, WebSocketServerSetup\n\ntry:\n    import uvicorn\n    from quart import request as _request\n    from quart import websocket as _websocket\n    from quart import Quart, Request, Response\n    from quart.datastructures import FileStorage\n    from quart import Websocket as QuartWebSocket\nexcept ModuleNotFoundError as e:  # pragma: no cover\n    raise ImportError(\n        \"Please install Quart first to use this driver. \"\n        \"Install with pip: `pip install nonebot2[quart]`\"\n    ) from e\n\n_AsyncCallable = TypeVar(\"_AsyncCallable\", bound=Callable[..., Coroutine])\n\n\ndef catch_closed(func):\n    @wraps(func)\n    async def decorator(*args, **kwargs):\n        try:\n            return await func(*args, **kwargs)\n        except asyncio.CancelledError:\n            raise WebSocketClosed(1000)\n\n    return decorator\n\n\nclass Config(BaseSettings):\n    \"\"\"Quart \u9a71\u52a8\u6846\u67b6\u8bbe\u7f6e\"\"\"\n\n    quart_reload: bool = False\n    \"\"\"\u5f00\u542f/\u5173\u95ed\u51b7\u91cd\u8f7d\"\"\"\n    quart_reload_dirs: Optional[List[str]] = None\n    \"\"\"\u91cd\u8f7d\u76d1\u63a7\u6587\u4ef6\u5939\u5217\u8868\uff0c\u9ed8\u8ba4\u4e3a uvicorn \u9ed8\u8ba4\u503c\"\"\"\n    quart_reload_delay: float = 0.25\n    \"\"\"\u91cd\u8f7d\u5ef6\u8fdf\uff0c\u9ed8\u8ba4\u4e3a uvicorn \u9ed8\u8ba4\u503c\"\"\"\n    quart_reload_includes: Optional[List[str]] = None\n    \"\"\"\u8981\u76d1\u542c\u7684\u6587\u4ef6\u5217\u8868\uff0c\u652f\u6301 glob pattern\uff0c\u9ed8\u8ba4\u4e3a uvicorn \u9ed8\u8ba4\u503c\"\"\"\n    quart_reload_excludes: Optional[List[str]] = None\n    \"\"\"\u4e0d\u8981\u76d1\u542c\u7684\u6587\u4ef6\u5217\u8868\uff0c\u652f\u6301 glob pattern\uff0c\u9ed8\u8ba4\u4e3a uvicorn \u9ed8\u8ba4\u503c\"\"\"\n    quart_extra: Dict[str, Any] = {}\n    \"\"\"\u4f20\u9012\u7ed9 `Quart` \u7684\u5176\u4ed6\u53c2\u6570\u3002\"\"\"\n\n    class Config:\n        extra = \"ignore\"\n\n\nclass Driver(ReverseDriver):\n    \"\"\"Quart \u9a71\u52a8\u6846\u67b6\"\"\"\n\n    def __init__(self, env: Env, config: NoneBotConfig):\n        super().__init__(env, config)\n\n        self.quart_config = Config(**config.dict())\n\n        self._server_app = Quart(\n            self.__class__.__qualname__, **self.quart_config.quart_extra\n        )\n\n    @property\n    @overrides(ReverseDriver)\n    def type(self) -> str:\n        \"\"\"\u9a71\u52a8\u540d\u79f0: `quart`\"\"\"\n        return \"quart\"\n\n    @property\n    @overrides(ReverseDriver)\n    def server_app(self) -> Quart:\n        \"\"\"`Quart` \u5bf9\u8c61\"\"\"\n        return self._server_app\n\n    @property\n    @overrides(ReverseDriver)\n    def asgi(self):\n        \"\"\"`Quart` \u5bf9\u8c61\"\"\"\n        return self._server_app\n\n    @property\n    @overrides(ReverseDriver)\n    def logger(self):\n        \"\"\"Quart \u4f7f\u7528\u7684 logger\"\"\"\n        return self._server_app.logger\n\n    @overrides(ReverseDriver)\n    def setup_http_server(self, setup: HTTPServerSetup):\n        async def _handle() -> Response:\n            return await self._handle_http(setup)\n\n        self._server_app.add_url_rule(\n            setup.path.path,\n            endpoint=setup.name,\n            methods=[setup.method],\n            view_func=_handle,\n        )\n\n    @overrides(ReverseDriver)\n    def setup_websocket_server(self, setup: WebSocketServerSetup) -> None:\n        async def _handle() -> None:\n            return await self._handle_ws(setup)\n\n        self._server_app.add_websocket(\n            setup.path.path,\n            endpoint=setup.name,\n            view_func=_handle,\n        )\n\n    @overrides(ReverseDriver)\n    def on_startup(self, func: _AsyncCallable) -> _AsyncCallable:\n        \"\"\"\u53c2\u8003\u6587\u6863: [`Startup and Shutdown`](https://pgjones.gitlab.io/quart/how_to_guides/startup_shutdown.html)\"\"\"\n        return self.server_app.before_serving(func)  # type: ignore\n\n    @overrides(ReverseDriver)\n    def on_shutdown(self, func: _AsyncCallable) -> _AsyncCallable:\n        \"\"\"\u53c2\u8003\u6587\u6863: [`Startup and Shutdown`](https://pgjones.gitlab.io/quart/how_to_guides/startup_shutdown.html)\"\"\"\n        return self.server_app.after_serving(func)  # type: ignore\n\n    @overrides(ReverseDriver)\n    def run(\n        self,\n        host: Optional[str] = None,\n        port: Optional[int] = None,\n        *,\n        app: Optional[str] = None,\n        **kwargs,\n    ):\n        \"\"\"\u4f7f\u7528 `uvicorn` \u542f\u52a8 Quart\"\"\"\n        super().run(host, port, app, **kwargs)\n        LOGGING_CONFIG = {\n            \"version\": 1,\n            \"disable_existing_loggers\": False,\n            \"handlers\": {\n                \"default\": {\n                    \"class\": \"nonebot.log.LoguruHandler\",\n                },\n            },\n            \"loggers\": {\n                \"uvicorn.error\": {\"handlers\": [\"default\"], \"level\": \"INFO\"},\n                \"uvicorn.access\": {\n                    \"handlers\": [\"default\"],\n                    \"level\": \"INFO\",\n                },\n            },\n        }\n        uvicorn.run(\n            app or self.server_app,  # type: ignore\n            host=host or str(self.config.host),\n            port=port or self.config.port,\n            reload=self.quart_config.quart_reload,\n            reload_dirs=self.quart_config.quart_reload_dirs,\n            reload_delay=self.quart_config.quart_reload_delay,\n            reload_includes=self.quart_config.quart_reload_includes,\n            reload_excludes=self.quart_config.quart_reload_excludes,\n            log_config=LOGGING_CONFIG,\n            **kwargs,\n        )\n\n    async def _handle_http(self, setup: HTTPServerSetup) -> Response:\n        request: Request = _request\n\n        json = await request.get_json() if request.is_json else None\n\n        data = await request.form\n        files_dict = await request.files\n        files: List[Tuple[str, FileTypes]] = []\n        key: str\n        value: FileStorage\n        for key, value in files_dict.items():\n            files.append((key, (value.filename, value.stream, value.content_type)))\n\n        http_request = BaseRequest(\n            request.method,\n            request.url,\n            headers=list(request.headers.items()),\n            cookies=list(request.cookies.items()),\n            content=await request.get_data(\n                cache=False, as_text=False, parse_form_data=False\n            ),\n            data=data or None,\n            json=json,\n            files=files or None,\n            version=request.http_version,\n        )\n\n        response = await setup.handle_func(http_request)\n\n        return Response(\n            response.content or \"\",\n            response.status_code or 200,\n            headers=dict(response.headers),\n        )\n\n    async def _handle_ws(self, setup: WebSocketServerSetup) -> None:\n        websocket: QuartWebSocket = _websocket\n\n        http_request = BaseRequest(\n            websocket.method,\n            websocket.url,\n            headers=list(websocket.headers.items()),\n            cookies=list(websocket.cookies.items()),\n            version=websocket.http_version,\n        )\n\n        ws = WebSocket(request=http_request, websocket=websocket)\n\n        await setup.handle_func(ws)\n\n\nclass WebSocket(BaseWebSocket):\n    \"\"\"Quart WebSocket Wrapper\"\"\"\n\n    def __init__(self, *, request: BaseRequest, websocket: QuartWebSocket):\n        super().__init__(request=request)\n        self.websocket = websocket\n\n    @property\n    @overrides(BaseWebSocket)\n    def closed(self):\n        # FIXME\n        return True\n\n    @overrides(BaseWebSocket)\n    async def accept(self):\n        await self.websocket.accept()\n\n    @overrides(BaseWebSocket)\n    async def close(self, code: int = 1000, reason: str = \"\"):\n        await self.websocket.close(code, reason)\n\n    @overrides(BaseWebSocket)\n    @catch_closed\n    async def receive(self) -> Union[str, bytes]:\n        return await self.websocket.receive()\n\n    @overrides(BaseWebSocket)\n    @catch_closed\n    async def receive_text(self) -> str:\n        msg = await self.websocket.receive()\n        if isinstance(msg, bytes):\n            raise TypeError(\"WebSocket received unexpected frame type: bytes\")\n        return msg\n\n    @overrides(BaseWebSocket)\n    @catch_closed\n    async def receive_bytes(self) -> bytes:\n        msg = await self.websocket.receive()\n        if isinstance(msg, str):\n            raise TypeError(\"WebSocket received unexpected frame type: str\")\n        return msg\n\n    @overrides(BaseWebSocket)\n    async def send_text(self, data: str):\n        await self.websocket.send(data)\n\n    @overrides(BaseWebSocket)\n    async def send_bytes(self, data: bytes):\n        await self.websocket.send(data)\n\n\n__autodoc__ = {\"catch_closed\": False}\n", "path": "nonebot/drivers/quart.py"}], "after_files": [{"content": "\"\"\"[Quart](https://pgjones.gitlab.io/quart/index.html) \u9a71\u52a8\u9002\u914d\n\n```bash\nnb driver install quart\n# \u6216\u8005\npip install nonebot2[quart]\n```\n\n:::tip \u63d0\u793a\n\u672c\u9a71\u52a8\u4ec5\u652f\u6301\u670d\u52a1\u7aef\u8fde\u63a5\n:::\n\nFrontMatter:\n    sidebar_position: 5\n    description: nonebot.drivers.quart \u6a21\u5757\n\"\"\"\n\nimport asyncio\nfrom functools import wraps\nfrom typing import (\n    Any,\n    Dict,\n    List,\n    Tuple,\n    Union,\n    TypeVar,\n    Callable,\n    Optional,\n    Coroutine,\n    cast,\n)\n\nfrom pydantic import BaseSettings\n\nfrom nonebot.config import Env\nfrom nonebot.typing import overrides\nfrom nonebot.exception import WebSocketClosed\nfrom nonebot.internal.driver import FileTypes\nfrom nonebot.config import Config as NoneBotConfig\nfrom nonebot.drivers import Request as BaseRequest\nfrom nonebot.drivers import WebSocket as BaseWebSocket\nfrom nonebot.drivers import ReverseDriver, HTTPServerSetup, WebSocketServerSetup\n\ntry:\n    import uvicorn\n    from quart import request as _request\n    from quart.ctx import WebsocketContext\n    from quart.globals import websocket_ctx\n    from quart import Quart, Request, Response\n    from quart.datastructures import FileStorage\n    from quart import Websocket as QuartWebSocket\nexcept ModuleNotFoundError as e:  # pragma: no cover\n    raise ImportError(\n        \"Please install Quart first to use this driver. \"\n        \"Install with pip: `pip install nonebot2[quart]`\"\n    ) from e\n\n_AsyncCallable = TypeVar(\"_AsyncCallable\", bound=Callable[..., Coroutine])\n\n\ndef catch_closed(func):\n    @wraps(func)\n    async def decorator(*args, **kwargs):\n        try:\n            return await func(*args, **kwargs)\n        except asyncio.CancelledError:\n            raise WebSocketClosed(1000)\n\n    return decorator\n\n\nclass Config(BaseSettings):\n    \"\"\"Quart \u9a71\u52a8\u6846\u67b6\u8bbe\u7f6e\"\"\"\n\n    quart_reload: bool = False\n    \"\"\"\u5f00\u542f/\u5173\u95ed\u51b7\u91cd\u8f7d\"\"\"\n    quart_reload_dirs: Optional[List[str]] = None\n    \"\"\"\u91cd\u8f7d\u76d1\u63a7\u6587\u4ef6\u5939\u5217\u8868\uff0c\u9ed8\u8ba4\u4e3a uvicorn \u9ed8\u8ba4\u503c\"\"\"\n    quart_reload_delay: float = 0.25\n    \"\"\"\u91cd\u8f7d\u5ef6\u8fdf\uff0c\u9ed8\u8ba4\u4e3a uvicorn \u9ed8\u8ba4\u503c\"\"\"\n    quart_reload_includes: Optional[List[str]] = None\n    \"\"\"\u8981\u76d1\u542c\u7684\u6587\u4ef6\u5217\u8868\uff0c\u652f\u6301 glob pattern\uff0c\u9ed8\u8ba4\u4e3a uvicorn \u9ed8\u8ba4\u503c\"\"\"\n    quart_reload_excludes: Optional[List[str]] = None\n    \"\"\"\u4e0d\u8981\u76d1\u542c\u7684\u6587\u4ef6\u5217\u8868\uff0c\u652f\u6301 glob pattern\uff0c\u9ed8\u8ba4\u4e3a uvicorn \u9ed8\u8ba4\u503c\"\"\"\n    quart_extra: Dict[str, Any] = {}\n    \"\"\"\u4f20\u9012\u7ed9 `Quart` \u7684\u5176\u4ed6\u53c2\u6570\u3002\"\"\"\n\n    class Config:\n        extra = \"ignore\"\n\n\nclass Driver(ReverseDriver):\n    \"\"\"Quart \u9a71\u52a8\u6846\u67b6\"\"\"\n\n    def __init__(self, env: Env, config: NoneBotConfig):\n        super().__init__(env, config)\n\n        self.quart_config = Config(**config.dict())\n\n        self._server_app = Quart(\n            self.__class__.__qualname__, **self.quart_config.quart_extra\n        )\n\n    @property\n    @overrides(ReverseDriver)\n    def type(self) -> str:\n        \"\"\"\u9a71\u52a8\u540d\u79f0: `quart`\"\"\"\n        return \"quart\"\n\n    @property\n    @overrides(ReverseDriver)\n    def server_app(self) -> Quart:\n        \"\"\"`Quart` \u5bf9\u8c61\"\"\"\n        return self._server_app\n\n    @property\n    @overrides(ReverseDriver)\n    def asgi(self):\n        \"\"\"`Quart` \u5bf9\u8c61\"\"\"\n        return self._server_app\n\n    @property\n    @overrides(ReverseDriver)\n    def logger(self):\n        \"\"\"Quart \u4f7f\u7528\u7684 logger\"\"\"\n        return self._server_app.logger\n\n    @overrides(ReverseDriver)\n    def setup_http_server(self, setup: HTTPServerSetup):\n        async def _handle() -> Response:\n            return await self._handle_http(setup)\n\n        self._server_app.add_url_rule(\n            setup.path.path,\n            endpoint=setup.name,\n            methods=[setup.method],\n            view_func=_handle,\n        )\n\n    @overrides(ReverseDriver)\n    def setup_websocket_server(self, setup: WebSocketServerSetup) -> None:\n        async def _handle() -> None:\n            return await self._handle_ws(setup)\n\n        self._server_app.add_websocket(\n            setup.path.path,\n            endpoint=setup.name,\n            view_func=_handle,\n        )\n\n    @overrides(ReverseDriver)\n    def on_startup(self, func: _AsyncCallable) -> _AsyncCallable:\n        \"\"\"\u53c2\u8003\u6587\u6863: [`Startup and Shutdown`](https://pgjones.gitlab.io/quart/how_to_guides/startup_shutdown.html)\"\"\"\n        return self.server_app.before_serving(func)  # type: ignore\n\n    @overrides(ReverseDriver)\n    def on_shutdown(self, func: _AsyncCallable) -> _AsyncCallable:\n        \"\"\"\u53c2\u8003\u6587\u6863: [`Startup and Shutdown`](https://pgjones.gitlab.io/quart/how_to_guides/startup_shutdown.html)\"\"\"\n        return self.server_app.after_serving(func)  # type: ignore\n\n    @overrides(ReverseDriver)\n    def run(\n        self,\n        host: Optional[str] = None,\n        port: Optional[int] = None,\n        *,\n        app: Optional[str] = None,\n        **kwargs,\n    ):\n        \"\"\"\u4f7f\u7528 `uvicorn` \u542f\u52a8 Quart\"\"\"\n        super().run(host, port, app, **kwargs)\n        LOGGING_CONFIG = {\n            \"version\": 1,\n            \"disable_existing_loggers\": False,\n            \"handlers\": {\n                \"default\": {\n                    \"class\": \"nonebot.log.LoguruHandler\",\n                },\n            },\n            \"loggers\": {\n                \"uvicorn.error\": {\"handlers\": [\"default\"], \"level\": \"INFO\"},\n                \"uvicorn.access\": {\n                    \"handlers\": [\"default\"],\n                    \"level\": \"INFO\",\n                },\n            },\n        }\n        uvicorn.run(\n            app or self.server_app,  # type: ignore\n            host=host or str(self.config.host),\n            port=port or self.config.port,\n            reload=self.quart_config.quart_reload,\n            reload_dirs=self.quart_config.quart_reload_dirs,\n            reload_delay=self.quart_config.quart_reload_delay,\n            reload_includes=self.quart_config.quart_reload_includes,\n            reload_excludes=self.quart_config.quart_reload_excludes,\n            log_config=LOGGING_CONFIG,\n            **kwargs,\n        )\n\n    async def _handle_http(self, setup: HTTPServerSetup) -> Response:\n        request: Request = _request\n\n        json = await request.get_json() if request.is_json else None\n\n        data = await request.form\n        files_dict = await request.files\n        files: List[Tuple[str, FileTypes]] = []\n        key: str\n        value: FileStorage\n        for key, value in files_dict.items():\n            files.append((key, (value.filename, value.stream, value.content_type)))\n\n        http_request = BaseRequest(\n            request.method,\n            request.url,\n            headers=list(request.headers.items()),\n            cookies=list(request.cookies.items()),\n            content=await request.get_data(\n                cache=False, as_text=False, parse_form_data=False\n            ),\n            data=data or None,\n            json=json,\n            files=files or None,\n            version=request.http_version,\n        )\n\n        response = await setup.handle_func(http_request)\n\n        return Response(\n            response.content or \"\",\n            response.status_code or 200,\n            headers=dict(response.headers),\n        )\n\n    async def _handle_ws(self, setup: WebSocketServerSetup) -> None:\n        ctx = cast(WebsocketContext, websocket_ctx.copy())\n        websocket = websocket_ctx.websocket\n\n        http_request = BaseRequest(\n            websocket.method,\n            websocket.url,\n            headers=list(websocket.headers.items()),\n            cookies=list(websocket.cookies.items()),\n            version=websocket.http_version,\n        )\n\n        ws = WebSocket(request=http_request, websocket_ctx=ctx)\n\n        await setup.handle_func(ws)\n\n\nclass WebSocket(BaseWebSocket):\n    \"\"\"Quart WebSocket Wrapper\"\"\"\n\n    def __init__(self, *, request: BaseRequest, websocket_ctx: WebsocketContext):\n        super().__init__(request=request)\n        self.websocket_ctx = websocket_ctx\n\n    @property\n    def websocket(self) -> QuartWebSocket:\n        return self.websocket_ctx.websocket\n\n    @property\n    @overrides(BaseWebSocket)\n    def closed(self):\n        # FIXME\n        return True\n\n    @overrides(BaseWebSocket)\n    async def accept(self):\n        await self.websocket.accept()\n\n    @overrides(BaseWebSocket)\n    async def close(self, code: int = 1000, reason: str = \"\"):\n        await self.websocket.close(code, reason)\n\n    @overrides(BaseWebSocket)\n    @catch_closed\n    async def receive(self) -> Union[str, bytes]:\n        return await self.websocket.receive()\n\n    @overrides(BaseWebSocket)\n    @catch_closed\n    async def receive_text(self) -> str:\n        msg = await self.websocket.receive()\n        if isinstance(msg, bytes):\n            raise TypeError(\"WebSocket received unexpected frame type: bytes\")\n        return msg\n\n    @overrides(BaseWebSocket)\n    @catch_closed\n    async def receive_bytes(self) -> bytes:\n        msg = await self.websocket.receive()\n        if isinstance(msg, str):\n            raise TypeError(\"WebSocket received unexpected frame type: str\")\n        return msg\n\n    @overrides(BaseWebSocket)\n    async def send_text(self, data: str):\n        await self.websocket.send(data)\n\n    @overrides(BaseWebSocket)\n    async def send_bytes(self, data: bytes):\n        await self.websocket.send(data)\n\n\n__autodoc__ = {\"catch_closed\": False}\n", "path": "nonebot/drivers/quart.py"}]}