We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Feature: 正向驱动器 startup/shutdown hook 同步函数支持
**描述问题：**

BlockDriver注册同步on_startup与on_shutdown钩子函数时启动崩溃

**如何复现？**

```python
from nonebot import get_driver


@get_driver().on_startup
def i_will_crash():
    print("oops")

```

**期望的结果**



**环境信息：**

 - OS: Windows 10
 - Python Version: 3.10
 - Nonebot Version: 2.0.0b4

**协议端信息：**

 - 协议端: Kaiheila
 - 协议端版本: 0.0.2

**截图或日志**
```
07-12 23:56:09 [SUCCESS] nonebot | NoneBot is initializing...
07-12 23:56:09 [INFO] nonebot | Current Env: dev
07-12 23:56:09 [DEBUG] nonebot | Loaded Config: {'driver': '~httpx+~websockets', 'host': IPv4Address('127.0.0.1'), 'port': 8080, 'log_level': 'DEBUG', 'api_timeout': 30.0, 'superusers': set(), 'nickname': set(), 'command_start': {'/'}, 'command_sep': {'.'}, 'session_expire_timeout': datetime.timedelta(seconds=120)}
07-12 23:56:09 [DEBUG] nonebot | Succeeded to load adapter "Kaiheila"
07-12 23:56:09 [SUCCESS] nonebot | Succeeded to import "echo"
07-12 23:56:09 [SUCCESS] nonebot | Succeeded to import "nonebot_plugin_pixivbot_kook"
07-12 23:56:09 [SUCCESS] nonebot | Succeeded to import "test"
07-12 23:56:09 [WARNING] __main__ | Always use `nb run` to start the bot instead of manually running!
07-12 23:56:09 [SUCCESS] nonebot | Running NoneBot...
07-12 23:56:09 [DEBUG] nonebot | Loaded adapters: Kaiheila
oops
07-12 23:56:09 [ERROR] nonebot | Error when running startup function. Ignored!
Traceback (most recent call last):
  File "C:\Users\huang\PycharmProjects\kuraku-kook\bot.py", line 21, in <module>
    nonebot.run()
  File "C:\Users\huang\PycharmProjects\kuraku-kook\venv\lib\site-packages\nonebot\__init__.py", line 261, in run
    get_driver().run(*args, **kwargs)
  File "C:\Users\huang\PycharmProjects\kuraku-kook\venv\lib\site-packages\nonebot\drivers\_block_driver.py", line 60, in run
    loop.run_until_complete(self.serve())
  File "C:\Users\huang\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 628, in run_until_complete
    self.run_forever()
  File "C:\Users\huang\AppData\Local\Programs\Python\Python310\lib\asyncio\windows_events.py", line 321, in run_forever
    super().run_forever()
  File "C:\Users\huang\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 595, in run_forever
    self._run_once()
  File "C:\Users\huang\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 1881, in _run_once
    handle._run()
  File "C:\Users\huang\AppData\Local\Programs\Python\Python310\lib\asyncio\events.py", line 80, in _run
    self._context.run(self._callback, *self._args)
  File "C:\Users\huang\PycharmProjects\kuraku-kook\venv\lib\site-packages\nonebot\drivers\_block_driver.py", line 64, in serve
    await self.startup()
> File "C:\Users\huang\PycharmProjects\kuraku-kook\venv\lib\site-packages\nonebot\drivers\_block_driver.py", line 75, in startup
    await asyncio.gather(*cors)
  File "C:\Users\huang\AppData\Local\Programs\Python\Python310\lib\asyncio\tasks.py", line 776, in gather
    fut = _ensure_future(arg, loop=loop)
  File "C:\Users\huang\AppData\Local\Programs\Python\Python310\lib\asyncio\tasks.py", line 629, in _ensure_future
    raise TypeError('An asyncio.Future, a coroutine or an awaitable '
TypeError: An asyncio.Future, a coroutine or an awaitable is required
07-12 23:56:09 [INFO] nonebot | Application startup completed.
Exception in callback gather.<locals>._done_callback(<Task finishe...> result=None>) at C:\Users\huang\AppData\Local\Programs\Python\Python310\lib\asyncio\tasks.py:714
handle: <Handle gather.<locals>._done_callback(<Task finishe...> result=None>) at C:\Users\huang\AppData\Local\Programs\Python\Python310\lib\asyncio\tasks.py:714>
Traceback (most recent call last):
  File "C:\Users\huang\AppData\Local\Programs\Python\Python310\lib\asyncio\events.py", line 80, in _run
    self._context.run(self._callback, *self._args)
  File "C:\Users\huang\AppData\Local\Programs\Python\Python310\lib\asyncio\tasks.py", line 718, in _done_callback
    if outer.done():
NameError: free variable 'outer' referenced before assignment in enclosing scope
07-12 23:56:09 [DEBUG] nonebot | Kaiheila | WebSocket Connection to wss://ws.kaiheila.cn/gateway?compress=0&token=***************** established
07-12 23:56:09 [INFO] nonebot | Kaiheila | Bot ******** connected
07-12 23:56:09 [DEBUG] nonebot | Kaiheila | Bot ******** HeartBeat
07-12 23:56:35 [DEBUG] nonebot | Kaiheila | Bot ******** HeartBeat
07-12 23:57:01 [DEBUG] nonebot | Kaiheila | Bot ******** HeartBeat
```


--- 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/_block_driver.py`
Content:
```
1 import signal
2 import asyncio
3 import threading
4 from typing import Set, Callable, Awaitable
5 
6 from nonebot.log import logger
7 from nonebot.drivers import Driver
8 from nonebot.typing import overrides
9 from nonebot.config import Env, Config
10 
11 STARTUP_FUNC = Callable[[], Awaitable[None]]
12 SHUTDOWN_FUNC = Callable[[], Awaitable[None]]
13 HANDLED_SIGNALS = (
14     signal.SIGINT,  # Unix signal 2. Sent by Ctrl+C.
15     signal.SIGTERM,  # Unix signal 15. Sent by `kill <pid>`.
16 )
17 
18 
19 class BlockDriver(Driver):
20     def __init__(self, env: Env, config: Config):
21         super().__init__(env, config)
22         self.startup_funcs: Set[STARTUP_FUNC] = set()
23         self.shutdown_funcs: Set[SHUTDOWN_FUNC] = set()
24         self.should_exit: asyncio.Event = asyncio.Event()
25         self.force_exit: bool = False
26 
27     @property
28     @overrides(Driver)
29     def type(self) -> str:
30         """驱动名称: `block_driver`"""
31         return "block_driver"
32 
33     @property
34     @overrides(Driver)
35     def logger(self):
36         """block driver 使用的 logger"""
37         return logger
38 
39     @overrides(Driver)
40     def on_startup(self, func: STARTUP_FUNC) -> STARTUP_FUNC:
41         """
42         注册一个启动时执行的函数
43         """
44         self.startup_funcs.add(func)
45         return func
46 
47     @overrides(Driver)
48     def on_shutdown(self, func: SHUTDOWN_FUNC) -> SHUTDOWN_FUNC:
49         """
50         注册一个停止时执行的函数
51         """
52         self.shutdown_funcs.add(func)
53         return func
54 
55     @overrides(Driver)
56     def run(self, *args, **kwargs):
57         """启动 block driver"""
58         super().run(*args, **kwargs)
59         loop = asyncio.get_event_loop()
60         loop.run_until_complete(self.serve())
61 
62     async def serve(self):
63         self.install_signal_handlers()
64         await self.startup()
65         if self.should_exit.is_set():
66             return
67         await self.main_loop()
68         await self.shutdown()
69 
70     async def startup(self):
71         # run startup
72         cors = [startup() for startup in self.startup_funcs]
73         if cors:
74             try:
75                 await asyncio.gather(*cors)
76             except Exception as e:
77                 logger.opt(colors=True, exception=e).error(
78                     "<r><bg #f8bbd0>Error when running startup function. "
79                     "Ignored!</bg #f8bbd0></r>"
80                 )
81 
82         logger.info("Application startup completed.")
83 
84     async def main_loop(self):
85         await self.should_exit.wait()
86 
87     async def shutdown(self):
88         logger.info("Shutting down")
89 
90         logger.info("Waiting for application shutdown.")
91         # run shutdown
92         cors = [shutdown() for shutdown in self.shutdown_funcs]
93         if cors:
94             try:
95                 await asyncio.gather(*cors)
96             except Exception as e:
97                 logger.opt(colors=True, exception=e).error(
98                     "<r><bg #f8bbd0>Error when running shutdown function. "
99                     "Ignored!</bg #f8bbd0></r>"
100                 )
101 
102         for task in asyncio.all_tasks():
103             if task is not asyncio.current_task() and not task.done():
104                 task.cancel()
105         await asyncio.sleep(0.1)
106 
107         tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
108         if tasks and not self.force_exit:
109             logger.info("Waiting for tasks to finish. (CTRL+C to force quit)")
110         while tasks and not self.force_exit:
111             await asyncio.sleep(0.1)
112             tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
113 
114         for task in tasks:
115             task.cancel()
116 
117         await asyncio.gather(*tasks, return_exceptions=True)
118 
119         logger.info("Application shutdown complete.")
120         loop = asyncio.get_event_loop()
121         loop.stop()
122 
123     def install_signal_handlers(self) -> None:
124         if threading.current_thread() is not threading.main_thread():
125             # Signals can only be listened to from the main thread.
126             return
127 
128         loop = asyncio.get_event_loop()
129 
130         try:
131             for sig in HANDLED_SIGNALS:
132                 loop.add_signal_handler(sig, self.handle_exit, sig, None)
133         except NotImplementedError:
134             # Windows
135             for sig in HANDLED_SIGNALS:
136                 signal.signal(sig, self.handle_exit)
137 
138     def handle_exit(self, sig, frame):
139         if self.should_exit.is_set():
140             self.force_exit = True
141         else:
142             self.should_exit.set()
143
```
--- 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/_block_driver.py b/nonebot/drivers/_block_driver.py
--- a/nonebot/drivers/_block_driver.py
+++ b/nonebot/drivers/_block_driver.py
@@ -1,15 +1,16 @@
 import signal
 import asyncio
 import threading
-from typing import Set, Callable, Awaitable
+from typing import Set, Union, Callable, Awaitable
 
 from nonebot.log import logger
 from nonebot.drivers import Driver
 from nonebot.typing import overrides
 from nonebot.config import Env, Config
+from nonebot.utils import run_sync, is_coroutine_callable
 
-STARTUP_FUNC = Callable[[], Awaitable[None]]
-SHUTDOWN_FUNC = Callable[[], Awaitable[None]]
+STARTUP_FUNC = Callable[[], Union[None, Awaitable[None]]]
+SHUTDOWN_FUNC = Callable[[], Union[None, Awaitable[None]]]
 HANDLED_SIGNALS = (
     signal.SIGINT,  # Unix signal 2. Sent by Ctrl+C.
     signal.SIGTERM,  # Unix signal 15. Sent by `kill <pid>`.
@@ -69,7 +70,10 @@
 
     async def startup(self):
         # run startup
-        cors = [startup() for startup in self.startup_funcs]
+        cors = [
+            startup() if is_coroutine_callable(startup) else run_sync(startup)()
+            for startup in self.startup_funcs
+        ]
         if cors:
             try:
                 await asyncio.gather(*cors)
@@ -89,7 +93,10 @@
 
         logger.info("Waiting for application shutdown.")
         # run shutdown
-        cors = [shutdown() for shutdown in self.shutdown_funcs]
+        cors = [
+            shutdown() if is_coroutine_callable(shutdown) else run_sync(shutdown)()
+            for shutdown in self.shutdown_funcs
+        ]
         if cors:
             try:
                 await asyncio.gather(*cors)

{"golden_diff": "diff --git a/nonebot/drivers/_block_driver.py b/nonebot/drivers/_block_driver.py\n--- a/nonebot/drivers/_block_driver.py\n+++ b/nonebot/drivers/_block_driver.py\n@@ -1,15 +1,16 @@\n import signal\n import asyncio\n import threading\n-from typing import Set, Callable, Awaitable\n+from typing import Set, Union, Callable, Awaitable\n \n from nonebot.log import logger\n from nonebot.drivers import Driver\n from nonebot.typing import overrides\n from nonebot.config import Env, Config\n+from nonebot.utils import run_sync, is_coroutine_callable\n \n-STARTUP_FUNC = Callable[[], Awaitable[None]]\n-SHUTDOWN_FUNC = Callable[[], Awaitable[None]]\n+STARTUP_FUNC = Callable[[], Union[None, Awaitable[None]]]\n+SHUTDOWN_FUNC = Callable[[], Union[None, Awaitable[None]]]\n HANDLED_SIGNALS = (\n     signal.SIGINT,  # Unix signal 2. Sent by Ctrl+C.\n     signal.SIGTERM,  # Unix signal 15. Sent by `kill <pid>`.\n@@ -69,7 +70,10 @@\n \n     async def startup(self):\n         # run startup\n-        cors = [startup() for startup in self.startup_funcs]\n+        cors = [\n+            startup() if is_coroutine_callable(startup) else run_sync(startup)()\n+            for startup in self.startup_funcs\n+        ]\n         if cors:\n             try:\n                 await asyncio.gather(*cors)\n@@ -89,7 +93,10 @@\n \n         logger.info(\"Waiting for application shutdown.\")\n         # run shutdown\n-        cors = [shutdown() for shutdown in self.shutdown_funcs]\n+        cors = [\n+            shutdown() if is_coroutine_callable(shutdown) else run_sync(shutdown)()\n+            for shutdown in self.shutdown_funcs\n+        ]\n         if cors:\n             try:\n                 await asyncio.gather(*cors)\n", "issue": "Feature: \u6b63\u5411\u9a71\u52a8\u5668 startup/shutdown hook \u540c\u6b65\u51fd\u6570\u652f\u6301\n**\u63cf\u8ff0\u95ee\u9898\uff1a**\r\n\r\nBlockDriver\u6ce8\u518c\u540c\u6b65on_startup\u4e0eon_shutdown\u94a9\u5b50\u51fd\u6570\u65f6\u542f\u52a8\u5d29\u6e83\r\n\r\n**\u5982\u4f55\u590d\u73b0\uff1f**\r\n\r\n```python\r\nfrom nonebot import get_driver\r\n\r\n\r\n@get_driver().on_startup\r\ndef i_will_crash():\r\n    print(\"oops\")\r\n\r\n```\r\n\r\n**\u671f\u671b\u7684\u7ed3\u679c**\r\n\r\n\r\n\r\n**\u73af\u5883\u4fe1\u606f\uff1a**\r\n\r\n - OS: Windows 10\r\n - Python Version: 3.10\r\n - Nonebot Version: 2.0.0b4\r\n\r\n**\u534f\u8bae\u7aef\u4fe1\u606f\uff1a**\r\n\r\n - \u534f\u8bae\u7aef: Kaiheila\r\n - \u534f\u8bae\u7aef\u7248\u672c: 0.0.2\r\n\r\n**\u622a\u56fe\u6216\u65e5\u5fd7**\r\n```\r\n07-12 23:56:09 [SUCCESS] nonebot | NoneBot is initializing...\r\n07-12 23:56:09 [INFO] nonebot | Current Env: dev\r\n07-12 23:56:09 [DEBUG] nonebot | Loaded Config: {'driver': '~httpx+~websockets', 'host': IPv4Address('127.0.0.1'), 'port': 8080, 'log_level': 'DEBUG', 'api_timeout': 30.0, 'superusers': set(), 'nickname': set(), 'command_start': {'/'}, 'command_sep': {'.'}, 'session_expire_timeout': datetime.timedelta(seconds=120)}\r\n07-12 23:56:09 [DEBUG] nonebot | Succeeded to load adapter \"Kaiheila\"\r\n07-12 23:56:09 [SUCCESS] nonebot | Succeeded to import \"echo\"\r\n07-12 23:56:09 [SUCCESS] nonebot | Succeeded to import \"nonebot_plugin_pixivbot_kook\"\r\n07-12 23:56:09 [SUCCESS] nonebot | Succeeded to import \"test\"\r\n07-12 23:56:09 [WARNING] __main__ | Always use `nb run` to start the bot instead of manually running!\r\n07-12 23:56:09 [SUCCESS] nonebot | Running NoneBot...\r\n07-12 23:56:09 [DEBUG] nonebot | Loaded adapters: Kaiheila\r\noops\r\n07-12 23:56:09 [ERROR] nonebot | Error when running startup function. Ignored!\r\nTraceback (most recent call last):\r\n  File \"C:\\Users\\huang\\PycharmProjects\\kuraku-kook\\bot.py\", line 21, in <module>\r\n    nonebot.run()\r\n  File \"C:\\Users\\huang\\PycharmProjects\\kuraku-kook\\venv\\lib\\site-packages\\nonebot\\__init__.py\", line 261, in run\r\n    get_driver().run(*args, **kwargs)\r\n  File \"C:\\Users\\huang\\PycharmProjects\\kuraku-kook\\venv\\lib\\site-packages\\nonebot\\drivers\\_block_driver.py\", line 60, in run\r\n    loop.run_until_complete(self.serve())\r\n  File \"C:\\Users\\huang\\AppData\\Local\\Programs\\Python\\Python310\\lib\\asyncio\\base_events.py\", line 628, in run_until_complete\r\n    self.run_forever()\r\n  File \"C:\\Users\\huang\\AppData\\Local\\Programs\\Python\\Python310\\lib\\asyncio\\windows_events.py\", line 321, in run_forever\r\n    super().run_forever()\r\n  File \"C:\\Users\\huang\\AppData\\Local\\Programs\\Python\\Python310\\lib\\asyncio\\base_events.py\", line 595, in run_forever\r\n    self._run_once()\r\n  File \"C:\\Users\\huang\\AppData\\Local\\Programs\\Python\\Python310\\lib\\asyncio\\base_events.py\", line 1881, in _run_once\r\n    handle._run()\r\n  File \"C:\\Users\\huang\\AppData\\Local\\Programs\\Python\\Python310\\lib\\asyncio\\events.py\", line 80, in _run\r\n    self._context.run(self._callback, *self._args)\r\n  File \"C:\\Users\\huang\\PycharmProjects\\kuraku-kook\\venv\\lib\\site-packages\\nonebot\\drivers\\_block_driver.py\", line 64, in serve\r\n    await self.startup()\r\n> File \"C:\\Users\\huang\\PycharmProjects\\kuraku-kook\\venv\\lib\\site-packages\\nonebot\\drivers\\_block_driver.py\", line 75, in startup\r\n    await asyncio.gather(*cors)\r\n  File \"C:\\Users\\huang\\AppData\\Local\\Programs\\Python\\Python310\\lib\\asyncio\\tasks.py\", line 776, in gather\r\n    fut = _ensure_future(arg, loop=loop)\r\n  File \"C:\\Users\\huang\\AppData\\Local\\Programs\\Python\\Python310\\lib\\asyncio\\tasks.py\", line 629, in _ensure_future\r\n    raise TypeError('An asyncio.Future, a coroutine or an awaitable '\r\nTypeError: An asyncio.Future, a coroutine or an awaitable is required\r\n07-12 23:56:09 [INFO] nonebot | Application startup completed.\r\nException in callback gather.<locals>._done_callback(<Task finishe...> result=None>) at C:\\Users\\huang\\AppData\\Local\\Programs\\Python\\Python310\\lib\\asyncio\\tasks.py:714\r\nhandle: <Handle gather.<locals>._done_callback(<Task finishe...> result=None>) at C:\\Users\\huang\\AppData\\Local\\Programs\\Python\\Python310\\lib\\asyncio\\tasks.py:714>\r\nTraceback (most recent call last):\r\n  File \"C:\\Users\\huang\\AppData\\Local\\Programs\\Python\\Python310\\lib\\asyncio\\events.py\", line 80, in _run\r\n    self._context.run(self._callback, *self._args)\r\n  File \"C:\\Users\\huang\\AppData\\Local\\Programs\\Python\\Python310\\lib\\asyncio\\tasks.py\", line 718, in _done_callback\r\n    if outer.done():\r\nNameError: free variable 'outer' referenced before assignment in enclosing scope\r\n07-12 23:56:09 [DEBUG] nonebot | Kaiheila | WebSocket Connection to wss://ws.kaiheila.cn/gateway?compress=0&token=***************** established\r\n07-12 23:56:09 [INFO] nonebot | Kaiheila | Bot ******** connected\r\n07-12 23:56:09 [DEBUG] nonebot | Kaiheila | Bot ******** HeartBeat\r\n07-12 23:56:35 [DEBUG] nonebot | Kaiheila | Bot ******** HeartBeat\r\n07-12 23:57:01 [DEBUG] nonebot | Kaiheila | Bot ******** HeartBeat\r\n```\r\n\n", "before_files": [{"content": "import signal\nimport asyncio\nimport threading\nfrom typing import Set, Callable, Awaitable\n\nfrom nonebot.log import logger\nfrom nonebot.drivers import Driver\nfrom nonebot.typing import overrides\nfrom nonebot.config import Env, Config\n\nSTARTUP_FUNC = Callable[[], Awaitable[None]]\nSHUTDOWN_FUNC = Callable[[], Awaitable[None]]\nHANDLED_SIGNALS = (\n    signal.SIGINT,  # Unix signal 2. Sent by Ctrl+C.\n    signal.SIGTERM,  # Unix signal 15. Sent by `kill <pid>`.\n)\n\n\nclass BlockDriver(Driver):\n    def __init__(self, env: Env, config: Config):\n        super().__init__(env, config)\n        self.startup_funcs: Set[STARTUP_FUNC] = set()\n        self.shutdown_funcs: Set[SHUTDOWN_FUNC] = set()\n        self.should_exit: asyncio.Event = asyncio.Event()\n        self.force_exit: bool = False\n\n    @property\n    @overrides(Driver)\n    def type(self) -> str:\n        \"\"\"\u9a71\u52a8\u540d\u79f0: `block_driver`\"\"\"\n        return \"block_driver\"\n\n    @property\n    @overrides(Driver)\n    def logger(self):\n        \"\"\"block driver \u4f7f\u7528\u7684 logger\"\"\"\n        return logger\n\n    @overrides(Driver)\n    def on_startup(self, func: STARTUP_FUNC) -> STARTUP_FUNC:\n        \"\"\"\n        \u6ce8\u518c\u4e00\u4e2a\u542f\u52a8\u65f6\u6267\u884c\u7684\u51fd\u6570\n        \"\"\"\n        self.startup_funcs.add(func)\n        return func\n\n    @overrides(Driver)\n    def on_shutdown(self, func: SHUTDOWN_FUNC) -> SHUTDOWN_FUNC:\n        \"\"\"\n        \u6ce8\u518c\u4e00\u4e2a\u505c\u6b62\u65f6\u6267\u884c\u7684\u51fd\u6570\n        \"\"\"\n        self.shutdown_funcs.add(func)\n        return func\n\n    @overrides(Driver)\n    def run(self, *args, **kwargs):\n        \"\"\"\u542f\u52a8 block driver\"\"\"\n        super().run(*args, **kwargs)\n        loop = asyncio.get_event_loop()\n        loop.run_until_complete(self.serve())\n\n    async def serve(self):\n        self.install_signal_handlers()\n        await self.startup()\n        if self.should_exit.is_set():\n            return\n        await self.main_loop()\n        await self.shutdown()\n\n    async def startup(self):\n        # run startup\n        cors = [startup() for startup in self.startup_funcs]\n        if cors:\n            try:\n                await asyncio.gather(*cors)\n            except Exception as e:\n                logger.opt(colors=True, exception=e).error(\n                    \"<r><bg #f8bbd0>Error when running startup function. \"\n                    \"Ignored!</bg #f8bbd0></r>\"\n                )\n\n        logger.info(\"Application startup completed.\")\n\n    async def main_loop(self):\n        await self.should_exit.wait()\n\n    async def shutdown(self):\n        logger.info(\"Shutting down\")\n\n        logger.info(\"Waiting for application shutdown.\")\n        # run shutdown\n        cors = [shutdown() for shutdown in self.shutdown_funcs]\n        if cors:\n            try:\n                await asyncio.gather(*cors)\n            except Exception as e:\n                logger.opt(colors=True, exception=e).error(\n                    \"<r><bg #f8bbd0>Error when running shutdown function. \"\n                    \"Ignored!</bg #f8bbd0></r>\"\n                )\n\n        for task in asyncio.all_tasks():\n            if task is not asyncio.current_task() and not task.done():\n                task.cancel()\n        await asyncio.sleep(0.1)\n\n        tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]\n        if tasks and not self.force_exit:\n            logger.info(\"Waiting for tasks to finish. (CTRL+C to force quit)\")\n        while tasks and not self.force_exit:\n            await asyncio.sleep(0.1)\n            tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]\n\n        for task in tasks:\n            task.cancel()\n\n        await asyncio.gather(*tasks, return_exceptions=True)\n\n        logger.info(\"Application shutdown complete.\")\n        loop = asyncio.get_event_loop()\n        loop.stop()\n\n    def install_signal_handlers(self) -> None:\n        if threading.current_thread() is not threading.main_thread():\n            # Signals can only be listened to from the main thread.\n            return\n\n        loop = asyncio.get_event_loop()\n\n        try:\n            for sig in HANDLED_SIGNALS:\n                loop.add_signal_handler(sig, self.handle_exit, sig, None)\n        except NotImplementedError:\n            # Windows\n            for sig in HANDLED_SIGNALS:\n                signal.signal(sig, self.handle_exit)\n\n    def handle_exit(self, sig, frame):\n        if self.should_exit.is_set():\n            self.force_exit = True\n        else:\n            self.should_exit.set()\n", "path": "nonebot/drivers/_block_driver.py"}], "after_files": [{"content": "import signal\nimport asyncio\nimport threading\nfrom typing import Set, Union, Callable, Awaitable\n\nfrom nonebot.log import logger\nfrom nonebot.drivers import Driver\nfrom nonebot.typing import overrides\nfrom nonebot.config import Env, Config\nfrom nonebot.utils import run_sync, is_coroutine_callable\n\nSTARTUP_FUNC = Callable[[], Union[None, Awaitable[None]]]\nSHUTDOWN_FUNC = Callable[[], Union[None, Awaitable[None]]]\nHANDLED_SIGNALS = (\n    signal.SIGINT,  # Unix signal 2. Sent by Ctrl+C.\n    signal.SIGTERM,  # Unix signal 15. Sent by `kill <pid>`.\n)\n\n\nclass BlockDriver(Driver):\n    def __init__(self, env: Env, config: Config):\n        super().__init__(env, config)\n        self.startup_funcs: Set[STARTUP_FUNC] = set()\n        self.shutdown_funcs: Set[SHUTDOWN_FUNC] = set()\n        self.should_exit: asyncio.Event = asyncio.Event()\n        self.force_exit: bool = False\n\n    @property\n    @overrides(Driver)\n    def type(self) -> str:\n        \"\"\"\u9a71\u52a8\u540d\u79f0: `block_driver`\"\"\"\n        return \"block_driver\"\n\n    @property\n    @overrides(Driver)\n    def logger(self):\n        \"\"\"block driver \u4f7f\u7528\u7684 logger\"\"\"\n        return logger\n\n    @overrides(Driver)\n    def on_startup(self, func: STARTUP_FUNC) -> STARTUP_FUNC:\n        \"\"\"\n        \u6ce8\u518c\u4e00\u4e2a\u542f\u52a8\u65f6\u6267\u884c\u7684\u51fd\u6570\n        \"\"\"\n        self.startup_funcs.add(func)\n        return func\n\n    @overrides(Driver)\n    def on_shutdown(self, func: SHUTDOWN_FUNC) -> SHUTDOWN_FUNC:\n        \"\"\"\n        \u6ce8\u518c\u4e00\u4e2a\u505c\u6b62\u65f6\u6267\u884c\u7684\u51fd\u6570\n        \"\"\"\n        self.shutdown_funcs.add(func)\n        return func\n\n    @overrides(Driver)\n    def run(self, *args, **kwargs):\n        \"\"\"\u542f\u52a8 block driver\"\"\"\n        super().run(*args, **kwargs)\n        loop = asyncio.get_event_loop()\n        loop.run_until_complete(self.serve())\n\n    async def serve(self):\n        self.install_signal_handlers()\n        await self.startup()\n        if self.should_exit.is_set():\n            return\n        await self.main_loop()\n        await self.shutdown()\n\n    async def startup(self):\n        # run startup\n        cors = [\n            startup() if is_coroutine_callable(startup) else run_sync(startup)()\n            for startup in self.startup_funcs\n        ]\n        if cors:\n            try:\n                await asyncio.gather(*cors)\n            except Exception as e:\n                logger.opt(colors=True, exception=e).error(\n                    \"<r><bg #f8bbd0>Error when running startup function. \"\n                    \"Ignored!</bg #f8bbd0></r>\"\n                )\n\n        logger.info(\"Application startup completed.\")\n\n    async def main_loop(self):\n        await self.should_exit.wait()\n\n    async def shutdown(self):\n        logger.info(\"Shutting down\")\n\n        logger.info(\"Waiting for application shutdown.\")\n        # run shutdown\n        cors = [\n            shutdown() if is_coroutine_callable(shutdown) else run_sync(shutdown)()\n            for shutdown in self.shutdown_funcs\n        ]\n        if cors:\n            try:\n                await asyncio.gather(*cors)\n            except Exception as e:\n                logger.opt(colors=True, exception=e).error(\n                    \"<r><bg #f8bbd0>Error when running shutdown function. \"\n                    \"Ignored!</bg #f8bbd0></r>\"\n                )\n\n        for task in asyncio.all_tasks():\n            if task is not asyncio.current_task() and not task.done():\n                task.cancel()\n        await asyncio.sleep(0.1)\n\n        tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]\n        if tasks and not self.force_exit:\n            logger.info(\"Waiting for tasks to finish. (CTRL+C to force quit)\")\n        while tasks and not self.force_exit:\n            await asyncio.sleep(0.1)\n            tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]\n\n        for task in tasks:\n            task.cancel()\n\n        await asyncio.gather(*tasks, return_exceptions=True)\n\n        logger.info(\"Application shutdown complete.\")\n        loop = asyncio.get_event_loop()\n        loop.stop()\n\n    def install_signal_handlers(self) -> None:\n        if threading.current_thread() is not threading.main_thread():\n            # Signals can only be listened to from the main thread.\n            return\n\n        loop = asyncio.get_event_loop()\n\n        try:\n            for sig in HANDLED_SIGNALS:\n                loop.add_signal_handler(sig, self.handle_exit, sig, None)\n        except NotImplementedError:\n            # Windows\n            for sig in HANDLED_SIGNALS:\n                signal.signal(sig, self.handle_exit)\n\n    def handle_exit(self, sig, frame):\n        if self.should_exit.is_set():\n            self.force_exit = True\n        else:\n            self.should_exit.set()\n", "path": "nonebot/drivers/_block_driver.py"}]}