Repository: nonebot/nonebot2

Issue:
Bug: Union Checking Failed
**描述问题：**

这个插件中有一个语音相关功能，beta3版本会报错，但是beta2版本不会。不清楚是插件没有适配还是beta3的bug，来问一下。其他命令没有问题

**如何复现？**

输入littlepaimon插件对应的语音，beta2版本能够成功发生语音，beta3会报一大堆错，日志在下面
**期望的结果**

正常发送语音

**环境信息：**

 - OS: [windows]
 - Python Version: [3.9.1]
 - Nonebot Version: [nonebot2的beta3]

**协议端信息：**

 - 协议端: [go-cqhttp]
 - 协议端版本: [1.0.0-rc1]

**截图或日志**

```python
05-21 20:51:30 [INFO] nonebot | Event will be handled by <Matcher from LittlePaimon.Guess_voice, type=message, priority=12, temp=False>
05-21 20:51:30 [INFO] nonebot | Matcher <Matcher from LittlePaimon.Guess_voice, type=message, priority=12, temp=False> running complete
05-21 20:51:30 [ERROR] nonebot | Running matcher <Matcher from LittlePaimon.Guess_voice, type=message, priority=12, temp=False> failed.
Traceback (most recent call last):
  File "c:\users\administrator\appdata\local\programs\python\python39\lib\runpy.py", line 197, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "c:\users\administrator\appdata\local\programs\python\python39\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "C:\Users\Administrator\Desktop\nonebot\venv\Scripts\nb.exe\__main__.py", line 7, in <module>
    sys.exit(main())
  File "C:\Users\Administrator\Desktop\nonebot\venv\lib\site-packages\click\core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
  File "C:\Users\Administrator\Desktop\nonebot\venv\lib\site-packages\click\core.py", line 1055, in main
    rv = self.invoke(ctx)
  File "C:\Users\Administrator\Desktop\nonebot\venv\lib\site-packages\click\core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "C:\Users\Administrator\Desktop\nonebot\venv\lib\site-packages\click\core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "C:\Users\Administrator\Desktop\nonebot\venv\lib\site-packages\click\core.py", line 760, in invoke
    return __callback(*args, **kwargs)
  File "C:\Users\Administrator\Desktop\nonebot\venv\lib\site-packages\nb_cli\commands\main.py", line 30, in run
    run_bot(file, app)
  File "C:\Users\Administrator\Desktop\nonebot\venv\lib\site-packages\nb_cli\handlers\deploy.py", line 24, in run_bot
    nonebot.run(app=f"{module_name}:{app}")
  File "C:\Users\Administrator\Desktop\nonebot\venv\lib\site-packages\nonebot\__init__.py", line 259, in run
    get_driver().run(*args, **kwargs)
  File "C:\Users\Administrator\Desktop\nonebot\venv\lib\site-packages\nonebot\drivers\fastapi.py", line 170, in run
    uvicorn.run(
  File "C:\Users\Administrator\Desktop\nonebot\venv\lib\site-packages\uvicorn\main.py", line 463, in run
    server.run()
  File "C:\Users\Administrator\Desktop\nonebot\venv\lib\site-packages\uvicorn\server.py", line 60, in run
    return asyncio.run(self.serve(sockets=sockets))
  File "c:\users\administrator\appdata\local\programs\python\python39\lib\asyncio\runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "c:\users\administrator\appdata\local\programs\python\python39\lib\asyncio\base_events.py", line 629, in run_until_complete
    self.run_forever()
  File "c:\users\administrator\appdata\local\programs\python\python39\lib\asyncio\windows_events.py", line 316, in run_forever
    super().run_forever()
  File "c:\users\administrator\appdata\local\programs\python\python39\lib\asyncio\base_events.py", line 596, in run_forever
    self._run_once()
  File "c:\users\administrator\appdata\local\programs\python\python39\lib\asyncio\base_events.py", line 1890, in _run_once
    handle._run()
  File "c:\users\administrator\appdata\local\programs\python\python39\lib\asyncio\events.py", line 80, in _run
    self._context.run(self._callback, *self._args)
  File "C:\Users\Administrator\Desktop\nonebot\venv\lib\site-packages\nonebot\message.py", line 146, in _check_matcher
    await _run_matcher(Matcher, bot, event, state, stack, dependency_cache)
> File "C:\Users\Administrator\Desktop\nonebot\venv\lib\site-packages\nonebot\message.py", line 196, in _run_matcher
    await matcher.run(bot, event, state, stack, dependency_cache)
  File "C:\Users\Administrator\Desktop\nonebot\venv\lib\site-packages\nonebot\internal\matcher.py", line 668, in run
    await self.simple_run(bot, event, state, stack, dependency_cache)
  File "C:\Users\Administrator\Desktop\nonebot\venv\lib\site-packages\nonebot\internal\matcher.py", line 635, in simple_run
    await handler(
  File "C:\Users\Administrator\Desktop\nonebot\venv\lib\site-packages\nonebot\dependencies\__init__.py", line 89, in __call__
    values = await self.solve(**kwargs)
  File "C:\Users\Administrator\Desktop\nonebot\venv\lib\site-packages\nonebot\dependencies\__init__.py", line 188, in solve
    await checker._solve(**params)
  File "C:\Users\Administrator\Desktop\nonebot\venv\lib\site-packages\nonebot\internal\params.py", line 208, in _solve
    if isinstance(event, field.type_):
  File "c:\users\administrator\appdata\local\programs\python\python39\lib\typing.py", line 697, in __instancecheck__
    return self.__subclasscheck__(type(obj))
  File "c:\users\administrator\appdata\local\programs\python\python39\lib\typing.py", line 700, in __subclasscheck__
    raise TypeError("Subscripted generics cannot be used with"
TypeError: Subscripted generics cannot be used with class and instance checks
```


