#使用技巧#Python插件调试技巧及常见报错分析(新手必看,老手看不上的 干货)原创
金蝶云社区-CQ周玉立
CQ周玉立
193人赞赏了该文章 2,240次浏览 未经作者许可,禁止转载编辑于2024年09月09日 16:22:09
summary-icon摘要由AI智能服务提供

本文概述了Python插件开发过程中常见的报错信息及其解决方法,强调了不依赖断点调试的调试技巧,如通过界面提示信息、抛出异常信息和二分法定位报错位置等。同时,文章分析了编译报错和运行报错的不同类别,包括引用错误、缩进错误、编码问题、命名空间错误、参数类型不匹配等,并为每类错误提供了详细的解决方法和示例代码,以帮助开发者更有效地解决报错问题,提升开发能力。

往期回顾:

【Python插件入门】第1篇:Python插件入门讲解

【Python插件入门】第2篇:基本开发过程介绍

【Python插件入门】第3篇:插件中如何进行数据操作

【Python插件入门】第4篇:单据表单插件

【Python插件入门】第5篇:单据列表插件

【Python插件入门】第6篇:操作服务插件

【Python插件入门】第7篇:简单账表服务插件

【Python插件入门】第8篇:账表表单插件

【Python插件入门】第9篇:单据转换插件

【Python插件入门】第10篇(完结篇):插件常用工具类分享


    Python插件的开发过程中经常会遇到一些报错,由于Python插件不方便断点调试,对初学者来说,遇到一些报错时,就会比较懵,所以我们要学会自己分析报错信息,从而定位我们代码里面可能发生该报错信息的位置,然后自行解决报错问题。

    这是做开发必不可少的一个课题,不仅可以提高解决问题的效率!

    掌握了这些报错信之后,我们就不必太依赖于断点调试,并且开发能力也会有一大提升,熟悉的报错类型越多,写代码的时候就会考虑得越周全,代码实现后报错的概率会大大降低!

    前面第2篇文章也给大家做了一些相关介绍,下面再整理一些Python插件开发中常见的一些报错信息剖析,给大家做一个参考!

一、Python插件调试技巧

     Python插件调试不建议太依赖断点调试,可以采用"弹出提示信息"的方法或者记录日志的方式来测试,建议使用如下方法来调试:

  • 通过界面弹出提示信息,仅界面类插件可用:this.View.ShowMessage("提示信息");

  • 主动抛出异常信息,所有插件类型都可以用:raise Exception("提示信息!");

  • 二分法快速定位报错位置:在代码中先结合报错提示信息,分析一下可能发生报错的代码块,然后从上往下分段测试,在分段的位置加入如上所说的提示信息,并且暂时让后续代码不允许,如果使用this.View.ShowMessage,需要用return来终止代码,如果用的是raise Exception,则不需要return来终止代码,因为主动抛出异常之后,如果没有Try-Catch的情况下,代码会自动中断。例如下图示例:

    image.png

  • 二开一个C#通用库来间接使用断点调试变量:如果具备C#开发基础,可以开发一个简单C#代码来作为调试辅助,通过Py插件中因为这个自定义的dll库,然后在Py插件中调用C#辅助类里面的某个方法,从而间接触发断点,例如下图示例代码:

    image.pngimage.png

  • 非字符串类型的变量,或者不能直接转换成字符串使用的变量调试:

    通过弹出提示的方式分析一些变量时,很多时候都是复杂的对象,例如:实体数据包、SQL查询结果集等等,可以通过序列化的方法转换成JSON字符串来弹出。

    引用Kingdee.BOS.Util后,调用JsonUtil.Serialize(变量),可以把对象转换成JSON字符串来弹出分析!

    有些对象是不能序列化的(有可能会出现递归死循环),或者序列化出来的内容不全,这个时候要结合具体的对象来具体读取里面的属性或者调用对象的方法取出想调试的内容来构造字符串。

  • 循环结构的调试

    对于有循环结构的代码,不要在循环里面直接使用ShowMessage,如果使用raise Exception则只会循环一次,可能都不会得到想要的调试结果,这个时候,可以通过字符串拼接的方式来构造调试内容(可以结合try-catch结构捕获未知异常),然后在循环完之后,一次性弹出,类似于日志记录。

    参考如下代码示例:

    image.png

