前言

最近在处理一个 SKA 客户问题的时候发现影刀自定义指令有个很坑的地方,这里记录一下。

问题描述

问题很简单,客户开发了一个自定义指令,这个指令被 100 多个应用使用,指令原本设计了 N 个参数,现在指令要跟新要增加1个参数。

类比到 python 里就是原本原本函数要3个变量,现在更新了函数多了一个变量,理论上来说只要新增的变量设置个默认值就不影响旧代码的使用。

在影刀中按理来说只要新参数设置默认值应该不会影响旧流程的调用,然而现实不是这样。

举例说明

这里我准备了一个案例,案例中的自定义指令需要传入两个参数 p1、p2 如下图

使用方调用这个指令并传入2个参数如下图

现在,更自定义指令新增加一个参数 p3 并且设置默认值为 None 如下图

这个时候更新应用中的指令,点击运行测试我们会发现一切都很顺利没有任何报错,如下图

一切都这么顺利,旧流程不用做任何修改就能使用新指令,所以问题在哪呢?

在影刀中有 VisualFlow(可视化流程) 和 CodeFlow(代码流程) 两种方式,前面我们使用的是 VisualFlow 如果我们用代码的方式调用自定义指令会发生什么呢?

我们会悲伤的发现使用代码调用的方式会报错,提示我们少传入了一个参数。这个情况放在客户的场景下就是,客户用代码的方式在100多个应用中调用了这个自定义指令,因为自定义指令更新现在需要去100多个应用中修改调用方式。

可以想像这绝对不是一个好的选择,哪么有办法既更新指令又可以不对旧流程做任何修改吗?有的兄弟!有的!

解决方案

首先,我们点开可视化指令的源码,我们可以在源码中看到,影刀可视化调用自定义指令的代码是这样的

xbot_visual.process.run(process="xbot_extensions.activity_25e7d62a.process1", package=__name__, inputs={
            "p1": "111",
            "p2": "222"
            }, outputs=[
        ], _block=("main", 1, "a"))

可以看到是 VisualFlow 通过 xbot_visual 来调用的,并且这种调用方式支持不传参数 p3 ,在 CodeFlow 中调用是通过 xbot_extension 调用,这种调用就要求我们一定传入 p3 。

我们在深扒一下影刀自定义指令的源码,在源码中我们可以看到__init__.py 文件内容如下

import xbot_visual

def process1(p1,p2,p3):
    """
    a
    
    * @param p1,
    * @param p2,
    * @param p3,
    """
    outputs = []
    inputs = {"p1":p1,"p2":p2,"p3":p3}
    extension_module, activity_func = xbot_visual.process.activity_entry("xbot_extensions.activity_25e7d62a.process1", __name__)
    try:
        return xbot_visual.process.run(process="xbot_extensions.activity_25e7d62a.process1",package=__name__,inputs=inputs, outputs=outputs)
    finally:
        xbot_visual.process.replace_activity_module_to_entry_method("xbot_extensions.activity_25e7d62a.process1", extension_module, activity_func)

看到这个我们基本可以清楚 xbot_extensions.activity_25e7d62a.process1 走的就是 __init__.py 中的 process1 ,而必须传入 p3 的原因就是 process1 这个函数写死了必须要传 p3 ,所以我们只需要改一下函数定义,改成 def process1(p1, p2, p3=None) 问题迎刃而解。

并且这段代码我们也能发现 xbot_extensions 底层还是通过 xbot_visual 来调用自定义指令。