第一节 Dependent与Param的实例化

realhuhu 295 0

NoneBot的核心是Bot,Bot的核心是Matcher,Matcher的核心是Dependent,Dependent的核心是Param

本文简单介绍依赖注入概念,实现了一个丐版依赖注入,分析了Dependent如何解构handler,handler的参数如何被转化为Param子类

handler:on()所装饰的函数

什么是依赖注入

简单理解:程序运行的时候会存储一些变量,当函数需要这些变量时,自动把变量传给函数
以下面的自动同意主人邀请代码为例

from nonebot import on_request
from nonebot.adapters.onebot.v11.bot import Bot
from nonebot.adapters.onebot.v11.event import GroupRequestEvent

from .config import auto_request_config

auto_group_request = on_request()

@auto_group_request.handle()
async def auto(bot: Bot, event: GroupRequestEvent):
    if event.sub_type == "invite" and event.user_id == 10000:
        await event.approve(bot)

在运行到auto_group_request时,程序发现它需要nonebot.adapters.onebot.v11.bot.Bot和nonebot.adapters.onebot.v11.event.GroupRequestEvent类型的变量,于是程序在当前环境中寻找是否有这些变量,并将变量交给handler(函数auto),触发handler逻辑

如何实现依赖注入

要实现依赖注入,需要有以下步骤

  1. 有一个一直在维护的环境,用于存储变量(就叫它上下文吧)
  2. 函数需要有注解(annotation),这样才能根据注解从上下文中找出函数需要的参数
  3. 通过某种方法获取函数的参数名、注解

首先看如何获取函数名和注解

def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
    signature = inspect.signature(call) # 可以获取参数名和类型
    globalns = getattr(call, "__globals__", {})
    typed_params = [
        inspect.Parameter(
            name=param.name,
            kind=param.kind,
            default=param.default,
            annotation=get_typed_annotation(param, globalns), # 特殊处理str形式的annotation,如def test(a: "int", b: str)
        )
        for param in signature.parameters.values()
    ]
    return inspect.Signature(typed_params)

def get_typed_annotation(param: inspect.Parameter, globalns: Dict[str, Any]) -> Any: # 借助globalns将str形式的annotation还原
    annotation = param.annotation
    if isinstance(annotation, str):
        annotation = ForwardRef(annotation)
        annotation = evaluate_forwardref(annotation, globalns, globalns)
    return annotation

这里直接照抄NoneBot代码(位于nonebot.nonebot.dependencies.utils),get_typed_signature的作用就是获取函数的参数名和注解

第一节 Dependent与Param的实例化

这样我们可以定义Dependent,将函数拆解

class Dependent:
    def __init__(self, call: Callable):
        self.call = call
        self.params = self.parse_params(call)
        print(self.params)

    @staticmethod
    def parse_params(call: Callable):
        fields = []
        params = get_typed_signature(call).parameters.values()
        for param in params:
            annotation = param.annotation
            fields.append({
                "name": param.name,
                "type": annotation
            })
        return fields

当我们将Dependent作为装饰器,就可以获取被装饰函数的参数信息

第一节 Dependent与Param的实例化

那么该怎么调用函数呢,我们可以定义一个类变量context,用于存储上下文;同时定义__call__,在调用时从上下文获取变量并传给函数

class Dependent:
    context = {}

    def __init__(self, call: Callable):
        self.call = call
        self.params = self.parse_params(call)

    @staticmethod
    def parse_params(call: Callable):
        fields = []
        params = get_typed_signature(call).parameters.values()
        for param in params:
            annotation = param.annotation
            fields.append({
                "name": param.name,
                "type": annotation
            })
        return fields

    def __call__(self):
        self.call(**{
            param["name"]: self.context[param["type"]] for param in self.params
        })

这样就实现了一个简单的依赖注入,当context不同时,调用test的结果也不同

第一节 Dependent与Param的实例化

完整代码如下

点击查看完整内容

