You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Typeguard运行时类型检查中Callable参数匹配错误解决问询

Typeguard无法正确识别Callable的3个参数类型

背景

带类型标注的get_next_actions函数会返回一个带类型标注的函数actions_0(或None)。actions_0函数接收3个参数,返回一个Dict。

from typing import TYPE_CHECKING, Callable, Dict, List
if TYPE_CHECKING:
    from some_imports import Script
else:
    Script = object


# pylint: disable=W0613
@typechecked
def get_next_actions(
    required_objects: List[Dict[str, str]],
    optional_objects: List[Dict[str, str]],
    script: Script,
) -> Union[Callable[[AutomatorDevice, Screen, Script], Dict], None]:
    """根据必填和可选对象决定下一步操作
    示例操作序列:
    0. 选择文本框
    1. 向文本框输入数据
    2. 点击“下一步”按钮

    应用跳转到下一页后等待预设时长,可选择重试预设次数
    """
    # 初始页面直接点击确定
    return actions_0

return Screen(
    get_next_actions=get_next_actions,
    is_start=True,
    max_retries=max_retries,
    screen_nr=screen_nr,
    wait_time_sec=wait_time_sec,
    required_objects=required_objects,
)


# pylint: disable=W0613
@typechecked
def actions_0(dev: AutomatorDevice, screen: Screen, script: Script) -> Dict:
    """执行当前页面的第1组操作
    点击“连接请求”弹窗中的“确定”按钮
    """
    # 点击确定按钮
    dev(resourceId="android:id/button1").click()

    # 返回预期跳转的页面编号
    action_nr: int = int(inspect.stack()[0][3][8:])
    screen_nr: int = screen.screen_nr
    script_flow: nx.DiGraph = script.script_graph
    return {
        "expected_screens": get_expected_screen_nrs(
            G=script_flow, screen_nr=screen_nr, action_nr=action_nr
        )
    }

错误信息

使用Typeguard做类型检查时,会报错不允许为Callable指定3个输入类型:

TypeCheckError: the return value (function) did not match any element in the union:
  Callable[list, Dict]: has too many arguments in its declaration; expected 2 but 3 argument(s) declared
  NoneType: is not an instance of NoneType

将返回类型标注从:

) -> Union[Callable[[AutomatorDevice, Screen, Script], Dict], None]:

修改为:

) -> Union[Callable[[AutomatorDevice, float, Script], Dict], None]:

错误会消失。

问题

如何让Typeguard正确识别传入Callable的是action_0函数的类型标注?

最小可复现示例(MWE)

mwe.py

"""最小复现示例"""

import inspect
from typing import TYPE_CHECKING, Callable, Dict, List

import networkx as nx
from typeguard import typechecked
from uiautomator import AutomatorDevice

from some_imports import Screen

if TYPE_CHECKING:
    from some_imports import Script
else:
    Script = object


# pylint: disable=W0613
@typechecked
def get_next_actions(
    required_objects: List[Dict[str, str]],
    optional_objects: List[Dict[str, str]],
    script: float,
) -> Callable[[AutomatorDevice, Screen, Script], Dict]:
    """根据必填和可选对象决定下一步操作
    示例操作序列:
    0. 选择文本框
    1. 向文本框输入数据
    2. 点击“下一步”按钮

    应用跳转到下一页后等待预设时长,可选择重试预设次数
    """
    # 初始页面直接点击确定
    return actions_0


# pylint: disable=W0613
@typechecked
def actions_0(dev: AutomatorDevice, screen: Screen, script: Script) -> Dict:
    """执行当前页面的第1组操作
    点击“连接请求”弹窗中的“确定”按钮
    """
    # 点击确定按钮
    dev(resourceId="android:id/button1").click()

    # 返回预期跳转的页面编号
    action_nr: int = int(inspect.stack()[0][3][8:])
    screen_nr: int = screen.screen_nr
    script_flow: nx.DiGraph = nx.DiGraph()
    return {
        "expected_screens": get_expected_screen_nrs(
            G=script_flow, screen_nr=screen_nr, action_nr=action_nr
        )
    }


