/pydbgr/lib/stack.py
http://pydbgr.googlecode.com/ · Python · 253 lines · 188 code · 23 blank · 42 comment · 47 complexity · 460fed0f349a62375650244b81311c5d MD5 · raw file
- # -*- coding: utf-8 -*-
- # Copyright (C) 2008, 2009, 2010 Rocky Bernstein <rocky@gnu.org>
- #
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
- """ Functions for working with Python frames"""
- import re, types
- from import_relative import import_relative
- Mbytecode = import_relative('bytecode', top_name='pydbgr')
- Mprint = import_relative('print', top_name='pydbgr')
- def count_frames(frame, count_start=0):
- "Return a count of the number of frames"
- count = -count_start
- while frame:
- count += 1
- frame = frame.f_back
- return count
- import repr as Mrepr
- import inspect
- _re_pseudo_file = re.compile(r'^<.+>')
- def format_stack_entry(dbg_obj, frame_lineno, lprefix=': ',
- include_location=True):
- """Format and return a stack entry gdb-style.
- Note: lprefix is not used. It is kept for compatibility.
- """
- frame, lineno = frame_lineno
- filename = frame2file(dbg_obj.core, frame)
- s = ''
- if frame.f_code.co_name:
- s = frame.f_code.co_name
- else:
- s = "<lambda>"
- pass
- args, varargs, varkw, local_vars = inspect.getargvalues(frame)
- if '<module>' == s and ([], None, None,) == (args, varargs, varkw,):
- is_module = True
- if is_exec_stmt(frame):
- s += ' exec()'
- else:
- fn_name = get_call_function_name(frame)
- if fn_name: s += ' %s()' % fn_name
- pass
- else:
- is_module = False
- parms=inspect.formatargvalues(args, varargs, varkw, local_vars)
- maxargstrsize = dbg_obj.settings['maxargstrsize']
- if len(parms) >= maxargstrsize:
- parms = "%s...)" % parms[0:maxargstrsize]
- pass
- s += parms
- pass
- # Note: ddd can't handle wrapped stack entries (yet).
- # The 35 is hoaky though. FIXME.
- if len(s) >= 35: s += "\n "
- if '__return__' in frame.f_locals:
- rv = frame.f_locals['__return__']
- s += '->'
- s += Mrepr.repr(rv)
- pass
- if include_location:
- is_pseudo_file = _re_pseudo_file.match(filename)
- add_quotes_around_file = not is_pseudo_file
- if is_module:
- if not is_exec_stmt(frame) and not is_pseudo_file:
- s += ' file'
- elif s == '?()':
- if is_exec_stmt(frame):
- s = 'in exec'
- # exec_str = get_exec_string(frame.f_back)
- # if exec_str != None:
- # filename = exec_str
- # add_quotes_around_file = False
- # pass
- # pass
- elif not is_pseudo_file:
- s = 'in file'
- pass
- pass
- elif not is_pseudo_file:
- s += ' called from file'
- pass
- if add_quotes_around_file: filename = "'%s'" % filename
- s += " %s at line %r" % (filename, lineno)
- return s
- def frame2file(core_obj, frame):
- return core_obj.filename(core_obj.canonic_filename(frame))
- def is_exec_stmt(frame):
- """Return True if we are looking at an exec statement"""
- return hasattr(frame, 'f_back') and frame.f_back is not None and \
- Mbytecode.op_at_frame(frame.f_back)=='EXEC_STMT'
- import dis
- def get_call_function_name(frame):
- """If f_back is looking at a call function, return
- the name for it. Otherwise return None"""
- f_back = frame.f_back
- if not f_back: return None
- if 'CALL_FUNCTION' != Mbytecode.op_at_frame(f_back): return None
- co = f_back.f_code
- code = co.co_code
- # labels = dis.findlabels(code)
- linestarts = dict(dis.findlinestarts(co))
- inst = f_back.f_lasti
- while inst >= 0:
- # c = code[inst]
- # op = ord(c)
- if inst in linestarts:
- inst += 1
- oparg = ord(code[inst]) + (ord(code[inst+1]) << 8)
- return co.co_names[oparg]
- inst -= 1
- pass
- return None
- def print_stack_entry(proc_obj, i_stack):
- frame_lineno = proc_obj.stack[len(proc_obj.stack)-i_stack-1]
- frame, lineno = frame_lineno
- if frame is proc_obj.curframe:
- proc_obj.intf[-1].msg_nocr('->')
- else:
- proc_obj.intf[-1].msg_nocr('##')
- proc_obj.intf[-1].msg("%d %s" %
- (i_stack, format_stack_entry(proc_obj.debugger, frame_lineno)))
- def print_stack_trace(proc_obj, count=None):
- "Print count entries of the stack trace"
- if count is None:
- n=len(proc_obj.stack)
- else:
- n=min(len(proc_obj.stack), count)
- try:
- for i in range(n):
- print_stack_entry(proc_obj, i)
- except KeyboardInterrupt:
- pass
- return
- def print_dict(s, obj, title):
- if hasattr(obj, "__dict__"):
- d=obj.__dict__
- if type(d) == types.DictType or type(d) == types.DictProxyType:
- keys = d.keys()
- if len(keys) == 0:
- s += "\n No %s" % title
- else:
- s += "\n %s:\n" % title
- keys.sort()
- for key in keys:
- s+=" '%s':\t%s\n" % (key, d[key])
- pass
- pass
- pass
- return s
- def eval_print_obj(arg, frame, format=None, short=False):
- """Return a string representation of an object """
- try:
- if not frame:
- # ?? Should we have set up a dummy globals
- # to have persistence?
- val = eval(arg, None, None)
- else:
- val = eval(arg, frame.f_globals, frame.f_locals)
- pass
- except:
- return 'No symbol "' + arg + '" in current context.'
- return print_obj(arg, val, format, short)
- def print_obj(arg, val, format=None, short=False):
- """Return a string representation of an object """
- what = arg
- if format:
- what = format + ' ' + arg
- val = Mprint.printf(val, format)
- pass
- s = '%s = %s' % (what, val)
- if not short:
- s += '\n type = %s' % type(val)
- # Try to list the members of a class.
- # Not sure if this is correct or the
- # best way to do.
- s = print_dict(s, val, "object variables")
- if hasattr(val, "__class__"):
- s = print_dict(s, val.__class__, "class variables")
- pass
- pass
- return s
- # Demo stuff above
- if __name__=='__main__':
- class MockDebuggerCore():
- def canonic_filename(self, frame):
- return frame.f_code.co_filename
- def filename(self, name):
- return name
- pass
- class MockDebugger():
- def __init__(self):
- self.core = MockDebuggerCore()
- self.settings = {
- 'maxargstrsize': 80
- }
- pass
- pass
-
- frame = inspect.currentframe()
- m = MockDebugger()
- print format_stack_entry(m, (frame, 10,))
- print "frame count: ", count_frames(frame)
- print "frame count: ", count_frames(frame.f_back)
- print "frame count: ", count_frames(frame, 1)
- print "def statement: x=5?: ", Mbytecode.is_def_stmt('x=5', frame)
- # Not a "def" statement because frame is wrong spot
- print Mbytecode.is_def_stmt('def foo():', frame)
- def sqr(x): x * x
- def fn(x):
- frame = inspect.currentframe()
- print get_call_function_name(frame)
- return
- print '=' * 30
- eval('fn(5)')
- print '=' * 30
- print print_obj('fn', fn)
- print '=' * 30
- print print_obj('len', len)
- print '=' * 30
- print print_obj('MockDebugger', MockDebugger)
- pass