当然,这个代码有很多问题,比如不支持异步函数、context里可能没有对应类型的值,不支持Dependent的嵌套等

NoneBot的依赖注入

首先我们要知道,在下面这个代码中,函数会被包装为Dependent,放到Matcher.handlers列表中(具体流程会在Matcher章介绍,这里只介绍auto是如何从函数变成Denpendent)

from nonebot import on_request
from nonebot.adapters.onebot.v11.bot import Bot
from nonebot.adapters.onebot.v11.event import GroupRequestEvent

from .config import auto_request_config

auto_group_request = on_request()

@auto_group_request.handle()
async def auto(bot: Bot, event: GroupRequestEvent):
    if event.sub_type == "invite" and event.user_id == 10000:
        await event.approve(bot)

第一节 Dependent与Param的实例化

Dependent的实例化之parse_params

我们看看Dependent.parse(暂时忽视中间的[Any])在干什么(代码位于nonebot.dependencies.__init__)

@dataclass(frozen=True)
class Dependent(Generic[R]):
    ...
    @classmethod
    def parse(
            cls,
            *,
            call: _DependentCallable[R],
            parameterless: Optional[Iterable[Any]] = None,
            allow_types: Iterable[Type[Param]],
    ) -> "Dependent[R]":
        allow_types = tuple(allow_types)

        params = cls.parse_params(call, allow_types)
        parameterless_params = (
            tuple()
            if parameterless is None
            else cls.parse_parameterless(tuple(parameterless), allow_types)
        )

        return cls(call, params, parameterless_params)

它接收三个参数

  • call就是handler(如上文的auto),类型为_DependentCallable(还是先忽略[R],本文后会提及),等于Union[Callable[..., T], Callable[..., Awaitable[T]]],也就是说同步函数异步函数都支持
  • parameterless(暂时忽略,本文后会提及)
  • allow_types,类型为Iterable[Type[Param]],是为了实现嵌套依赖注入。当参数类型是Param的子类时,代码不会和之前一样傻傻的去context里找这个类型的变量,而是执行这个Param;对于Param的参数,会根据注解类型去context里找这个类型的变量,如果注解类型是Param的子类,那就执行这个Param...这就实现了嵌套dependent

例如下面这个代码

第一节 Dependent与Param的实例化

Depends(get_msg)是nonebot.internal.params.DependsInner类型,最终会变成nonebot.internal.params.DependParam类型(后文会介绍是怎么变的),而DependParam是nonebot.dependencies.Param子类,所以get_msg函数会被执行,由于它是Param的子类,有类似dependent的性质,能够从context中找到类型为PrivateMessageEvent的变量赋值给event,最终将返回值event.get_plaintext()赋值给msg
现在你应该对NoneBot插件里那些语法有些模糊理解了,至于Param是怎么实现的,我们很快就会介绍到

实际上,matcher中任何的参数最终都是Param,例如async def _(bot: Bot, event: PrivateMessageEvent, msg=Depends(get_msg))中,bot、event、msg最终都是Param的子类,只有可转化为Param的类型才可以作为handler的类型注解

接下来看parse内部代码,由于allow_types和parameterless_params暂时忽略,我们只看params = cls.parse_params(call, allow_types)
cls.parse_params和前面那个丐版依赖注入的parse_params功能相似

@dataclass(frozen=True)
class Dependent(Generic[R]):
    @staticmethod
    def parse_params(
        call: _DependentCallable[R], allow_types: Tuple[Type[Param], ...]
    ) -> Tuple[ModelField]:
        fields: List[ModelField] = []
        params = get_typed_signature(call).parameters.values()
        for param in params:
            default_value = Required
            if param.default != param.empty:
                default_value = param.default

            if isinstance(default_value, Param):
                field_info = default_value
            else:
                for allow_type in allow_types:
                    if field_info := allow_type._check_param(param, allow_types):
                        break
                else:
                    raise ValueError(
                        f"Unknown parameter {param.name} for function {call} with type {param.annotation}"
                    )
            default_value = field_info.default

            annotation: Any = Any
            required = default_value == Required
            if param.annotation != param.empty:
                annotation = param.annotation
            annotation = get_annotation_from_field_info(
                annotation, field_info, param.name
            )

            fields.append(
                ModelField(
                    name=param.name,
                    type_=annotation,
                    class_validators=None,
                    model_config=CustomConfig,
                    default=None if required else default_value,
                    required=required,
                    field_info=field_info,
                )
            )
        return tuple(fields)

这段代码信息量很大,我们来一段段分析

from nonebot import on_message
from nonebot.adapters.onebot.v11.bot import Bot
from nonebot.params import Depends, DefaultParam
from nonebot.adapters.onebot.v11.event import PrivateMessageEvent
from .config import auto_request_config

test = on_message()

def get_msg(event: PrivateMessageEvent):
    return event.get_plaintext()

@test.handle()
async def _(
        bot: Bot,
        event: PrivateMessageEvent,
        msg: str = Depends(get_msg),
        flag: bool = DefaultParam(True) # NoneBot中默认值需要这样写
):
    pass

假设handler如上图所示,则

def parse_params(
    call: _DependentCallable[R], allow_types: Tuple[Type[Param], ...]
) -> Tuple[ModelField]:
    fields: List[ModelField] = [] # 用于存储handler的参数的类型注解和默认值,相当于丐版依赖注入的fields类似
    params = get_typed_signature(call).parameters.values() # 获取handler的参数的类型注解和默认值
    ```
    params的结果
    odict_values([
    <Parameter "bot: Bot">,
    <Parameter "event: PrivateMessageEvent">,
    <Parameter "msg: str = Depends">,
    <Parameter "flag: str = DefaultParam">
    ])
    ```
        for param in params: # 遍历已经拆分出的参数列表,结果是
            ```
            如果没有默认值,如bot,event,就将默认值default_value设为Required
            msg有默认值,default_value就是Depends;flag有默认值,default_value就是DefaultParam
            ```
            default_value = Required
            if param.default != param.empty:
                default_value = param.default

            if isinstance(default_value, Param):
                ```
                如果default_value就是Param子类,将Param作为field_info
                这里只有DefaultParam是Param子类,所以参数flag的field_info为DefaultParam
                ```
                field_info = default_value
            else:
                ```
                对于bot、event、msg,会将其转换为Param子类
                这也是为什么说matcher中任何的参数最终都是Param
                ```
                for allow_type in allow_types:
                    if field_info := allow_type._check_param(param, allow_types):
                        break
                else:
                    raise ValueError(
                        f"Unknown parameter {param.name} for function {call} with type {param.annotation}"
                    )

bot、event、msg的field_info为什么是Param的子类?首先要知道allow_types是什么
不管你是on_message,on_regex还是on什么,handler最终都是在nonebot.internal.matcher.Matcher.new被转化为dependent,传入的allow_types为cls.HANDLER_PARAM_TYPES

第一节 Dependent与Param的实例化

也就是

第一节 Dependent与Param的实例化

这些Param都位于nonebot.internal.params

class DependParam(Param):
	...
	@classmethod
	def _check_param(
		cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
	) -> Optional["DependParam"]:
		if isinstance(param.default, DependsInner):
			dependency: T_Handler
			if param.default.dependency is None:
				assert param.annotation is not param.empty, "Dependency cannot be empty"
				dependency = param.annotation
			else:
				dependency = param.default.dependency
			sub_dependent = Dependent[Any].parse(
				call=dependency,
				allow_types=allow_types,
			)
			return cls(
				Required, use_cache=param.default.use_cache, dependent=sub_dependent
			)
	...
class BotParam(Param):
	...
	@classmethod
	def _check_param(
		cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
	) -> Optional["BotParam"]:
		from nonebot.adapters import Bot

		if param.default == param.empty:
			if generic_check_issubclass(param.annotation, Bot):
				checker: Optional[ModelField] = None
				if param.annotation is not Bot:
					checker = ModelField(
						name=param.name,
						type_=param.annotation,
						class_validators=None,
						model_config=CustomConfig,
						default=None,
						required=True,
					)
				return cls(Required, checker=checker)
			elif param.annotation == param.empty and param.name == "bot":
				return cls(Required)
	...
class EventParam(Param):
	...
	@classmethod
	def _check_param(
		cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
	) -> Optional["EventParam"]:
		from nonebot.adapters import Event

		if param.default == param.empty:
			if generic_check_issubclass(param.annotation, Event):
				checker: Optional[ModelField] = None
				if param.annotation is not Event:
					checker = ModelField(
						name=param.name,
						type_=param.annotation,
						class_validators=None,
						model_config=CustomConfig,
						default=None,
						required=True,
					)
				return cls(Required, checker=checker)
			elif param.annotation == param.empty and param.name == "event":
				return cls(Required)
	...
class StateParam(Param):
	...
	@classmethod
	def _check_param(
		cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
	) -> Optional["StateParam"]:
		if param.default == param.empty:
			if param.annotation is T_State:
				return cls(Required)
			elif param.annotation == param.empty and param.name == "state":
				return cls(Required)
	...
class ArgParam(Param):
	...
	@classmethod
	def _check_param(
		cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
	) -> Optional["ArgParam"]:
		if isinstance(param.default, ArgInner):
			return cls(
				Required, key=param.default.key or param.name, type=param.default.type
			)
	...
class MatcherParam(Param):
	...
	@classmethod
	def _check_param(
		cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
	) -> Optional["MatcherParam"]:
		from nonebot.matcher import Matcher
		if generic_check_issubclass(param.annotation, Matcher) or (
			param.annotation == param.empty and param.name == "matcher"
		):
			return cls(Required)
	...
class DefaultParam(Param):
	...
	@classmethod
	def _check_param(
		cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
	) -> Optional["DefaultParam"]:
		if param.default != param.empty:
			return cls(param.default)
	...

容易看出,msg、bot、event会依次被DependParam._check_param、BotParam._check_param、EventParam._check_param转化为DependParam、BotParam、EventParam
所以说“handler中任何的参数最终都是Param”,指的是handler中任何的field_info最终都是Param
接着分析parse_params

def parse_params(
    call: _DependentCallable[R], allow_types: Tuple[Type[Param], ...]
) -> Tuple[ModelField]:
    fields: List[ModelField] = []
    for param in params:
        ```
        经过处理,field_info全部变成了Param子类
        bot的field_info: BotParam
        event的field_info: EventParam
        msg的field_info: DependParam
        flag的field_info: DefaultParam
        ```
        ...
        default_value = field_info.default # 除了DefaultParam可以设置field_info.default,如本例中为True,其它Param子类的field_info.default都是Required

        annotation: Any = Any
        required = default_value == Required # 除了DefaultParam外其它Param子类的required都是True
        if param.annotation != param.empty:
            # param的注解不为空的话,annotation就等于param.annotation
            annotation = param.annotation
            ```
            bot的annotation: Bot
            event的annotation: Event
            msg的annotation: Depends
            flag的annotation: DefaultParam
            ```
        annotation = get_annotation_from_field_info(
            annotation, field_info, param.name
        ) # 貌似是无效代码,annotation值不改变

        fields.append(
            ModelField(
                name=param.name, # 参数名
                type_=annotation, # 参数类型注解
                class_validators=None, # 校验钩子,这里只是用ModelField简单存储信息,不需要
                model_config=CustomConfig, # 见nonebot.dependencies.\__init__.CustomConfig,允许任意类型,不重要
                default=None if required else default_value, # 类型注解为DefaultParam的参数才有,如这个例子中flag的default是True (flag:bool=DefaultParam(True)),其它参数则均为None
                required=required, # 只有类型注解为DefaultParam的参数才为False,其它参数的required都是True
                field_info=field_info, # 参数转化后的Param子类
            )
        ) # 将函数参数信息存到fields列表

    return tuple(fields) # 转为元组