二、编译报错:BOS中编辑完脚本点击确定后报错

  • Could not add reference to assembly Kingdee.BOS.App(或者其他)

        此错误意思是在应用服务器的bin目录找不到clr.AddReference(xxx)里面的dll组件库,这个是BOS平台的BUG,只要保证bin目录确实是有这个dll文件的,代码就能  正常运行,这个报错提示不用管就行。因为在BOSIDE上进行Python插件的注册,而这段Python代码最终是在服务端运行,BOSIDE属于客户端软件,BOSIDE所在目录下肯定不会包含服务端的组件,其提示缺少服务端的组件是正常的,而在应用服务器上是包含了所有的组件的,所以等真正到了运行时,此Python插件不会缺少任何平台组件,可以正常运行的。参考:python插件引入了kingdee.bos.app,但是保存会提示错误信息

      通常此报错可以忽略,可以检查一下cle.AddRef添加的dll引用是否写错,

      如果确实是引用了第三方或者自定义的外部dll库,那就要确认一下是否将dll拷贝到bin目录,并重启IIS

  • unexpected token '<newline>'

      不该出现换行的地方出现了换行,通常是需要打":"的地方没打,例如,if后面、方法定义后面等。

  • unexpected indent   

      缩进格式不对或者空格不对,通常是用空格敲了缩进,但是没对齐,或者复制了网页等地方的代码,格式混乱,重新用TAB键进行缩进,删除空格重新敲一下

  • unexpectedtoken ':'、unexpected token ';' …unexpected token ‘XXX'

      通常是误敲了中文冒号、分号、或者不该出现的其他内容等,也可能是由于其他原因(如括号没对齐等)导致该内容不能正常识别。

  • EOL while scanning single-quoted string

     字符串常量没有正确的结束,首尾双引号或者单引号缺失不成对,通常是多行字符串常量定义有误,多行字符串首尾用3个双引号【“””】引起来。

  • unindent does not match any outer indentation level

     缩进格式不对,缩进层级不对,通常是复制过来的代码,有多余的空格,或者该缩进的地方没缩进,检查缩进层级后重新用TAB键缩进!

  • expectedan indented block

    缩进问题,冒号后面缺少下级缩进内容,例如,for循环里面没有任何内容...等,或检查缩进是否有空格和TAB混用或者缩进明细不对的情况!

