We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Bug: run_sync忽略了上下文变量
### 操作系统

Windows

### Python 版本

3.11.0

### NoneBot 版本

2.0.0rc4

### 适配器

-

### 协议端

-

### 描述问题

[run_sync](https://github.com/nonebot/nonebot2/blob/e98d28f3b4fdda2504ecc07318563ce202464b96/nonebot/utils.py#L114)忽略了上下文变量，可能会导致异常，比如https://github.com/nonebot/nonebot2/issues/1966

### 复现步骤

-

### 期望的结果

使用```copy_context```然后```ctx.run```进executor

### 截图或日志

-

--- 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/utils.py`
Content:
```
1 """本模块包含了 NoneBot 的一些工具函数
2 
3 FrontMatter:
4     sidebar_position: 8
5     description: nonebot.utils 模块
6 """
7 
8 import re
9 import json
10 import asyncio
11 import inspect
12 import importlib
13 import dataclasses
14 from pathlib import Path
15 from functools import wraps, partial
16 from contextlib import asynccontextmanager
17 from typing_extensions import ParamSpec, get_args, get_origin
18 from typing import (
19     Any,
20     Type,
21     Tuple,
22     Union,
23     TypeVar,
24     Callable,
25     Optional,
26     Coroutine,
27     AsyncGenerator,
28     ContextManager,
29     overload,
30 )
31 
32 from pydantic.typing import is_union, is_none_type
33 
34 from nonebot.log import logger
35 from nonebot.typing import overrides
36 
37 P = ParamSpec("P")
38 R = TypeVar("R")
39 T = TypeVar("T")
40 K = TypeVar("K")
41 V = TypeVar("V")
42 
43 
44 def escape_tag(s: str) -> str:
45     """用于记录带颜色日志时转义 `<tag>` 类型特殊标签
46 
47     参考: [loguru color 标签](https://loguru.readthedocs.io/en/stable/api/logger.html#color)
48 
49     参数:
50         s: 需要转义的字符串
51     """
52     return re.sub(r"</?((?:[fb]g\s)?[^<>\s]*)>", r"\\\g<0>", s)
53 
54 
55 def generic_check_issubclass(
56     cls: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...]]
57 ) -> bool:
58     """检查 cls 是否是 class_or_tuple 中的一个类型子类。
59 
60     特别的，如果 cls 是 `typing.Union` 或 `types.UnionType` 类型，
61     则会检查其中的类型是否是 class_or_tuple 中的一个类型子类。（None 会被忽略）
62     """
63     try:
64         return issubclass(cls, class_or_tuple)
65     except TypeError:
66         origin = get_origin(cls)
67         if is_union(origin):
68             return all(
69                 is_none_type(type_) or generic_check_issubclass(type_, class_or_tuple)
70                 for type_ in get_args(cls)
71             )
72         elif origin:
73             return issubclass(origin, class_or_tuple)
74         return False
75 
76 
77 def is_coroutine_callable(call: Callable[..., Any]) -> bool:
78     """检查 call 是否是一个 callable 协程函数"""
79     if inspect.isroutine(call):
80         return inspect.iscoroutinefunction(call)
81     if inspect.isclass(call):
82         return False
83     func_ = getattr(call, "__call__", None)
84     return inspect.iscoroutinefunction(func_)
85 
86 
87 def is_gen_callable(call: Callable[..., Any]) -> bool:
88     """检查 call 是否是一个生成器函数"""
89     if inspect.isgeneratorfunction(call):
90         return True
91     func_ = getattr(call, "__call__", None)
92     return inspect.isgeneratorfunction(func_)
93 
94 
95 def is_async_gen_callable(call: Callable[..., Any]) -> bool:
96     """检查 call 是否是一个异步生成器函数"""
97     if inspect.isasyncgenfunction(call):
98         return True
99     func_ = getattr(call, "__call__", None)
100     return inspect.isasyncgenfunction(func_)
101 
102 
103 def run_sync(call: Callable[P, R]) -> Callable[P, Coroutine[None, None, R]]:
104     """一个用于包装 sync function 为 async function 的装饰器
105 
106     参数:
107         call: 被装饰的同步函数
108     """
109 
110     @wraps(call)
111     async def _wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
112         loop = asyncio.get_running_loop()
113         pfunc = partial(call, *args, **kwargs)
114         result = await loop.run_in_executor(None, pfunc)
115         return result
116 
117     return _wrapper
118 
119 
120 @asynccontextmanager
121 async def run_sync_ctx_manager(
122     cm: ContextManager[T],
123 ) -> AsyncGenerator[T, None]:
124     """一个用于包装 sync context manager 为 async context manager 的执行函数"""
125     try:
126         yield await run_sync(cm.__enter__)()
127     except Exception as e:
128         ok = await run_sync(cm.__exit__)(type(e), e, None)
129         if not ok:
130             raise e
131     else:
132         await run_sync(cm.__exit__)(None, None, None)
133 
134 
135 @overload
136 async def run_coro_with_catch(
137     coro: Coroutine[Any, Any, T],
138     exc: Tuple[Type[Exception], ...],
139 ) -> Union[T, None]:
140     ...
141 
142 
143 @overload
144 async def run_coro_with_catch(
145     coro: Coroutine[Any, Any, T],
146     exc: Tuple[Type[Exception], ...],
147     return_on_err: R,
148 ) -> Union[T, R]:
149     ...
150 
151 
152 async def run_coro_with_catch(
153     coro: Coroutine[Any, Any, T],
154     exc: Tuple[Type[Exception], ...],
155     return_on_err: Optional[R] = None,
156 ) -> Optional[Union[T, R]]:
157     try:
158         return await coro
159     except exc:
160         return return_on_err
161 
162 
163 def get_name(obj: Any) -> str:
164     """获取对象的名称"""
165     if inspect.isfunction(obj) or inspect.isclass(obj):
166         return obj.__name__
167     return obj.__class__.__name__
168 
169 
170 def path_to_module_name(path: Path) -> str:
171     """转换路径为模块名"""
172     rel_path = path.resolve().relative_to(Path.cwd().resolve())
173     if rel_path.stem == "__init__":
174         return ".".join(rel_path.parts[:-1])
175     else:
176         return ".".join(rel_path.parts[:-1] + (rel_path.stem,))
177 
178 
179 def resolve_dot_notation(
180     obj_str: str, default_attr: str, default_prefix: Optional[str] = None
181 ) -> Any:
182     """解析并导入点分表示法的对象"""
183     modulename, _, cls = obj_str.partition(":")
184     if default_prefix is not None and modulename.startswith("~"):
185         modulename = default_prefix + modulename[1:]
186     module = importlib.import_module(modulename)
187     if not cls:
188         return getattr(module, default_attr)
189     instance = module
190     for attr_str in cls.split("."):
191         instance = getattr(instance, attr_str)
192     return instance
193 
194 
195 class DataclassEncoder(json.JSONEncoder):
196     """在JSON序列化 {re}`nonebot.adapters._message.Message` (List[Dataclass]) 时使用的 `JSONEncoder`"""
197 
198     @overrides(json.JSONEncoder)
199     def default(self, o):
200         if dataclasses.is_dataclass(o):
201             return {f.name: getattr(o, f.name) for f in dataclasses.fields(o)}
202         return super().default(o)
203 
204 
205 def logger_wrapper(logger_name: str):
206     """用于打印 adapter 的日志。
207 
208     参数:
209         logger_name: adapter 的名称
210 
211     返回:
212         日志记录函数
213 
214             - level: 日志等级
215             - message: 日志信息
216             - exception: 异常信息
217     """
218 
219     def log(level: str, message: str, exception: Optional[Exception] = None):
220         logger.opt(colors=True, exception=exception).log(
221             level, f"<m>{escape_tag(logger_name)}</m> | {message}"
222         )
223 
224     return log
225
```
--- 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/utils.py b/nonebot/utils.py
--- a/nonebot/utils.py
+++ b/nonebot/utils.py
@@ -12,6 +12,7 @@
 import importlib
 import dataclasses
 from pathlib import Path
+from contextvars import copy_context
 from functools import wraps, partial
 from contextlib import asynccontextmanager
 from typing_extensions import ParamSpec, get_args, get_origin
@@ -111,7 +112,9 @@
     async def _wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
         loop = asyncio.get_running_loop()
         pfunc = partial(call, *args, **kwargs)
-        result = await loop.run_in_executor(None, pfunc)
+        context = copy_context()
+        context_run = context.run
+        result = await loop.run_in_executor(None, context_run, pfunc)
         return result
 
     return _wrapper

{"golden_diff": "diff --git a/nonebot/utils.py b/nonebot/utils.py\n--- a/nonebot/utils.py\n+++ b/nonebot/utils.py\n@@ -12,6 +12,7 @@\n import importlib\n import dataclasses\n from pathlib import Path\n+from contextvars import copy_context\n from functools import wraps, partial\n from contextlib import asynccontextmanager\n from typing_extensions import ParamSpec, get_args, get_origin\n@@ -111,7 +112,9 @@\n     async def _wrapper(*args: P.args, **kwargs: P.kwargs) -> R:\n         loop = asyncio.get_running_loop()\n         pfunc = partial(call, *args, **kwargs)\n-        result = await loop.run_in_executor(None, pfunc)\n+        context = copy_context()\n+        context_run = context.run\n+        result = await loop.run_in_executor(None, context_run, pfunc)\n         return result\n \n     return _wrapper\n", "issue": "Bug: run_sync\u5ffd\u7565\u4e86\u4e0a\u4e0b\u6587\u53d8\u91cf\n### \u64cd\u4f5c\u7cfb\u7edf\n\nWindows\n\n### Python \u7248\u672c\n\n3.11.0\n\n### NoneBot \u7248\u672c\n\n2.0.0rc4\n\n### \u9002\u914d\u5668\n\n-\n\n### \u534f\u8bae\u7aef\n\n-\n\n### \u63cf\u8ff0\u95ee\u9898\n\n[run_sync](https://github.com/nonebot/nonebot2/blob/e98d28f3b4fdda2504ecc07318563ce202464b96/nonebot/utils.py#L114)\u5ffd\u7565\u4e86\u4e0a\u4e0b\u6587\u53d8\u91cf\uff0c\u53ef\u80fd\u4f1a\u5bfc\u81f4\u5f02\u5e38\uff0c\u6bd4\u5982https://github.com/nonebot/nonebot2/issues/1966\n\n### \u590d\u73b0\u6b65\u9aa4\n\n-\n\n### \u671f\u671b\u7684\u7ed3\u679c\n\n\u4f7f\u7528```copy_context```\u7136\u540e```ctx.run```\u8fdbexecutor\n\n### \u622a\u56fe\u6216\u65e5\u5fd7\n\n-\n", "before_files": [{"content": "\"\"\"\u672c\u6a21\u5757\u5305\u542b\u4e86 NoneBot \u7684\u4e00\u4e9b\u5de5\u5177\u51fd\u6570\n\nFrontMatter:\n    sidebar_position: 8\n    description: nonebot.utils \u6a21\u5757\n\"\"\"\n\nimport re\nimport json\nimport asyncio\nimport inspect\nimport importlib\nimport dataclasses\nfrom pathlib import Path\nfrom functools import wraps, partial\nfrom contextlib import asynccontextmanager\nfrom typing_extensions import ParamSpec, get_args, get_origin\nfrom typing import (\n    Any,\n    Type,\n    Tuple,\n    Union,\n    TypeVar,\n    Callable,\n    Optional,\n    Coroutine,\n    AsyncGenerator,\n    ContextManager,\n    overload,\n)\n\nfrom pydantic.typing import is_union, is_none_type\n\nfrom nonebot.log import logger\nfrom nonebot.typing import overrides\n\nP = ParamSpec(\"P\")\nR = TypeVar(\"R\")\nT = TypeVar(\"T\")\nK = TypeVar(\"K\")\nV = TypeVar(\"V\")\n\n\ndef escape_tag(s: str) -> str:\n    \"\"\"\u7528\u4e8e\u8bb0\u5f55\u5e26\u989c\u8272\u65e5\u5fd7\u65f6\u8f6c\u4e49 `<tag>` \u7c7b\u578b\u7279\u6b8a\u6807\u7b7e\n\n    \u53c2\u8003: [loguru color \u6807\u7b7e](https://loguru.readthedocs.io/en/stable/api/logger.html#color)\n\n    \u53c2\u6570:\n        s: \u9700\u8981\u8f6c\u4e49\u7684\u5b57\u7b26\u4e32\n    \"\"\"\n    return re.sub(r\"</?((?:[fb]g\\s)?[^<>\\s]*)>\", r\"\\\\\\g<0>\", s)\n\n\ndef generic_check_issubclass(\n    cls: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...]]\n) -> bool:\n    \"\"\"\u68c0\u67e5 cls \u662f\u5426\u662f class_or_tuple \u4e2d\u7684\u4e00\u4e2a\u7c7b\u578b\u5b50\u7c7b\u3002\n\n    \u7279\u522b\u7684\uff0c\u5982\u679c cls \u662f `typing.Union` \u6216 `types.UnionType` \u7c7b\u578b\uff0c\n    \u5219\u4f1a\u68c0\u67e5\u5176\u4e2d\u7684\u7c7b\u578b\u662f\u5426\u662f class_or_tuple \u4e2d\u7684\u4e00\u4e2a\u7c7b\u578b\u5b50\u7c7b\u3002\uff08None \u4f1a\u88ab\u5ffd\u7565\uff09\n    \"\"\"\n    try:\n        return issubclass(cls, class_or_tuple)\n    except TypeError:\n        origin = get_origin(cls)\n        if is_union(origin):\n            return all(\n                is_none_type(type_) or generic_check_issubclass(type_, class_or_tuple)\n                for type_ in get_args(cls)\n            )\n        elif origin:\n            return issubclass(origin, class_or_tuple)\n        return False\n\n\ndef is_coroutine_callable(call: Callable[..., Any]) -> bool:\n    \"\"\"\u68c0\u67e5 call \u662f\u5426\u662f\u4e00\u4e2a callable \u534f\u7a0b\u51fd\u6570\"\"\"\n    if inspect.isroutine(call):\n        return inspect.iscoroutinefunction(call)\n    if inspect.isclass(call):\n        return False\n    func_ = getattr(call, \"__call__\", None)\n    return inspect.iscoroutinefunction(func_)\n\n\ndef is_gen_callable(call: Callable[..., Any]) -> bool:\n    \"\"\"\u68c0\u67e5 call \u662f\u5426\u662f\u4e00\u4e2a\u751f\u6210\u5668\u51fd\u6570\"\"\"\n    if inspect.isgeneratorfunction(call):\n        return True\n    func_ = getattr(call, \"__call__\", None)\n    return inspect.isgeneratorfunction(func_)\n\n\ndef is_async_gen_callable(call: Callable[..., Any]) -> bool:\n    \"\"\"\u68c0\u67e5 call \u662f\u5426\u662f\u4e00\u4e2a\u5f02\u6b65\u751f\u6210\u5668\u51fd\u6570\"\"\"\n    if inspect.isasyncgenfunction(call):\n        return True\n    func_ = getattr(call, \"__call__\", None)\n    return inspect.isasyncgenfunction(func_)\n\n\ndef run_sync(call: Callable[P, R]) -> Callable[P, Coroutine[None, None, R]]:\n    \"\"\"\u4e00\u4e2a\u7528\u4e8e\u5305\u88c5 sync function \u4e3a async function \u7684\u88c5\u9970\u5668\n\n    \u53c2\u6570:\n        call: \u88ab\u88c5\u9970\u7684\u540c\u6b65\u51fd\u6570\n    \"\"\"\n\n    @wraps(call)\n    async def _wrapper(*args: P.args, **kwargs: P.kwargs) -> R:\n        loop = asyncio.get_running_loop()\n        pfunc = partial(call, *args, **kwargs)\n        result = await loop.run_in_executor(None, pfunc)\n        return result\n\n    return _wrapper\n\n\n@asynccontextmanager\nasync def run_sync_ctx_manager(\n    cm: ContextManager[T],\n) -> AsyncGenerator[T, None]:\n    \"\"\"\u4e00\u4e2a\u7528\u4e8e\u5305\u88c5 sync context manager \u4e3a async context manager \u7684\u6267\u884c\u51fd\u6570\"\"\"\n    try:\n        yield await run_sync(cm.__enter__)()\n    except Exception as e:\n        ok = await run_sync(cm.__exit__)(type(e), e, None)\n        if not ok:\n            raise e\n    else:\n        await run_sync(cm.__exit__)(None, None, None)\n\n\n@overload\nasync def run_coro_with_catch(\n    coro: Coroutine[Any, Any, T],\n    exc: Tuple[Type[Exception], ...],\n) -> Union[T, None]:\n    ...\n\n\n@overload\nasync def run_coro_with_catch(\n    coro: Coroutine[Any, Any, T],\n    exc: Tuple[Type[Exception], ...],\n    return_on_err: R,\n) -> Union[T, R]:\n    ...\n\n\nasync def run_coro_with_catch(\n    coro: Coroutine[Any, Any, T],\n    exc: Tuple[Type[Exception], ...],\n    return_on_err: Optional[R] = None,\n) -> Optional[Union[T, R]]:\n    try:\n        return await coro\n    except exc:\n        return return_on_err\n\n\ndef get_name(obj: Any) -> str:\n    \"\"\"\u83b7\u53d6\u5bf9\u8c61\u7684\u540d\u79f0\"\"\"\n    if inspect.isfunction(obj) or inspect.isclass(obj):\n        return obj.__name__\n    return obj.__class__.__name__\n\n\ndef path_to_module_name(path: Path) -> str:\n    \"\"\"\u8f6c\u6362\u8def\u5f84\u4e3a\u6a21\u5757\u540d\"\"\"\n    rel_path = path.resolve().relative_to(Path.cwd().resolve())\n    if rel_path.stem == \"__init__\":\n        return \".\".join(rel_path.parts[:-1])\n    else:\n        return \".\".join(rel_path.parts[:-1] + (rel_path.stem,))\n\n\ndef resolve_dot_notation(\n    obj_str: str, default_attr: str, default_prefix: Optional[str] = None\n) -> Any:\n    \"\"\"\u89e3\u6790\u5e76\u5bfc\u5165\u70b9\u5206\u8868\u793a\u6cd5\u7684\u5bf9\u8c61\"\"\"\n    modulename, _, cls = obj_str.partition(\":\")\n    if default_prefix is not None and modulename.startswith(\"~\"):\n        modulename = default_prefix + modulename[1:]\n    module = importlib.import_module(modulename)\n    if not cls:\n        return getattr(module, default_attr)\n    instance = module\n    for attr_str in cls.split(\".\"):\n        instance = getattr(instance, attr_str)\n    return instance\n\n\nclass DataclassEncoder(json.JSONEncoder):\n    \"\"\"\u5728JSON\u5e8f\u5217\u5316 {re}`nonebot.adapters._message.Message` (List[Dataclass]) \u65f6\u4f7f\u7528\u7684 `JSONEncoder`\"\"\"\n\n    @overrides(json.JSONEncoder)\n    def default(self, o):\n        if dataclasses.is_dataclass(o):\n            return {f.name: getattr(o, f.name) for f in dataclasses.fields(o)}\n        return super().default(o)\n\n\ndef logger_wrapper(logger_name: str):\n    \"\"\"\u7528\u4e8e\u6253\u5370 adapter \u7684\u65e5\u5fd7\u3002\n\n    \u53c2\u6570:\n        logger_name: adapter \u7684\u540d\u79f0\n\n    \u8fd4\u56de:\n        \u65e5\u5fd7\u8bb0\u5f55\u51fd\u6570\n\n            - level: \u65e5\u5fd7\u7b49\u7ea7\n            - message: \u65e5\u5fd7\u4fe1\u606f\n            - exception: \u5f02\u5e38\u4fe1\u606f\n    \"\"\"\n\n    def log(level: str, message: str, exception: Optional[Exception] = None):\n        logger.opt(colors=True, exception=exception).log(\n            level, f\"<m>{escape_tag(logger_name)}</m> | {message}\"\n        )\n\n    return log\n", "path": "nonebot/utils.py"}], "after_files": [{"content": "\"\"\"\u672c\u6a21\u5757\u5305\u542b\u4e86 NoneBot \u7684\u4e00\u4e9b\u5de5\u5177\u51fd\u6570\n\nFrontMatter:\n    sidebar_position: 8\n    description: nonebot.utils \u6a21\u5757\n\"\"\"\n\nimport re\nimport json\nimport asyncio\nimport inspect\nimport importlib\nimport dataclasses\nfrom pathlib import Path\nfrom contextvars import copy_context\nfrom functools import wraps, partial\nfrom contextlib import asynccontextmanager\nfrom typing_extensions import ParamSpec, get_args, get_origin\nfrom typing import (\n    Any,\n    Type,\n    Tuple,\n    Union,\n    TypeVar,\n    Callable,\n    Optional,\n    Coroutine,\n    AsyncGenerator,\n    ContextManager,\n    overload,\n)\n\nfrom pydantic.typing import is_union, is_none_type\n\nfrom nonebot.log import logger\nfrom nonebot.typing import overrides\n\nP = ParamSpec(\"P\")\nR = TypeVar(\"R\")\nT = TypeVar(\"T\")\nK = TypeVar(\"K\")\nV = TypeVar(\"V\")\n\n\ndef escape_tag(s: str) -> str:\n    \"\"\"\u7528\u4e8e\u8bb0\u5f55\u5e26\u989c\u8272\u65e5\u5fd7\u65f6\u8f6c\u4e49 `<tag>` \u7c7b\u578b\u7279\u6b8a\u6807\u7b7e\n\n    \u53c2\u8003: [loguru color \u6807\u7b7e](https://loguru.readthedocs.io/en/stable/api/logger.html#color)\n\n    \u53c2\u6570:\n        s: \u9700\u8981\u8f6c\u4e49\u7684\u5b57\u7b26\u4e32\n    \"\"\"\n    return re.sub(r\"</?((?:[fb]g\\s)?[^<>\\s]*)>\", r\"\\\\\\g<0>\", s)\n\n\ndef generic_check_issubclass(\n    cls: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...]]\n) -> bool:\n    \"\"\"\u68c0\u67e5 cls \u662f\u5426\u662f class_or_tuple \u4e2d\u7684\u4e00\u4e2a\u7c7b\u578b\u5b50\u7c7b\u3002\n\n    \u7279\u522b\u7684\uff0c\u5982\u679c cls \u662f `typing.Union` \u6216 `types.UnionType` \u7c7b\u578b\uff0c\n    \u5219\u4f1a\u68c0\u67e5\u5176\u4e2d\u7684\u7c7b\u578b\u662f\u5426\u662f class_or_tuple \u4e2d\u7684\u4e00\u4e2a\u7c7b\u578b\u5b50\u7c7b\u3002\uff08None \u4f1a\u88ab\u5ffd\u7565\uff09\n    \"\"\"\n    try:\n        return issubclass(cls, class_or_tuple)\n    except TypeError:\n        origin = get_origin(cls)\n        if is_union(origin):\n            return all(\n                is_none_type(type_) or generic_check_issubclass(type_, class_or_tuple)\n                for type_ in get_args(cls)\n            )\n        elif origin:\n            return issubclass(origin, class_or_tuple)\n        return False\n\n\ndef is_coroutine_callable(call: Callable[..., Any]) -> bool:\n    \"\"\"\u68c0\u67e5 call \u662f\u5426\u662f\u4e00\u4e2a callable \u534f\u7a0b\u51fd\u6570\"\"\"\n    if inspect.isroutine(call):\n        return inspect.iscoroutinefunction(call)\n    if inspect.isclass(call):\n        return False\n    func_ = getattr(call, \"__call__\", None)\n    return inspect.iscoroutinefunction(func_)\n\n\ndef is_gen_callable(call: Callable[..., Any]) -> bool:\n    \"\"\"\u68c0\u67e5 call \u662f\u5426\u662f\u4e00\u4e2a\u751f\u6210\u5668\u51fd\u6570\"\"\"\n    if inspect.isgeneratorfunction(call):\n        return True\n    func_ = getattr(call, \"__call__\", None)\n    return inspect.isgeneratorfunction(func_)\n\n\ndef is_async_gen_callable(call: Callable[..., Any]) -> bool:\n    \"\"\"\u68c0\u67e5 call \u662f\u5426\u662f\u4e00\u4e2a\u5f02\u6b65\u751f\u6210\u5668\u51fd\u6570\"\"\"\n    if inspect.isasyncgenfunction(call):\n        return True\n    func_ = getattr(call, \"__call__\", None)\n    return inspect.isasyncgenfunction(func_)\n\n\ndef run_sync(call: Callable[P, R]) -> Callable[P, Coroutine[None, None, R]]:\n    \"\"\"\u4e00\u4e2a\u7528\u4e8e\u5305\u88c5 sync function \u4e3a async function \u7684\u88c5\u9970\u5668\n\n    \u53c2\u6570:\n        call: \u88ab\u88c5\u9970\u7684\u540c\u6b65\u51fd\u6570\n    \"\"\"\n\n    @wraps(call)\n    async def _wrapper(*args: P.args, **kwargs: P.kwargs) -> R:\n        loop = asyncio.get_running_loop()\n        pfunc = partial(call, *args, **kwargs)\n        context = copy_context()\n        context_run = context.run\n        result = await loop.run_in_executor(None, context_run, pfunc)\n        return result\n\n    return _wrapper\n\n\n@asynccontextmanager\nasync def run_sync_ctx_manager(\n    cm: ContextManager[T],\n) -> AsyncGenerator[T, None]:\n    \"\"\"\u4e00\u4e2a\u7528\u4e8e\u5305\u88c5 sync context manager \u4e3a async context manager \u7684\u6267\u884c\u51fd\u6570\"\"\"\n    try:\n        yield await run_sync(cm.__enter__)()\n    except Exception as e:\n        ok = await run_sync(cm.__exit__)(type(e), e, None)\n        if not ok:\n            raise e\n    else:\n        await run_sync(cm.__exit__)(None, None, None)\n\n\n@overload\nasync def run_coro_with_catch(\n    coro: Coroutine[Any, Any, T],\n    exc: Tuple[Type[Exception], ...],\n) -> Union[T, None]:\n    ...\n\n\n@overload\nasync def run_coro_with_catch(\n    coro: Coroutine[Any, Any, T],\n    exc: Tuple[Type[Exception], ...],\n    return_on_err: R,\n) -> Union[T, R]:\n    ...\n\n\nasync def run_coro_with_catch(\n    coro: Coroutine[Any, Any, T],\n    exc: Tuple[Type[Exception], ...],\n    return_on_err: Optional[R] = None,\n) -> Optional[Union[T, R]]:\n    try:\n        return await coro\n    except exc:\n        return return_on_err\n\n\ndef get_name(obj: Any) -> str:\n    \"\"\"\u83b7\u53d6\u5bf9\u8c61\u7684\u540d\u79f0\"\"\"\n    if inspect.isfunction(obj) or inspect.isclass(obj):\n        return obj.__name__\n    return obj.__class__.__name__\n\n\ndef path_to_module_name(path: Path) -> str:\n    \"\"\"\u8f6c\u6362\u8def\u5f84\u4e3a\u6a21\u5757\u540d\"\"\"\n    rel_path = path.resolve().relative_to(Path.cwd().resolve())\n    if rel_path.stem == \"__init__\":\n        return \".\".join(rel_path.parts[:-1])\n    else:\n        return \".\".join(rel_path.parts[:-1] + (rel_path.stem,))\n\n\ndef resolve_dot_notation(\n    obj_str: str, default_attr: str, default_prefix: Optional[str] = None\n) -> Any:\n    \"\"\"\u89e3\u6790\u5e76\u5bfc\u5165\u70b9\u5206\u8868\u793a\u6cd5\u7684\u5bf9\u8c61\"\"\"\n    modulename, _, cls = obj_str.partition(\":\")\n    if default_prefix is not None and modulename.startswith(\"~\"):\n        modulename = default_prefix + modulename[1:]\n    module = importlib.import_module(modulename)\n    if not cls:\n        return getattr(module, default_attr)\n    instance = module\n    for attr_str in cls.split(\".\"):\n        instance = getattr(instance, attr_str)\n    return instance\n\n\nclass DataclassEncoder(json.JSONEncoder):\n    \"\"\"\u5728JSON\u5e8f\u5217\u5316 {re}`nonebot.adapters._message.Message` (List[Dataclass]) \u65f6\u4f7f\u7528\u7684 `JSONEncoder`\"\"\"\n\n    @overrides(json.JSONEncoder)\n    def default(self, o):\n        if dataclasses.is_dataclass(o):\n            return {f.name: getattr(o, f.name) for f in dataclasses.fields(o)}\n        return super().default(o)\n\n\ndef logger_wrapper(logger_name: str):\n    \"\"\"\u7528\u4e8e\u6253\u5370 adapter \u7684\u65e5\u5fd7\u3002\n\n    \u53c2\u6570:\n        logger_name: adapter \u7684\u540d\u79f0\n\n    \u8fd4\u56de:\n        \u65e5\u5fd7\u8bb0\u5f55\u51fd\u6570\n\n            - level: \u65e5\u5fd7\u7b49\u7ea7\n            - message: \u65e5\u5fd7\u4fe1\u606f\n            - exception: \u5f02\u5e38\u4fe1\u606f\n    \"\"\"\n\n    def log(level: str, message: str, exception: Optional[Exception] = None):\n        logger.opt(colors=True, exception=exception).log(\n            level, f\"<m>{escape_tag(logger_name)}</m> | {message}\"\n        )\n\n    return log\n", "path": "nonebot/utils.py"}]}