至此我们就弄清了handler的参数是如何解构的

Dependent的实例化之parse_parameterless

接下来分析之前忽略的parameterless参数

@dataclass(frozen=True)
class Dependent(Generic[R]):
    @classmethod
    def parse(
        cls,
        *,
        call: _DependentCallable[R],
        parameterless: Optional[Iterable[Any]] = None,
        allow_types: Iterable[Type[Param]],
    ) -> "Dependent[R]":
        allow_types = tuple(allow_types)

        params = cls.parse_params(call, allow_types) # 已经分析完了
        parameterless_params = (
            tuple()
            if parameterless is None
            else cls.parse_parameterless(tuple(parameterless), allow_types)
        ) # 接下来分析

        return cls(call, params, parameterless_params)

易知parameterless_params默认是空元组,如果parameterless不为None,进入cls.parse_parameterless

@dataclass(frozen=True)
class Dependent(Generic[R]):
    ...
    @staticmethod
    def parse_parameterless(
        parameterless: Tuple[Any, ...], allow_types: Tuple[Type[Param], ...]
    ) -> Tuple[Param, ...]:
        parameterless_params: List[Param] = []
        for value in parameterless:
            for allow_type in allow_types:
                if param := allow_type._check_parameterless(value, allow_types):
                    break
            else:
                raise ValueError(f"Unknown parameterless {value}")
            parameterless_params.append(param)
        return tuple(parameterless_params)

和parse_params中的Param化如出一辙,只不过用的是_check_parameterless
返回值是parameterless参数Param化后的元组
目前只有DependParam支持parameterless

def parse_int(key: str):
    async def _key_parser(
        matcher: Matcher, state: T_State, input: int | Message = Arg(key)
    ):
        if isinstance(input, int):
            return

        plaintext = input.extract_plain_text()
        if not plaintext.isdigit():
            await matcher.reject_arg(key, "请只输入数字,不然我没法理解呢!")
        state[key] = int(plaintext)

    return  _key_parser

@history_cmd.got(
    "month",
    prompt="你请输入你要查询的月份",
    parameterless=[Depends(parse_int("month"))], # 最终parameterless_params=(DependParm,)
)
async def history_handle_group_message(
    bot: Bot,
    event: GroupMessageEvent,
    year: int = Arg(),
    month: int = Arg(),
    day: int = Arg(),
):
    pass

Dependent的实例化

@dataclass(frozen=True)
class Dependent(Generic[R]):
    call: _DependentCallable[R]
    params: Tuple[ModelField] = field(default_factory=tuple)
    parameterless: Tuple[Param] = field(default_factory=tuple)

    ...

    @classmethod
    def parse(
        cls,
        *,
        call: _DependentCallable[R],
        parameterless: Optional[Iterable[Any]] = None,
        allow_types: Iterable[Type[Param]],
    ) -> "Dependent[R]":
        allow_types = tuple(allow_types) # 已经分析完了

        params = cls.parse_params(call, allow_types) # 已经分析完了
        parameterless_params = (
            tuple()
            if parameterless is None
            else cls.parse_parameterless(tuple(parameterless), allow_types)
        ) # 已经分析完了

        return cls(call, params, parameterless_params) #接下来分析

看起来Dependent没有__init__,为什么能用cls()来实例化?
因为Dependent被dataclass装饰了,cls(call, params, parameterless_params)会将call、params、parameterless_params赋值给cls.call、cls.params、cls.parameterless_params
至此Dependent实例化就分析完了

Param子类的实例化

前面简单介绍了Param,这节将详细介绍Param子类实例化

DependParam

class DependsInner:
    def __init__(
        self,
        dependency: Optional[T_Handler] = None,
        *,
        use_cache: bool = True,
    ) -> None:
        self.dependency = dependency
        self.use_cache = use_cache

    def __repr__(self) -> str:
        dep = get_name(self.dependency)
        cache = "" if self.use_cache else ", use_cache=False"
        return f"DependsInner({dep}{cache})"

def Depends(
    dependency: Optional[T_Handler] = None,
    *,
    use_cache: bool = True,
) -> Any:
    return DependsInner(dependency, use_cache=use_cache)

class DependParam(Param):
    @classmethod
    def _check_param(
        cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
    ) -> Optional["DependParam"]:
        if isinstance(param.default, DependsInner): # 只有DependsInner会被转化为DependParam
            dependency: T_Handler
            ```
            param.default是DependsInner类型,param.default.dependency就是Depends的参数,例如前文的get_msg
            所以一般来说param.default.dependency不是None,除非你在使用Depends时没有传入函数,如async def(msg=Depends())
            ```
            if param.default.dependency is None: # 如async def(msg:str=Depends())或async def(msg=Depends())
                assert param.annotation is not param.empty, "Dependency cannot be empty" # handler参数没有类型注释且Depends没有传参就会报错,如async def(msg=Depends())
                dependency = param.annotation # 形如async def(msg:get_msg=Depends()),等价于async def(msg=Depends(get_msg))
            else:
                dependency = param.default.dependency # 例如async def(msg=Depends(get_msg)),dependency就是get_msg

            ```
            省流:async def(msg:get_msg=Depends())或者async def(msg=Depends(get_msg))都是正确写法。对于第二种写法,msg的类型注释不会影响代码运行
            ```
            sub_dependent = Dependent[Any].parse(
                call=dependency,
                allow_types=allow_types,
            ) # dependency转化为Dependent,sub_dependent的allow_types与handler生成的主Dependent相同,意味着依赖注入可以递归调用
            return cls(
                Required, use_cache=param.default.use_cache, dependent=sub_dependent
            ) # DependsInner转化为DependParam,

    @classmethod
    def _check_parameterless(
        cls, value: Any, allow_types: Tuple[Type[Param], ...]
    ) -> Optional["Param"]:
        if isinstance(value, DependsInner): # 和_check_param类似
            assert value.dependency, "Dependency cannot be empty"
            dependent = Dependent[Any].parse(
                call=value.dependency, allow_types=allow_types
            )
            return cls(Required, use_cache=value.use_cache, dependent=dependent) # DependsInner转化为DependParam

    # 可以看出DependParam要么是DependParam要么是DependParameterless

BotParam

class BotParam(Param):
    @classmethod
    def _check_param(
            cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
    ) -> Optional["BotParam"]:
        from nonebot.adapters import Bot

        if param.default == param.empty: # 有两种用法
            if generic_check_issubclass(param.annotation, Bot): # async def(xxx:Bot)这一种用法,不能接默认值,参数名可以随便取,Bot需要是nonebot.internal.adapter.bot.Bot本身或它的子类
                checker: Optional[ModelField] = None
                if param.annotation is not Bot:
                    ```
                    如果Bot不是nonebot.internal.adapter.bot.Bot,说明Bot是它的子类
                    这说明bot是协议的bot而不是基类bot,于是记录协议的bot的信息
                    只有协议bot消息才能通过check(将在下一节介绍)
                    例如handler规定bot是OneBot V11的bot,那只有OneBot V11的事件会触发这个handler,
                    那为什么基类Bot不需要check呢?这样的话如果handler的bot是基类bot,那么这个插件是可以多平台运行的
                    ```
                    checker = ModelField(
                        name=param.name,
                        type_=param.annotation,
                        class_validators=None,
                        model_config=CustomConfig,
                        default=None,
                        required=True,
                    )
                return cls(Required, checker=checker) # 将Bot转化为BotParam。基类Bot的checker为None,任何消息都能触发handler,协议Bot只有收到对应协议的消息才能触发handler
            elif param.annotation == param.empty and param.name == "bot": # async def(bot)是另一种用法,参数名必须是bot,且不能有类型注释,这表明handler是可以多平台运行的
                return cls(Required) # 基类Bot的BotParam