三、运行报错:Python插件执行过程中报错

  • ascii’ codec can‘t decode byte 0 in position 0: ordinal not in range (有时也报:没有可用于编码0 的数据)

    Py插件中使用str(XXX)时,如果XXX含有中文字符,则会报此错误!

    解决方法:使用format或者其他方式来转换字符串,不要使用str方法,str仅在确认不会出现中文字符的情况下使用!

  • attribute 'XXX' of 'namespace#' object is read-only

    直接调用命名空间.方法时会出现,通常是类名与命名空间重名了系统识别成了命名空间,而不是类

    解决方法单独添加类的引用,并给类加别名(例如: from System import Object as CObject CObject.XXX),或者用类的命名空间别名来调用(imort System as SYS SYS.Object.XXX)。

  • 值“Microsoft.Scripting.Interpreter.InterpretedFrameInfo”不是“System.String”类型,不能在此泛型集合中使用。

    代码中调用的类方法内部发生异常,系统无法解析,就会提示这个,通常是执行SQL报错,检查SQL即可,其他情况要针对具体的代码情况进行分析处理,通常也是调用某个类的方法时传入的参数有问题导致的!

    解决方法:结合具体代码情况分析,例如,执行SQL出错就测试SQL、调用下推出错,就手动下推测试等,可以参考Python插件动态调用C#源码的案例(配套课程中有讲解)来还原到C#执行,也能测试出报错!

    PT-151005 [8.2.0.20231109]版本以及以后,平台已经支持对Python的内部异常抛出详细的信息

  • global name ‘XXX' is not defined

    XXX”没有定义,通常是缺少相关的引用,或者类名、变量名写错了,注意大小写!

    解决方法:根据提示的内容定位报错位置,检查类名、变量名是否写添加类的引用!

  • No module named XXX

    引用的命名空间有错(from xxx 或者import AAA)存在“XXX",请检查命名空间引入写法是否有误,或者缺少添加对应类库引用(clr.AddRef)

    解决方法:通过clr.AddRe添加对应dll引用,检查引入的命名空间是否写错,注意大小写!

  • XXX is not Callable

    XXX 不是一个方法,不能直接调用,一般是出现"XXX()"这种写法,去掉括号试试

    解决方法:检查XXX是否是一个方法名,如果不是方法名,不能这样写!

  • index out of range: X / 索引超出范围。必须为非负值并小于集合大小。

    索引越界,X超出了最大索引值,通过索引值访问集合的元素时,传入的索引值超过了最大长度,或者format拼接字符串时,“{X}“中的X超过了format后面的参数个数!

    解决方法:找到索引越界的代码,分析为什么会出现越界,然后修改索引值,通过索引值访问集合时要做长度判断容错!

  • iteration over non-sequence of type TTT

    TTT类型的对象是一个非序列对象,或者不是一个集合,不能用来循环,通常是 for x in TTTObj TTTObj不是一个可循环的对象!

    解决方法:检查循环的地方,修改代码,不能用来循环的对象,不要写到循环结构里面!

  • expectedAAA, got BBB

    通常是调用C#库的方法时,传的参数类型不匹配,参数顺序和参数类型都要匹配,需要什么类型,就要传什么类型的参数

    解决方法:查找调用方法的地方,如有必要,可借助前面讲的C#插件工程来查看方法定义,以便查看所调方法的参数类型!

  • XXX() takes exactly 2 arguments (3 given)/takes at most 2 arguments (3 given)

    Py插件中使用str(XXX)时,如果XXX含有中文字符,则会报此错误!

    解决方法:使用format或者其他方式来转换字符串,不要使用str方法,str仅在确认不会出现中文字符的情况下使用!

  • 实体类型…中不存在名为XXX的属性

    通常是调用C#库的方法时,传的参数类型不匹配,参数顺序和参数类型都要匹配,需要什么类型,就要传什么类型的参数

    解决方法:查找调用方法的地方,如有必要,可借助前面讲的C#插件工程来查看方法定义,以便查看所调方法的参数类型!

  • ‘TTT' object has no attribute ‘XXX'

    TTT类型的对象,没有XXX这个可以访问的方法或者属性!

    解决方法:找到调用XXX的地方,检查类的方法名或者属性名是否写正确,注意大小写,也有可能是扩展方法的原因,py里面要还原成原始调用:扩展方法类.扩展方法(类对象,…)

  • 无法将透明代理强制转换为类型“Kingdee.BOS.App.Core.XXX”

     Py里面直接通过使用反射代理类获取接口服务类的实现对象时,会出现这个报错,Python插件里面目前不能直接这样调用!

    解决方法可以直接使用原始接口实现类的对象实例化,来获取类对象,反编译代理类从报错信息中分析具体实现类,并添加该类的相关引用!

                    例如:

                     viewService=ServiceHelper.GetService[IViewService]();#py通过此写法反射获取对象会报错
                     viewService=ServiceFactory.GetService[IViewService](this.Context);#py此写法也会报错
                     viewService=ViewService();#Py应按此方法改写

  • 就提示一个"XXX",报错堆栈里面有:IronPython.Runtime.PythonDictionary.GetItem(Object key)

    Py中通过key从Python的字典获取Value时候出错,如果传入的key不存在,通常就会出现这个报错

    解决方法:检查访问字典的地方,确认传入的key是否正确,通常从字典获取key对应的Value时,要做容错判断:if(DIC.has_key(key))

  • ‘TTT' object is unsubscriptable / 'NoneType' object is unsubscriptable

    TTT类型的对象,不能通过中括号来取值,NoneType表示通过中括号取值的对象为空。例如,Obj[XX],Obj不能通过中括号来取值,如果obj为空,则会报后面这个NoneType的错!

    解决方法:找到报错的地方,检查是否代码有误,或该对象是否为空了,通常中括号取值前都要有判空的意识(if(obj is not None))

  • NoneType …各种类型的报错…

    出现含有NoneType的报错时,是指出现了为空的对象,但是使用这个对象的地方又不能为空。

    解决方法:检查代码中可能出现空对象报错的地方,进行判空或者修改代码,可结合详细报错信息或二分法来定位报错位置

    

========================本篇正文结束=================

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


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

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