diff --git a/nonebot/utils.py b/nonebot/utils.py index b4eb9aba..984662aa 100644 --- a/nonebot/utils.py +++ b/nonebot/utils.py @@ -31,7 +31,7 @@ from typing import ( overload, ) -from pydantic.typing import is_union, is_none_type +from pydantic.typing import is_union, is_none_type, is_literal_type, all_literal_values from nonebot.log import logger @@ -75,9 +75,18 @@ def generic_check_issubclass( is_none_type(type_) or generic_check_issubclass(type_, class_or_tuple) for type_ in get_args(cls) ) + elif is_literal_type(cls): + return all( + is_none_type(value) or isinstance(value, class_or_tuple) + for value in all_literal_values(cls) + ) # ensure generic List, Dict can be checked elif origin: - return issubclass(origin, class_or_tuple) + # avoid class check error (typing.Final, typing.ClassVar, etc...) + try: + return issubclass(origin, class_or_tuple) + except TypeError: + return False elif isinstance(cls, TypeVar): if cls.__constraints__: return all( diff --git a/tests/test_utils.py b/tests/test_utils.py index fbcdeaff..7db3f7b5 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,5 +1,5 @@ import json -from typing import Dict, List, Union, TypeVar +from typing import Dict, List, Union, Literal, TypeVar, ClassVar from utils import FakeMessage, FakeMessageSegment from nonebot.utils import ( @@ -24,8 +24,11 @@ def test_generic_check_issubclass(): assert generic_check_issubclass(int, (int, float)) assert not generic_check_issubclass(str, (int, float)) assert generic_check_issubclass(Union[int, float, None], (int, float)) + assert generic_check_issubclass(Literal[1, 2, 3], int) + assert not generic_check_issubclass(Literal[1, 2, "3"], int) assert generic_check_issubclass(List[int], list) assert generic_check_issubclass(Dict[str, int], dict) + assert not generic_check_issubclass(ClassVar[int], int) assert generic_check_issubclass(TypeVar("T", int, float), (int, float)) assert generic_check_issubclass(TypeVar("T", bound=int), (int, float))