@typechecked
def get_expected_screen_nrs(
    G: nx.DiGraph, screen_nr: int, action_nr: int
) -> List[int]:
    """返回当前页面执行指定操作后预期跳转的页面编号列表"""
    expected_screens: List[int] = []
    for edge in G.edges:
        if edge[0] == screen_nr:
            if action_nr in G[edge[0]][edge[1]]["actions"]:
                expected_screens.append(edge[1])
    return expected_screens


required_objects: List[Dict[str, str]] = [{"key": "val_1"}]
optional_objects: List[Dict[str, str]] = [{"key": "val_2"}]
script: float = 9.4

something = get_next_actions(
    required_objects=required_objects,
    optional_objects=optional_objects,
    script=script,
)

print(something)

some_imports.py

from typing import Callable, Dict, List, Union
from typeguard import typechecked

class Screen:
    """代表Android应用的一个页面"""

    # pylint: disable=R0913
    # pylint: disable=W0102
    @typechecked
    def __init__(
        self,
        is_start: bool,
        get_next_actions: Callable[
            [Dict[str, str], Dict[str, str], Dict[str, str]],
            Union[Callable, None],
        ],
        max_retries: int,
        required_objects: List[Dict[str, str]],
        screen_nr: int,
        wait_time_sec: float,
        optional_objects: List[Dict[str, str]] = [],
    ) -> None:
        print("hello world.")


# pylint: disable=R0902
class Script:
    """实验管理器
    先准备实验运行环境,再调用私有方法执行包含4个阶段的实验
    """

    # pylint: disable=R0903
    # pylint: disable=R0913
    @typechecked
    def __init__(
        self,
        app_name: str,
        overwrite: bool,
        package_name: str,
        version: str,
        cli_input_data: Dict[str, Union[str, Dict[str, str]]],
    ) -> None:
        self.app_name: str = app_name

运行python mwe.py会抛出以下错误:

Traceback (most recent call last):
  File "/home/name/git/temp/mwe.py", line 78, in <module>
    something = get_next_actions(
  File "/home/name/git/temp/mwe.py", line 36, in get_next_actions
    return actions_0
  File "/home/name/miniconda/envs/snncompare/lib/python3.10/site-packages/typeguard/_functions.py", line 164, in check_return_type
    check_type_internal(retval, annotation, memo)
  File "/home/name/miniconda/envs/snncompare/lib/python3.10/site-packages/typeguard/_checkers.py", line 756, in check_type_internal
    checker(value, origin_type, args, memo)
  File "/home/name/miniconda/envs/snncompare/lib/python3.10/site-packages/typeguard/_checkers.py", line 193, in check_callable
    raise TypeCheckError(
typeguard.TypeCheckError: the return value (function) has too many arguments in its declaration; expected 2 but 3 argument(s) declared

解决方案

问题根源在于**Screen类型的导入逻辑不一致**:Script仅在类型检查阶段导入,运行时用object替代,但Screen是直接导入的。Typeguard在运行时检查时,会尝试解析Screen的实际类型,导致错误识别Callable的参数数量。

修复步骤

  1. 将Screen的导入也放到TYPE_CHECKING分支,运行时用object替代:
if TYPE_CHECKING:
    from some_imports import Script, Screen
else:
    Script = object
    Screen = object
  1. 确保所有用于类型标注的自定义类,在运行时都被替换为object,避免Typeguard在运行时解析类型细节。

替代方案

如果需要运行时保留类型信息:

  • 使用typing.TypeVar定义Screen和Script的类型变量,确保Typeguard能正确识别。
  • 升级Typeguard到最新版本,旧版本存在Callable多参数检查的bug。

修改后重新运行代码,Typeguard将不再抛出参数数量不匹配的错误。


内容的提问来源于stack exchange,提问作者a.t.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 21:47:03