使用 PyDev 进行调试 [size=1.166em]PyDev 是一个开源的的 plugin,它可以方便的和 Eclipse 集成,提供方便强大的调试功能。同时作为一个优秀的 Python IDE 还提供语法错误提示、源代码编辑助手、Quick Outline、Globals Browser、Hierarchy View、运行等强大功能。下面讲述如何将 PyDev 和 Eclipse 集成。在安装 PyDev 之前,需要先安装 Java 1.4 或更高版本、Eclipse 以及 Python。 第一步:启动 Eclipse,在 Eclipse 菜单栏中找到 Help 栏,选择 Help > Install New Software,并选择 Add button,添加 Ptdev 的下载站点 http://pydev.org/updates。选择 PyDev 之后完成余下的步骤便可以安装 PyDev。 图 7. 安装 PyDev[size=1.166em]安装完成之后需要配置 Python 解释器,在 Eclipse 菜单栏中,选择 Window > Preferences > Pydev > Interpreter – Python。Python 安装在 C:\Python27 路径下。单击 New,选择 Python 解释器 python.exe,打开后显示出一个包含很多复选框的窗口,选择需要加入系统 PYTHONPATH 的路径,单击 OK。 图 8. 配置 PyDev[size=1.166em]在配置完 Pydev 之后,可以通过在 Eclipse 菜单栏中,选择 File > New > Project > Pydev >Pydev Project,单击 Next 创建 Python 项目,下面的内容假设 python 项目已经创建,并且有个需要调试的脚本 remote.py(具体内容如下),它是一个登陆到远程机器上去执行一些命令的脚本,在运行的时候需要传入一些参数,下面将详细讲述如何在调试过程中传入参数 . 清单 11. Pydev 调试示例代码 #!/usr/bin/env python import os def telnetdo(HOST=None, USER=None, PASS=None, COMMAND=None): #define a function import telnetlib, sys if not HOST: try: HOST = sys.argv[1] USER = sys.argv[2] PASS = sys.argv[3] COMMAND = sys.argv[4] except: print "Usage: remote.py host user pass command" return tn = telnetlib.Telnet() # try: tn.open(HOST) except: print "Cannot open host" return tn.read_until("login:") tn.write(USER + '\n') if PASS: tn.read_until("Password:") tn.write(PASS + '\n') tn.write(COMMAND + '\n') tn.write("exit\n") tmp = tn.read_all() tn.close() del tn return tmp if __name__ == '__main__': print telnetdo()
[size=1.166em]在调试的时候有些情况需要传入一些参数,在调试之前需要进行相应的配置以便接收所需要的参数,选择需要调试的程序(本例 remote.py),该脚本在 debug 的过程中需要输入四个参数:host,user,password 以及命令。在 eclipse 的工程目录下选择需要 debug 的程序,单击右键,选择“Debug As”->“Debug Configurations”,在 Arguments Tab 页中选择“Variables”。如下 图 9 所示 . 图 9. 配置变量[size=1.166em]在窗口”Select Variable”之后选择“Edit Varuables” ,出现如下窗口,在下图中选择”New” 并在弹出的窗口中输入对应的变量名和值。特别需要注意的是在值的后面一定要有空格,不然所有的参数都会被当做第一个参数读入。 图 10. 添加具体变量[size=1.166em]按照以上方式依次配置完所有参数,然后在”select variable“窗口中安装参数所需要的顺序依次选择对应的变量。配置完成之后状态如下图 11 所示。 图 11. 完成配置[size=1.166em]选择 Debug 便可以开始程序的调试,调试方法与 eclipse 内置的调试功能的使用相似,并且支持多线程的 debug,这方面的文章已经有很多,读者可以自行搜索阅读,或者参考”使用 Eclipse 平台进行调试“一文。 使用日志功能达到调试的目的[size=1.166em]日志信息是软件开发过程中进行调试的一种非常有用的方式,特别是在大型软件开发过程需要很多相关人员进行协作的情况下。开发人员通过在代码中加入一些特定的能够记录软件运行过程中的各种事件信息能够有利于甄别代码中存在的问题。这些信息可能包括时间,描述信息以及错误或者异常发生时候的特定上下文信息。 最原始的 debug 方法是通过在代码中嵌入 print 语句,通过输出一些相关的信息来定位程序的问题。但这种方法有一定的缺陷,正常的程序输出和 debug 信息混合在一起,给分析带来一定困难,当程序调试结束不再需要 debug 输出的时候,通常没有很简单的方法将 print 的信息屏蔽掉或者定位到文件。python 中自带的 logging 模块可以比较方便的解决这些问题,它提供日志功能,将 logger 的 level 分为五个级别,可以通过 Logger.setLevel(lvl) 来设置。默认的级别为 warning。 表 2. 日志的级别Level使用情形
DEBUG 详细的信息,在追踪问题的时候使用
INFO 正常的信息
WARNING 一些不可预见的问题发生,或者将要发生,如磁盘空间低等,但不影响程序的运行
ERROR 由于某些严重的问题,程序中的一些功能受到影响
CRITICAL 严重的错误,或者程序本身不能够继续运行 [size=1.166em]logging lib 包含 4 个主要对象 - logger:logger 是程序信息输出的接口。它分散在不同的代码中使得程序可以在运行的时候记录相应的信息,并根据设置的日志级别或 filter 来决定哪些信息需要输出并将这些信息分发到其关联的 handler。常用的方法有 Logger.setLevel(),Logger.addHandler() ,Logger.removeHandler() ,Logger.addFilter() ,Logger.debug(), Logger.info(), Logger.warning(), Logger.error(),getLogger() 等。logger 支持层次继承关系,子 logger 的名称通常是父 logger.name 的方式。如果不创建 logger 的实例,则使用默认的 root logger,通过 logging.getLogger() 或者 logging.getLogger("") 得到 root logger 实例。
- Handler:Handler 用来处理信息的输出,可以将信息输出到控制台,文件或者网络。可以通过 Logger.addHandler() 来给 logger 对象添加 handler,常用的 handler 有 StreamHandler 和 FileHandler 类。StreamHandler 发送错误信息到流,而 FileHandler 类用于向文件输出日志信息,这两个 handler 定义在 logging 的核心模块中。其他的 hander 定义在 logging.handles 模块中,如 HTTPHandler,SocketHandler。
- Formatter:Formatter 则决定了 log 信息的格式 , 格式使用类似于 %(< dictionary key >)s 的形式来定义,如'%(asctime)s - %(levelname)s - %(message)s',支持的 key 可以在 python 自带的文档 LogRecord attributes 中查看。
- Filter:Filter 用来决定哪些信息需要输出。可以被 handler 和 logger 使用,支持层次关系,比如如果设置了 filter 为名称为 A.B 的 logger,则该 logger 和其子 logger 的信息会被输出,如 A.B,A.B.C.
清单 12. 日志使用示例import logging LOG1=logging.getLogger('b.c') LOG2=logging.getLogger('d.e') filehandler = logging.FileHandler('test.log','a') formatter = logging.Formatter('%(name)s %(asctime)s %(levelname)s %(message)s') filehandler.setFormatter(formatter) filter=logging.Filter('b') filehandler.addFilter(filter) LOG1.addHandler(filehandler) LOG2.addHandler(filehandler) LOG1.setLevel(logging.INFO) LOG2.setLevel(logging.DEBUG) LOG1.debug('it is a debug info for log1') LOG1.info('normal infor for log1') LOG1.warning('warning info for log1:b.c') LOG1.error('error info for log1:abcd') LOG1.critical('critical info for log1:not worked') LOG2.debug('debug info for log2') LOG2.info('normal info for log2') LOG2.warning('warning info for log2') LOG2.error('error:b.c') LOG2.critical('critical')
[size=1.166em]上例设置了 filter b,则 b.c 为 b 的子 logger,因此满足过滤条件该 logger 相关的日志信息会 被输出,而其他不满足条件的 logger(这里是 d.e)会被过滤掉。 清单 13. 输出结果b.c 2011-11-25 11:07:29,733 INFO normal infor for log1 b.c 2011-11-25 11:07:29,733 WARNING warning info for log1:b.c b.c 2011-11-25 11:07:29,733 ERROR error info for log1:abcd b.c 2011-11-25 11:07:29,733 CRITICAL critical info for log1:not worked
[size=1.166em]logging 的使用非常简单,同时它是线程安全的,下面结合多线程的例子讲述如何使用 logging 进行 debug。 清单 14. 多线程使用 logginglogging.conf [loggers] keys=root,simpleExample [handlers] keys=consoleHandler [formatters] keys=simpleFormatter [logger_root] level=DEBUG handlers=consoleHandler [logger_simpleExample] level=DEBUG handlers=consoleHandler qualname=simpleExample propagate=0 [handler_consoleHandler] class=StreamHandler level=DEBUG formatter=simpleFormatter args=(sys.stdout,) [formatter_simpleFormatter] format=%(asctime)s - %(name)s - %(levelname)s - %(message)s datefmt= code example: #!/usr/bin/python import thread import time import logging import logging.config logging.config.fileConfig('logging.conf') # create logger logger = logging.getLogger('simpleExample') # Define a function for the thread def print_time( threadName, delay): logger.debug('thread 1 call print_time function body') count = 0 logger.debug('count:%s',count)
总结[size=1.166em]全文介绍了 python 中 debug 的几种不同的方式,包括 pdb 模块、利用 PyDev 和 Eclipse 集成进行调试、PyCharm 以及 Debug 日志进行调试,希望能给相关 python 使用者一点参考。更多关于 python debugger 的资料可以参见参考资料。
|