Python调试器的设计思路

有人询问Python调试器的设计原理,这里简单说明一下。

Python本身带了一个pdb调试库,但并没有调试界面。

我之前曾经写过一个pdb的派生类,用于在Script.NET集成开发环境中集成python调试器,代码如下。整体思路是:
pdb类中已经定义了几个用于派生的成员函数,例如user_call是调用一个函数时候会被调用的函数,user_line是执行每一行之前都会被调
用的函数,user_return是函数返回时候会被调用的函数,user_exception是发生异常时候会被调用的函数。
只要重载上面需要的函数做自己的处理就可以,例如重载user_line函数,在此函数中判断是否在一个断点上,或者是否是单步调试状态,如果需要停下
来,就通过一个消息循环使当前运行的脚本停下来等候用户输入,同时在某个界面窗口中显示变量等信息。

#################################################################
# Script.NET Python support script
# Copyright Blueant Software.
# Author      : blueant
# Create Date : 2002-06-11
# This script is platform python debugger support library.
#################################################################

"""Platform python debugger support library.
"""

import sys
import bdb
import _platform
from types import *
from TclInterp import *

# get sting format value of variable
def stringify(thing):
    try:
        typ = type(thing)
        if hasattr(thing, '__name__'):
            return thing.__name__
            #if hasattr(typ, '__name__'):
            #    return thing.__name__ + ' (' + typ.__name__ + ')'
            #else:
            #    return thing.__name__ + ' (' + str(typ) + ')'
        else:
            return repr(thing)
    except:
        return "Looking at object failed - %s: %s" % (sys.exc_info()
[0], sys.exc_info()[1])

def ismultitem(thing):
    typ = type(thing)
    return 0
#    if (typ == ListType || typ == TupleType || typ == DictType):
#        return 1
#    elif (typ == ClassType || typ == ModuleType || typ ==
InstanceType):
#        return 2
#    else:
#        return 0

class Xdb(bdb.Bdb):
    def __init__(self):
        bdb.Bdb.__init__(self)
        self.reset()
        self.forget()
        self.curframe = None
        self.init_vars_window()

    def forget(self):
        self.lineno = None
        self.stack = []
        self.curindex = 0
        self.curframe = None

    def setup(self, f, t):
        self.forget()
        self.stack, self.curindex = self.get_stack(f, t)
        self.curframe = self.stack[self.curindex][0]

    def user_call(self, frame, args):
        return
        #if not self.stop_here(frame): return
        #name = frame.f_code.co_name
        #if not name: name = '???'
        #_platform.pydbg(_platform.interpid, 0,
frame.f_code.co_filename, name, frame.f_lineno, '')

    def user_line(self, frame):
        if frame.f_lineno == 0:
            return

        self.setup(frame, None)
        #self.refresh_debuginfo()
        name = frame.f_code.co_name
        if not name: name = ''
        _platform.pydbg(_platform.interpid, 1,
frame.f_code.co_filename, name, frame.f_lineno, '')
        self.forget()

    def user_return(self, frame, retval):
        return
        #_platform.pydbg(_platform.interpid, 2,
frame.f_code.co_filename, frame.f_code.co_name, frame.f_lineno, '')

    def user_exception(self, frame, exc_stuff):
        #_platform.pydbg(_platform.interpid, 3,