Discussion:
Union 出现了问题

Related https://github.com/KimigaiiWuyi/GenshinUID/issues/236
> Union 出现了问题
> 
> Related [KimigaiiWuyi/GenshinUID#236](https://github.com/KimigaiiWuyi/GenshinUID/issues/236)

那要怎么解决呢
暂时可以通过使用python 3.10解决

Resolving patch:
diff --git a/nonebot/internal/adapter/event.py b/nonebot/internal/adapter/event.py
--- a/nonebot/internal/adapter/event.py
+++ b/nonebot/internal/adapter/event.py
@@ -1,4 +1,5 @@
 import abc
+from typing import Any, Type, TypeVar
 
 from pydantic import BaseModel
 
@@ -6,6 +7,8 @@
 
 from .message import Message
 
+E = TypeVar("E", bound="Event")
+
 
 class Event(abc.ABC, BaseModel):
     """Event 基类。提供获取关键信息的方法，其余信息可直接获取。"""
@@ -14,6 +17,12 @@ class Config:
         extra = "allow"
         json_encoders = {Message: DataclassEncoder}
 
+    @classmethod
+    def validate(cls: Type["E"], value: Any) -> "E":
+        if isinstance(value, Event) and not isinstance(value, cls):
+            raise TypeError(f"{value} is incompatible with Event type {cls}")
+        return super().validate(value)
+
     @abc.abstractmethod
     def get_type(self) -> str:
         """获取事件类型的方法，类型通常为 NoneBot 内置的四种类型。"""
diff --git a/nonebot/internal/params.py b/nonebot/internal/params.py
--- a/nonebot/internal/params.py
+++ b/nonebot/internal/params.py
@@ -9,6 +9,7 @@
 
 from nonebot.log import logger
 from nonebot.exception import TypeMisMatch
+from nonebot.dependencies.utils import check_field_type
 from nonebot.dependencies import Param, Dependent, CustomConfig
 from nonebot.typing import T_State, T_Handler, T_DependencyCache
 from nonebot.utils import (
@@ -159,14 +160,14 @@ async def _solve(
 class _BotChecker(Param):
     async def _solve(self, bot: "Bot", **kwargs: Any) -> Any:
         field: ModelField = self.extra["field"]
-        if isinstance(bot, field.type_):
-            return bot
-        else:
+        try:
+            return check_field_type(field, bot)
+        except TypeMisMatch:
             logger.debug(
                 f"Bot type {type(bot)} not match "
                 f"annotation {field._type_display()}, ignored"
             )
-            raise TypeMisMatch(field, bot)
+            raise
 
 
 class BotParam(Param):
@@ -205,14 +206,14 @@ async def _solve(self, bot: "Bot", **kwargs: Any) -> Any:
 class _EventChecker(Param):
     async def _solve(self, event: "Event", **kwargs: Any) -> Any:
         field: ModelField = self.extra["field"]
-        if isinstance(event, field.type_):
-            return event
-        else:
+        try:
+            return check_field_type(field, event)
+        except TypeMisMatch:
             logger.debug(
                 f"Event type {type(event)} not match "
                 f"annotation {field._type_display()}, ignored"
             )
-            raise TypeMisMatch(field, event)
+            raise
 
 
 class EventParam(Param):
diff --git a/nonebot/utils.py b/nonebot/utils.py
--- a/nonebot/utils.py
+++ b/nonebot/utils.py
@@ -62,12 +62,10 @@ def generic_check_issubclass(
     except TypeError:
         origin = get_origin(cls)
         if is_union(origin):
-            for type_ in get_args(cls):
-                if not is_none_type(type_) and not generic_check_issubclass(
-                    type_, class_or_tuple
-                ):
-                    return False
-            return True
+            return all(
+                is_none_type(type_) or generic_check_issubclass(type_, class_or_tuple)
+                for type_ in get_args(cls)
+            )
         elif origin:
             return issubclass(origin, class_or_tuple)
         return False


Test patch:
diff --git a/tests/plugins/param/param_bot.py b/tests/plugins/param/param_bot.py
--- a/tests/plugins/param/param_bot.py
+++ b/tests/plugins/param/param_bot.py
@@ -1,3 +1,5 @@
+from typing import Union
+
 from nonebot.adapters import Bot
 
 
@@ -5,9 +7,29 @@ async def get_bot(b: Bot) -> Bot:
     return b
 
 
-class SubBot(Bot):
+async def legacy_bot(bot):
+    return bot
+
+
+async def not_legacy_bot(bot: int):
+    ...
+
+
+class FooBot(Bot):
     ...
 
 
-async def sub_bot(b: SubBot) -> SubBot:
+async def sub_bot(b: FooBot) -> FooBot:
     return b
+
+
+class BarBot(Bot):
+    ...
+
+
+async def union_bot(b: Union[FooBot, BarBot]) -> Union[FooBot, BarBot]:
+    return b
+
+
+async def not_bot(b: Union[int, Bot]):
+    ...
diff --git a/tests/plugins/param/param_event.py b/tests/plugins/param/param_event.py
--- a/tests/plugins/param/param_event.py
+++ b/tests/plugins/param/param_event.py
@@ -1,3 +1,5 @@
+from typing import Union
+
 from nonebot.adapters import Event, Message
 from nonebot.params import EventToMe, EventType, EventMessage, EventPlainText
 
@@ -6,14 +8,34 @@ async def event(e: Event) -> Event:
     return e
 
 
-class SubEvent(Event):
+async def legacy_event(event):
+    return event
+
+
+async def not_legacy_event(event: int):
+    ...
+
+
+class FooEvent(Event):
     ...
 
 
-async def sub_event(e: SubEvent) -> SubEvent:
+async def sub_event(e: FooEvent) -> FooEvent:
     return e
 
 
+class BarEvent(Event):
+    ...
+
+
+async def union_event(e: Union[FooEvent, BarEvent]) -> Union[FooEvent, BarEvent]:
+    return e
+
+
+async def not_event(e: Union[int, Event]):
+    ...
+
+
 async def event_type(t: str = EventType()) -> str:
     return t
 
diff --git a/tests/plugins/param/param_state.py b/tests/plugins/param/param_state.py
--- a/tests/plugins/param/param_state.py
+++ b/tests/plugins/param/param_state.py
@@ -19,6 +19,14 @@ async def state(x: T_State) -> T_State:
     return x
 
 
+async def legacy_state(state):
+    return state
+
+
+async def not_legacy_state(state: int):
+    ...
+
+
 async def command(cmd: Tuple[str, ...] = Command()) -> Tuple[str, ...]:
     return cmd
 
diff --git a/tests/test_param.py b/tests/test_param.py
--- a/tests/test_param.py
+++ b/tests/test_param.py
@@ -37,15 +37,32 @@ async def test_depend(app: App, load_plugin):
 async def test_bot(app: App, load_plugin):
     from nonebot.params import BotParam
     from nonebot.exception import TypeMisMatch
-    from plugins.param.param_bot import SubBot, get_bot, sub_bot
+    from plugins.param.param_bot import (
+        FooBot,
+        get_bot,
+        not_bot,
+        sub_bot,
+        union_bot,
+        legacy_bot,
+        not_legacy_bot,
+    )
 
     async with app.test_dependent(get_bot, allow_types=[BotParam]) as ctx:
         bot = ctx.create_bot()
         ctx.pass_params(bot=bot)
         ctx.should_return(bot)
 
+    async with app.test_dependent(legacy_bot, allow_types=[BotParam]) as ctx:
+        bot = ctx.create_bot()
+        ctx.pass_params(bot=bot)
+        ctx.should_return(bot)
+
+    with pytest.raises(ValueError):
+        async with app.test_dependent(not_legacy_bot, allow_types=[BotParam]) as ctx:
+            ...
+
     async with app.test_dependent(sub_bot, allow_types=[BotParam]) as ctx:
-        bot = ctx.create_bot(base=SubBot)
+        bot = ctx.create_bot(base=FooBot)
         ctx.pass_params(bot=bot)
         ctx.should_return(bot)
 
@@ -54,37 +71,68 @@ async def test_bot(app: App, load_plugin):
             bot = ctx.create_bot()
             ctx.pass_params(bot=bot)
 
+    async with app.test_dependent(union_bot, allow_types=[BotParam]) as ctx:
+        bot = ctx.create_bot(base=FooBot)
+        ctx.pass_params(bot=bot)
+        ctx.should_return(bot)
+
+    with pytest.raises(ValueError):
+        async with app.test_dependent(not_bot, allow_types=[BotParam]) as ctx:
+            ...
+
 
 @pytest.mark.asyncio
 async def test_event(app: App, load_plugin):
     from nonebot.exception import TypeMisMatch
     from nonebot.params import EventParam, DependParam
     from plugins.param.param_event import (
-        SubEvent,
+        FooEvent,
         event,
+        not_event,
         sub_event,
         event_type,
         event_to_me,
+        union_event,
+        legacy_event,
         event_message,
         event_plain_text,
+        not_legacy_event,
     )
 
     fake_message = make_fake_message()("text")
     fake_event = make_fake_event(_message=fake_message)()
-    fake_subevent = make_fake_event(_base=SubEvent)()
+    fake_fooevent = make_fake_event(_base=FooEvent)()
 
     async with app.test_dependent(event, allow_types=[EventParam]) as ctx:
         ctx.pass_params(event=fake_event)
         ctx.should_return(fake_event)
 
+    async with app.test_dependent(legacy_event, allow_types=[EventParam]) as ctx:
+        ctx.pass_params(event=fake_event)
+        ctx.should_return(fake_event)
+
+    with pytest.raises(ValueError):
+        async with app.test_dependent(
+            not_legacy_event, allow_types=[EventParam]
+        ) as ctx:
+            ...
+
     async with app.test_dependent(sub_event, allow_types=[EventParam]) as ctx:
-        ctx.pass_params(event=fake_subevent)
-        ctx.should_return(fake_subevent)
+        ctx.pass_params(event=fake_fooevent)
+        ctx.should_return(fake_fooevent)
 
     with pytest.raises(TypeMisMatch):
         async with app.test_dependent(sub_event, allow_types=[EventParam]) as ctx:
             ctx.pass_params(event=fake_event)
 
+    async with app.test_dependent(union_event, allow_types=[EventParam]) as ctx:
+        ctx.pass_params(event=fake_fooevent)
+        ctx.should_return(fake_event)
+
+    with pytest.raises(ValueError):
+        async with app.test_dependent(not_event, allow_types=[EventParam]) as ctx:
+            ...
+
     async with app.test_dependent(
         event_type, allow_types=[EventParam, DependParam]
     ) as ctx:
@@ -132,8 +180,10 @@ async def test_state(app: App, load_plugin):
         command_arg,
         raw_command,
         regex_group,
+        legacy_state,
         command_start,
         regex_matched,
+        not_legacy_state,
         shell_command_args,
         shell_command_argv,
     )
@@ -157,6 +207,16 @@ async def test_state(app: App, load_plugin):
         ctx.pass_params(state=fake_state)
         ctx.should_return(fake_state)
 
+    async with app.test_dependent(legacy_state, allow_types=[StateParam]) as ctx:
+        ctx.pass_params(state=fake_state)
+        ctx.should_return(fake_state)
+
+    with pytest.raises(ValueError):
+        async with app.test_dependent(
+            not_legacy_state, allow_types=[StateParam]
+        ) as ctx:
+            ...
+
     async with app.test_dependent(
         command, allow_types=[StateParam, DependParam]
     ) as ctx: