コード例 #1
0
def is_kwarg_annotation_correct(
    parameter_name: str,
    parameter_type: Any,
    supposed_type: Any,
    common_prefix: str = "",
):
    wrong_type_flag = True

    if get_origin(supposed_type) is Union:
        for type_ in get_args(supposed_type):
            if type_ == parameter_type:
                wrong_type_flag = False

    else:
        if type(supposed_type) == str:  # 避免循环导入,有时会使用字符串形式的类型
            # <class 'pepperbot.core.message.chain.MessageChain'>
            pattern = re.search(r"<class '(.*)'>", str(parameter_type))
            if not pattern:
                raise InitializationError(common_prefix + f"无法确认参数{parameter_name}的类型")

            str_parameter_type = pattern.groups()[0].split(".")[-1]
            if supposed_type == str_parameter_type:
                wrong_type_flag = False

        else:
            if supposed_type == parameter_type:
                wrong_type_flag = False

    if wrong_type_flag:
        raise InitializationError(
            common_prefix
            + f"的参数{parameter_name}的类型应该为{supposed_type},而不是{parameter_type}"
        )
コード例 #2
0
def is_handler_method_args_valid(
    class_handler: Any, method: Callable, method_name: str
):

    if "onebot" in method_name:
        mapping = ONEBOTV11_KWARGS_MAPPING
        event_name = method_name.replace("onebot_", "")

    elif "keaimao" in method_name:
        mapping = KEAIMAO_KWARGS_MAPPING
        event_name = method_name.replace("keaimao_", "")

    else:
        mapping = UNIVERSAL_KWARGS_MAPPING
        event_name = method_name

    kwarg_name_type_mapping, usable_kwargs_hint = gen_usable_kwargs_hint(
        mapping, event_name
    )
    usable_kwarg_names = kwarg_name_type_mapping.keys()

    source_file_name = inspect.getsourcefile(class_handler)
    class_handler_name = class_handler.__name__

    common_prefix = (
        f"\n{source_file_name}文件中的类响应器{class_handler_name}的" + f"{method_name}事件"
    )

    all_args, var_args, var_kwargs = inspect.getargs(method.__code__)

    if var_args or var_kwargs:
        raise InitializationError(
            common_prefix
            + "不需要提供*或者**参数,PepperBot会自动根据声明的参数以及类型注入"
            + usable_kwargs_hint
        )

    for arg_name in all_args[1:]:  # 第一个是self
        if arg_name not in usable_kwarg_names:
            raise InitializationError(
                common_prefix + f"上不存在参数{arg_name}" + usable_kwargs_hint
            )

        if arg_name not in method.__annotations__.keys():
            raise InitializationError(
                common_prefix
                + f"的参数{arg_name}未提供类型注解,其类型为{kwarg_name_type_mapping[arg_name]}"
                + usable_kwargs_hint
            )

    # 经过上两步验证,此时的参数,一定是有效的,而且有类型注解
    for arg_name, arg_type in method.__annotations__.items():

        supposed_arg_type = kwarg_name_type_mapping[arg_name]

        is_kwarg_annotation_correct(
            arg_name, arg_type, supposed_arg_type, common_prefix
        )
コード例 #3
0
ファイル: initial.py プロジェクト: SSmJaE/PepperBot
    def register_adapter(
        self,
        bot_protocol: T_BotProtocol,
        receive_protocol: T_WebProtocol,
        backend_protocol: T_WebProtocol,
        backend_port: int,
        backend_host="127.0.0.1",
        receive_uri: str = "",
    ):
        uri: str
        request_handler: Optional[Callable] = None

        if bot_protocol not in ALL_AVAILABLE_BOT_PROTOCOLS:
            raise InitializationError(f"尚不支持的机器人协议 {bot_protocol}")

        if receive_uri:
            uri = receive_uri
        else:
            if receive_protocol == "http":
                request_handler = lambda request: http_receiver(request, bot_protocol)
                uri = DEFAULT_URI[bot_protocol] + "/http"

            elif receive_protocol == "websocket":
                request_handler = lambda request, ws: websocket_receiver(
                    request, ws, bot_protocol
                )
                uri = DEFAULT_URI[bot_protocol] + "/ws"

            else:
                raise InitializationError(f"未知通信协议 {receive_protocol}")

        create_web_routes(
            sanic_app,
            receive_protocol=receive_protocol,
            uri=uri,
            request_handler=request_handler,
        )

        api_caller: ApiCaller

        if backend_protocol == "http":
            api_caller = ApiCaller(
                bot_protocol=bot_protocol,
                backend_protocol="http",
                backend_port=backend_port,
                backend_host=backend_host,
            )
        elif backend_protocol == "websocket":
            raise InitializationError(f"backend_protocol尚不支持ws")
        else:
            raise InitializationError(f"未知通信协议 {backend_protocol}")

        api_callers[bot_protocol] = api_caller
コード例 #4
0
def is_command_method_return_valid(
    method: Callable, method_name: str, method_names: List[str], common_prefix: str
):
    """
    通过ast,保证方法返回值要么为None,要么是同一class的其它方法名

    同时不能是catch, timeout, exit,不应该由指令作者主动触发;initial和finish可以

    即使有了静态检测,运行时检测方法返回值也是需要的
    """
    for info in get_return_identifiers(method):
        ids = info["ids"]

        wrong = False

        if not ids:  # 不返回
            continue

        if len(ids) > 1:  # 不能返回多个值
            wrong = True

        identifier = ids[0]
        if identifier not in method_names:  # 要么返回下一步的方法名,要么不返回
            wrong = True

        if identifier in LIFECYCLE_NO_PROGRAMMIC:
            wrong = True

        if wrong:
            raise InitializationError(
                common_prefix + f"方法 {method_name} 的返回值可以为除catch, timeout, exit以外的生命周期,"
                "即initial和finish,或者直接返回None(不返回)以结束会话"
            )
コード例 #5
0
def is_handler_method_name_valid(method_name: str):
    if not (
        method_name in ALL_META_EVENTS
        or method_name in ALL_COMMON_EVENTS
        or method_name in ALL_GROUP_EVENTS
        or method_name in ALL_PRIVATE_EVENTS
    ):
        raise InitializationError(f"无效的事件名 {method_name}")
コード例 #6
0
ファイル: cache.py プロジェクト: SSmJaE/PepperBot
def cache_class_command(class_command: Callable, command_name: str):
    # 多个group handler,相同command的处理(解析所有指令和groupId,重新生成缓存)
    # 同一个commandClass,就实例化一次

    # if lifecycle_name not in get_own_methods():

    class_command_instance = class_command()
    command_method_mapping = {}

    for method in get_own_methods(class_command_instance):
        method_name = method.__name__

        patterns: List[Tuple[str, T_PatternArg]] = []
        compressed_patterns = []

        if method_name not in LIFECYCLE_WITHOUT_PATTERNS:
            signature = inspect.signature(method)

            for arg_name, p in signature.parameters.items():

                if p.default == "PatternArg":
                    annotation = p.annotation
                    if annotation not in runtime_pattern_arg_types:
                        raise InitializationError(f"仅支持str, bool, int, float和所有消息类型")

                    # 未具体指定类型(int, float, bool)的Text按照str处理
                    patterns.append(
                        (arg_name, annotation if annotation != Text else str)
                    )

            # debug(patterns)

            compressed_patterns = merge_text_of_patterns(patterns)
        # debug(compressed_patterns)

        command_method_mapping[method_name] = CommandMethodCache(
            method=method,
            patterns=patterns,
            compressed_patterns=compressed_patterns,
        )

    class_command_mapping[command_name] = ClassCommandCache(
        class_instance=class_command_instance,
        command_method_mapping=command_method_mapping,
    )

    command_config: CommandConfig = getattr(class_command, COMMAND_CONFIG)

    class_command_config_mapping[command_name]["default"] = command_config
コード例 #7
0
ファイル: __init__.py プロジェクト: SSmJaE/PepperBot
    def decorator(handler: Callable):

        if not isclass(handler):
            raise InitializationError("register装饰器只能注册class,不能注册function")

        # 收集register中的BotRoute,apply_routes中应用
        register_routes.append(
            BotRoute(
                handler=handler,
                commands=commands,
                groups=groups,
                friends=friends,
            ))

        return handler
コード例 #8
0
def is_valid_class_command(class_command: Any):
    source_file_name = inspect.getsourcefile(class_command)
    class_command_name = class_command.__name__

    common_prefix = f"\n{source_file_name} 文件中的指令 {class_command_name} 的"

    methods = list(get_own_methods(class_command))
    method_names = [method.__name__ for method in methods]

    if not "initial" in method_names:
        raise InitializationError("指令必须定义initial方法作为入口")

    for method in methods:
        method_name = method.__name__

        is_command_method_args_valid(method, method_name, common_prefix)
        is_command_method_return_valid(method, method_name, method_names, common_prefix)

    return True
コード例 #9
0
def is_command_method_args_valid(
    method: Callable, method_name: str, common_prefix: str
):

    common_prefix += f"{method_name}方法"

    signature = inspect.signature(method)

    for arg_name, p in signature.parameters.items():

        # self
        if arg_name == "self":
            continue

        # no *, **
        if p.kind == p.VAR_POSITIONAL or p.kind == p.VAR_KEYWORD:
            raise InitializationError(
                common_prefix + "不需要提供*或者**参数,PepperBot会自动根据声明的参数以及类型注入"
            )

        ## has type hint
        kwarg_name_type_mapping, usable_kwargs_hint = gen_usable_kwargs_hint(
            COMMAND_DEFAULT_KWARGS, method_name
        )

        if p.annotation == p.empty:
            raise InitializationError(
                common_prefix
                + f"指令的方法 {method_name} 的参数 {arg_name} 未提供类型注解"
                + usable_kwargs_hint
                if arg_name in kwarg_name_type_mapping.keys()
                else ""
            )

        else:
            # kwargs == no default value
            if p.default != p.empty and p.default != "PatternArg":
                raise InitializationError(
                    common_prefix + f"不应为 {arg_name} 除pattern外的参数设置默认值"
                )

            # type hint correct
            if p.default != "PatternArg":
                supposed_type = kwarg_name_type_mapping.get(arg_name)
                if not supposed_type:
                    raise InitializationError(
                        common_prefix + f"{arg_name} 无对应的类型,请确认该参数是否为有效参数"
                    )

                is_kwarg_annotation_correct(
                    arg_name, p.annotation, supposed_type, common_prefix
                )

        # pattern args
        if p.default == "PatternArg":
            # no pattern in lifecycle hooks
            if method_name in LIFECYCLE_WITHOUT_PATTERNS:
                raise InitializationError(common_prefix + f"这些生命周期不应支持pattern")

            if p.annotation not in runtime_pattern_arg_types:
                raise InitializationError(
                    common_prefix + f"仅支持str, bool, int, float和所有消息类型"
                )
コード例 #10
0
def parse_routes(routes: Iterable[BotRoute]):
    for route in routes:

        commands = []
        if route.commands:
            commands = route.commands

        command_names: Set[str] = set()
        for command in commands:
            command_name = command.__name__

            if not is_valid_class_command(command):
                raise InitializationError(
                    f"路由 {route} 中的指令 {command_name} 不符合要求")

            if command_name not in class_command_mapping.keys():
                cache_class_command(command, command_name)

            command_names.add(command_name)

        handler_names: Set[str] = set()
        if route.handlers:
            for handler in route.handlers:
                handler_name = handler.__name__

                if not is_valid_class_handler(handler):
                    raise InitializationError(
                        f"路由 {route} 中的消息响应器 {handler_name} 不符合要求")

                if handler_name not in class_handler_mapping.keys():
                    cache_class_handler(handler, handler_name)

                handler_names.add(handler_name)

        # -------------------------------------------------------------------------------

        # pydantic会校验不合法的情况,所以不用else
        if route.groups == "*":
            for protocol in ALL_AVAILABLE_BOT_PROTOCOLS:
                route_mapping.global_commands[protocol][
                    "group"] |= command_names
                route_mapping.global_handlers[protocol][
                    "group"] |= handler_names

        elif route.groups and type(route.groups) == dict:
            for protocol, group_ids in route.groups.items():
                if group_ids == "*":
                    route_mapping.global_handlers[protocol][
                        "group"] |= handler_names

                else:
                    for group_id in group_ids:
                        group_id = str(group_id)

                        route_mapping.mapping[protocol]["group"][group_id][
                            "commands"] |= command_names

                        route_mapping.mapping[protocol]["group"][group_id][
                            "class_handlers"] |= handler_names

        elif callable(route.groups):
            validator = route.groups

            if not is_valid_route_validator(validator):
                raise InitializationError(f"路由{route}中的groups的validator不符合要求")

            validator_name = validator.__name__
            if validator_name not in route_validator_mapping.keys():
                cache_route_validator(validator, validator_name)

            route_mapping.mapping["validators"]["group"][validator_name][
                "commands"] |= command_names

            route_mapping.mapping["validators"]["group"][validator_name][
                "class_handlers"] |= handler_names

        # -------------------------------------------------------------------------------

        if route.friends == "*":
            for protocol in ALL_AVAILABLE_BOT_PROTOCOLS:
                route_mapping.global_commands[protocol][
                    "private"] |= command_names
                route_mapping.global_handlers[protocol][
                    "private"] |= handler_names

        elif route.friends and type(route.friends) == dict:
            for protocol, private_ids in route.friends.items():
                if private_ids == "*":
                    route_mapping.global_handlers[protocol][
                        "private"] |= handler_names

                else:
                    for private_id in private_ids:
                        private_id = str(private_id)

                        route_mapping.mapping[protocol]["private"][private_id][
                            "commands"] |= command_names

                        route_mapping.mapping[protocol]["private"][private_id][
                            "class_handlers"] |= handler_names

        elif callable(route.friends):
            validator = route.friends

            if not is_valid_route_validator(validator):
                raise InitializationError(f"路由{route}中的friends的validator不符合要求")

            validator_name = validator.__name__
            if validator_name not in route_validator_mapping.keys():
                cache_route_validator(validator, validator_name)

            route_mapping.mapping["validators"]["private"][validator_name][
                "commands"] |= command_names

            route_mapping.mapping["validators"]["private"][validator_name][
                "class_handlers"] |= handler_names