frame.f_code.co_filename, frame.f_code.co_name, frame.f_lineno+1,
exc_stuff)
        print '+++ exception', exc_stuff
        self.set_continue()

    # set/clear breakpoint
    def bp_set(self, filename, lineno, set):
        if set :
            self.set_break(filename, lineno)
        else:
            self.clear_break(filename, lineno)

    def init_vars_window(self):
        # local vars
        self.window_locals = xpOutputWindow('局部', 0, 2, 1)
        self.window_locals.ModifyStyle('', 'SINGLESELECT')
        self.window_locals.SetFont('FixedSys')
        self.window_locals.DeleteRow(-1)
        self.window_locals.DelColumn(-1)
        self.window_locals.AddColumn('变量名', 120, 'left')
        self.window_locals.AddColumn('值', 120, 'left')
        self.window_locals.AddColumn('类型', 80, 'left')
        #self.window_locals.ReadProfile('OwmEdit//Debug', 'VarWatch')
        # global vars
        self.window_globals = xpOutputWindow('全局', 0, 2, 1)
        self.window_globals.ModifyStyle('', 'SINGLESELECT')
        self.window_globals.SetFont('FixedSys')
        self.window_globals.DeleteRow(-1)
        self.window_globals.DelColumn(-1)
        self.window_globals.AddColumn('变量名', 120, 'left')
        self.window_globals.AddColumn('值', 120, 'left')
        self.window_globals.AddColumn('类型', 80, 'left')
        #self.window_globals.ReadProfile('OwmEdit//Debug', 'VarWatch')

    def init_stack_window(self):
        self.window_stack = xpOutputWindow('堆栈', 0, 1, 1)
        self.window_stack.ModifyStyle('', 'SINGLESELECT')
        self.window_stack.SetRowColors('0,0,255', '255,255,225')
        self.window_stack.DeleteRow(-1)
        self.window_stack.DelColumn(-1)
        self.window_stack.AddColumn('函数', 120, 'left')
        self.window_stack.AddColumn('行', 50, 'left')
        self.window_stack.AddColumn('文件', 150, 'left')
        self.window_stack.AutoSizeColumn(2)

    # initial var windows' double click script
    def init_vars_window_script(self):
        # local var window
        script = """
                        set item        [output -GetInfo "局部" SelItem];
                        set name        [output -GetInfo "局部" <$item,0>];
                        set value       [output -GetInfo "局部" <$item,1>];
                        set type        [output -GetInfo "局部" <$item,2>];
                        MessageBox -MBOK -MB_ICONINFORMATION -title "查看变量" /
                                "变量名:$name /n变量值:$value /n变量类型:$type";
                        unset item name value type;
                        """;
        self.window_locals.AppendRowScript(script);
        # global var window
        script = """
                        set item        [output -GetInfo "全局" SelItem];
                        set name        [output -GetInfo "全局" <$item,0>];
                        set value       [output -GetInfo "全局" <$item,1>];
                        set type        [output -GetInfo "全局" <$item,2>];
                        MessageBox -MBOK -MB_ICONINFORMATION -title "查看变量" /
                                "变量名:$name /n变量值:$value /n变量类型:$type";
                        unset item name value type;
                        """;
        self.window_globals.AppendRowScript(script);

    # refresh debug info
    def refresh_debuginfo(self):
        #self.init_stack_window()
        #self.init_vars_window()
        self.refresh_stack()
        self.refresh_vars(self.curframe)

    def refresh_vars(self, frame):
        self.init_vars_window()
        self.window_locals.DeleteRow(-1)
        self.window_globals.DeleteRow(-1)
        self.init_vars_window_script()
        # local vars
        row = 0
        for key in frame.f_locals:
            if not (key[:2] == key[-2:] == '__'):
                self.window_locals.AppendRow(key)
                self.window_locals.SetItemText(row, 1,
stringify(frame.f_locals[key]))
                if ismultitem(frame.f_locals[key]):
                    self.window_locals.SetTextColor(row, 1, 5)
                self.window_locals.SetItemText(row, 2,
type(frame.f_locals[key]).__name__)
                self.window_locals.SetRowScript(row, 1);
                row += 1
        # global vars
        row = 0
        for key in frame.f_globals:
            if not (key[:2] == key[-2:] == '__'):
                self.window_globals.AppendRow(key)
                self.window_globals.SetItemText(row, 1,
stringify(frame.f_globals[key]))
                if ismultitem(frame.f_globals[key]):
                    self.window_globals.SetTextColor(row, 1, 5)
                self.window_globals.SetItemText(row, 2,
type(frame.f_globals[key]).__name__)
                self.window_globals.SetRowScript(row, 1);
                row += 1

    def refresh_stack(self):
        self.init_stack_window()
        self.window_stack.DeleteRow(-1)
        row = 0
        for f, lineno in self.stack:
            self.window_stack.AppendRow(f.f_code.co_name, 21)
            self.window_stack.SetItemText(row, 1, lineno)
            self.window_stack.SetItemText(row, 2,
f.f_code.co_filename)
            row += 1
        self.window_stack.AutoSizeColumn(2)

if __name__=='__main__':
        x = Xdb()
        filename = 'e://work//platform//samples//python//TestTclCmd.py'
        x.set_break(filename, 15)
        print x.run('execfile(' + `filename` + ')')

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值