EventParam

class EventParam(Param): # 和BotParam如出一辙
    @classmethod
    def _check_param(
        cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
    ) -> Optional["EventParam"]:
        from nonebot.adapters import Event

        if param.default == param.empty:
            if generic_check_issubclass(param.annotation, Event):
                checker: Optional[ModelField] = None
                if param.annotation is not Event:
                    checker = ModelField(
                        name=param.name,
                        type_=param.annotation,
                        class_validators=None,
                        model_config=CustomConfig,
                        default=None,
                        required=True,
                    )
                return cls(Required, checker=checker) # 将Event转化为EventParam。基类Event的checker为None,任何事件都能触发handler,协议Event只有收到对应协议的event才能触发handler
            elif param.annotation == param.empty and param.name == "event":
                return cls(Required) # 基类Event转化的EventParam,可以多平台触发

StateParam

class StateParam(Param):
    @classmethod
    def _check_param(
        cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
    ) -> Optional["StateParam"]:
        if param.default == param.empty: # 不能有默认值
            if param.annotation is T_State: # 用法一:async def (xxx:T_State),参数名随便取,类型注释必须是nonebot.typing.T_State
                return cls(Required)
            elif param.annotation == param.empty and param.name == "state": # 用法二:async def (state),参数名必须是state且不能有类型注释
                return cls(Required)

MatcherParam

class MatcherParam(Param):
    @classmethod
    def _check_param(
        cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
    ) -> Optional["MatcherParam"]:
        from nonebot.matcher import Matcher
        if generic_check_issubclass(param.annotation, Matcher) or (
            param.annotation == param.empty and param.name == "matcher"
        ):
        ```
        async def (xxx:Matcher)参数名随便取,类型必须是nonebot.matcher.Matcher
        或
        async def (matcher)参数名必须是matcher,且没有类型注释
        ```
            return cls(Required)
        ```
        为什么要MatcherParam呢,我自己就在handler里能不知道matcher是什么?
        其实MatcherParam主要是在parameterless,使得依赖注入对不同handler可以复用
        如前文parameterless=[Depends(parse_int("month"))]这种用法
        ```

ArgParam

class ArgInner:
    def __init__(
        self, key: Optional[str], type: Literal["message", "str", "plaintext"]
    ) -> None:
        self.key = key
        self.type = type

    def __repr__(self) -> str:
        return f"ArgInner(key={self.key!r}, type={self.type!r})"

def Arg(key: Optional[str] = None) -> Any:
    return ArgInner(key, "message")

def ArgStr(key: Optional[str] = None) -> str:
    return ArgInner(key, "str")  # type: ignore

def ArgPlainText(key: Optional[str] = None) -> str:
    return ArgInner(key, "plaintext")  # type: ignore

class ArgParam(Param): # 暂时可能看不懂用法,到下一节介绍Matcher的运行时再研究
    @classmethod
    def _check_param(
        cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
    ) -> Optional["ArgParam"]:
        if isinstance(param.default, ArgInner):
            return cls(
                Required, key=param.default.key or param.name, type=param.default.type
            )

ExceptionParam

class DefaultParam(Param):
    @classmethod
    def _check_param(
        cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
    ) -> Optional["ExceptionParam"]:
        if generic_check_issubclass(param.annotation, Exception) or (
            param.annotation == param.empty and param.name == "exception"
        ):
            ```
            可以用Exception作为类型注释或将参数命名为exception
            ```
            return cls(Required)

DefaultParam

class DefaultParam(Param):
    @classmethod
    def _check_param(
        cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
    ) -> Optional["DefaultParam"]:
        ```
        由于函数被拆解了,python自身的默认值写法无法使用,需要借助DefaultParam
        至于为什么能实现默认值,见前文关于Dependent.parse的分析
        ```
        if param.default != param.empty:
            return cls(param.default)

发表评论 取消回复
表情 图片 链接 代码

分享