#实践案例#Python插件库封装-(支持其他插件调用、WebAPI接口调用、执行计划调用等)原创
金蝶云社区-CQ周玉立
CQ周玉立
116人赞赏了该文章 1,297次浏览 未经作者许可,禁止转载编辑于2024年10月12日 17:44:12
summary-icon摘要由AI智能服务提供

本文讨论了在Python插件开发中,如何封装和管理通用的Python方法以便复用。介绍了“CV技能”复制粘贴方法的不足,并提出了将Python方法封装成代码文件,通过exec指令或IronPython引擎动态加载执行。进一步分析了IronPython的运行原理,并说明了如何封装Python插件库,以便其他插件(包括C#插件)调用。还讨论了将封装好的Python插件方法开放成外部WebAPI接口,以及如何实现这些功能。最后,总结了该方案的优势,并提到可以作为Python代码管理工具,实现代码共享和协同开发。

一、业务背景

    小伙伴们实践了Python插件开发后,觉得很方便,如果你擅于代码封装,就会形成许多通用的插件方法,那这些插件方法如何快速复用呢?

    目前采用的是"CV技能"(复制粘贴,把曾经封装过的Python插件方法复制过来使用,基于Python插件的便利性,这也是很不错的!

    但是,这也会有一些缺点,不方便统一管理这些通用的代码,如果方法里面后续发现了BUG或者需要完善,有可能需要对很多个复制使用这个方法的插件中都修改!

    如果会C#插件开发的同学就知道,通用的插件代码,我们可以封装成dll库,然后添加到不同的插件工程中来进行引用!

  •     Python插件运维迭代起来非常方便快捷,能不能把一些通用的Python方法封装成代码文件,提供给其他地方调用呢?

    不用怀疑,当然是可以的Python作为脚本语言,可以动态加载使用,甚至可以直接通过exec指令,直接执行代码中把Python脚本的字符串,

    就像  @请输入昵称___老师 分享的这个案例:#使用技巧# 开发个Python在线测试功能

    也就是说,我们可以把要执行的Python代码,作为字符串传递给exec执行,至少这是一种可行的实现思路!

    还有其他方法吗?可以再深入思考一下,我们在BOS里面注册Python脚本之后,系统是如何运行的呢?

    我们知道,Python插件开发并不是原生的Python开发,而是IronPython,这个在第1篇文章就给大家介绍过了,IronPython是.NET平台用来解释执行Python脚本的,系统运行我们注册的Python脚本时,也是回到.NET平台通过IronPython引擎来执行的,这也是C#的插件可以用Python来开发的核心支撑,主要是以下2个dll库来完成的:

(IronPython的具体实现原理和这2个dll库的用法,大家可以自行查询资料了解更多信息)

  •     IronPython.dll

  •     Microsoft.Scripting.dll

    实际上Python插件的本质是用Python语法来调用.NET库来实现开发的,也就是说我们可以在Python插件中调用上面的2个dll库,基于这个原理,我们封装好的Python插件代码,不仅通过C#插件中能调用,在Python插件也可以调用!

    基于以上思路分析, 我们就可以封装自己的Python插件库,然后提供给其他地方调用了,可以包括以下几种场景:

  •     其他插件调用封装好的Python插件方法(Python插件和C#插件均可)

  •     封装好的Python插件方法,开放成外部WebAPI接口,可以快速定制各种自定义WebAPI接口,并且运维迭代方便。

       (基于此案例思路:#实践案例#Python插件变通实现自定义WebAPI接口查询报表)

  •     C#插件执行计划插件调用封装好的Python插件方法(虽然Python插件不能直接开发执行计划插件,这也能便通过实现了)

二、解决方案

  •     首先,如何对封装好的Python代码文件进行管理?放到固定文件夹?数据库?

    这里选择的存到服务器一个自定义的文件夹下面,这个文件夹我们可以根据WebSite的相对路径来自动识别,建议放到WebSite的同级目录,方便查找,并且不在IIS站点目录下,与IIS服务不会相互影响!

    然后我们开发一个简单的单据来作为文件上传入口,记录文件的保存路径:

        上传代码文件->单据保存自动将上传的文件移动到固定的文件路径->删除单据也可以自动删除该文件!

image.png

        image.png

  •     代码查看,上传后的代码文件,可以支持在线查看和复制

        (由于Python代码缩进格式敏感,暂不开发在线编辑功能,可用记事本编辑保存后重新上传)

        image.png

  •     解析Python代码文件,将代码文件中的方法以及参数解析出来,然后录入方法和参数说明,Python插件库就更完善了!

    如果解析报错,说明上传的代码文件有问题,则需要检查修改之后重新上传,这样相当于也有了一个代码检查的功能,保证代码文件的可靠性!

        image.png

  •     方法调用,动态调用Python代码文件的关键代码就在这里

    其他插件要调用方法时,就可以用这样的代码写法来调用Python代码文件中的方法,这里分享一下如何调用Python代码文件的核心代码!

    前面提到了有2种方式可以动态执行Python代码:exec指令IronPython引擎 

    exec指令,我验证过也能用,这里简单介绍一下!

    (我后面实际选择的是使用IronPython引擎,用IronPython更方便解析Python代码方法签名)

        exec其实就是把一段Python代码的字符串直接执行,类似于执行SQL语句,当然对于执行过程也可以传参和获取返回值,大家可以查询exec的相关资料!

        这里有2种思路:一种是直接封装方法代码,然后把调用方法的代码拼接成字符串来调用

        image.png

    另一种是直接封装Python类,然后通过类对象来调用方法(定义静态方法,可以省略self参数),这种思路似乎更好一点!

    image.png

    通过IronPython引擎来动态执行Python代码文件核心重点代码来了...

        首先,根据Py代码文件路径,通过IronPython引擎解析得到一个ScriptScope(脚本作用域)

        image.png

        I.如果是插件代码调用,可以直接通过: 作用域.方法名(...), 可直接调用固定方法,并传入固定的匹配参数,和通过类名调用静态方法的写法类似了!

#...前面如何获取Py代码库文件的路径PyFileFullName就很简单了,省略...
pythonClassScope=getPyClassInfo(PyFileFullName);
BillFormId="BD_Material";
allFlds=pythonClassScope.GetALLFiedInfoByFormId(this.Context, BillFormId);

        II.也可以通过方法名的字符串动态调用(开放到WebAPI接口调用时需要动态传方法名和动态传递参数),此时注意方法参数列表传递的处理方式,有2种方式

image.png

#①借助exec来调用,方法名和参数列表都是拼接成字符串
retObj=None;#声明一个变量接收返回值
exec("retObj=pythonClassScope.GetALLFiedInfoByFormId(this.Context, BillFormId);");
#②(推荐)先根据方法名从Scope中获取到Py方法对象,然后再调用
pythonClassScope=getPyClassInfo(PyFileFullName);
methodName="GetALLFiedInfoByFormId";
onePyFun=getPyFun(pythonClassScope,methodName);#onePyFun就代表方法名对应的方法对象
retObj=onePyFun(this.Context, BillFormId);

#如果参数列表也是动态匹配的,Py插件调用时,可使用Python里面的"*"号的解包用法传参(可自行查询相关用法)
funParas=[];#先把参数按顺序构造到一个列表中,通常是循环某个数据来源,WebAPI接口动态调用时,使用此写法封装调用过程
funParas.append(this.Context);
funParas.append(BillFormId);
retObj=onePyFun(*funParas);#调用方法

  •     WebAPI接口调用,对Python插件库封装接口调用触发的功能

    基于前面发布的这个案例的思路:#实践案例#Python插件变通实现自定义WebAPI接口查询报表

    实现原理类似,不过多叙述,核心就是根据Python代码文件解析的方法,配置参数列表->指定参数类型->生成调用的JSON参数格式

      然后开发一个通用的保存服务插件,按上述使用方法名动态调用的逻辑来执行方法,然后返回方法的返回值即可!

      image.png

三、实现效果演示

(这里主要演示一下WebAPI调用Python插件库的效果)

Python插件库演示.gif

四、总结几点

  • 基于以上原理,可以把常用的Python插件方法分类封装成插件库,然后其他地方可以通过获取这个Python文件来调用。

  • 执行计划插件也能调用,可用C#封装一个通用的执行计划插件,在Run方法里面调用指定的Py方法即可,这样就能变通用Python来开发定时任务,而且维护方便。

  • 如果封装给WebAPI调用的方法,由于接口数据交互是以JSON的形式,要注意参数类型最好是普通数据类型(文本、数字、JSON对象等),并且返回值也最好是字符串,复杂对象可以序列化成JSON字符串返回。

  • 插件方法中经常会用到Context,要把这个提取出来,作为方法参数传入,不可直接在Py插件库的方法中访问this.Context。

  • 这个Python插件库建立起来之后,可以作为自己的一个Python代码管理工具,并且可以做一些更精准的搜索功能,可以快速的查找想要的代码。

  • 基于这个Python插件库的建立,可以进一步实现Python插件开发协同,甚至可以通过git等平台来实现Python插件代码共享、快速复用。

==============================正文结束=====================================

感谢大家的关注与评阅,希望能为大家的实际问题带来参考和启发。

此方案全部用Python插件实现,如果需要此方案的二开补丁或者完整源码,可私信作者!


发布于 金蝶云星空BOS开发交流圈 社群

图标赞 116
116人点赞
还没有人点赞,快来当第一个点赞的人吧!
图标打赏
2人打赏
还没有人打赏,快来当第一个打赏的人吧!