class FrameViewer: def __init__(self, root, frame): self.root = root self.frame = frame self.top = Toplevel(self.root) self.repr = Repr() self.repr.maxstring = 60 self.load_variables() def load_variables(self): row = 0 if self.frame.f_locals is not self.frame.f_globals: l = Label(self.top, text="Local Variables", borderwidth=2, relief="raised") l.grid(row=row, column=0, columnspan=2, sticky="ew") row = self.load_names(self.frame.f_locals, row+1) l = Label(self.top, text="Global Variables", borderwidth=2, relief="raised") l.grid(row=row, column=0, columnspan=2, sticky="ew") row = self.load_names(self.frame.f_globals, row+1) def load_names(self, dict, row): names = dict.keys() names.sort() for name in names: value = dict[name] svalue = self.repr.repr(value) l = Label(self.top, text=name) l.grid(row=row, column=0, sticky="w") l = Entry(self.top, width=60, borderwidth=0) l.insert(0, svalue) l.grid(row=row, column=1, sticky="w") row = row+1 return row
def test_tuple(self): eq = self.assertEquals eq(r((1, )), "(1,)") t3 = (1, 2, 3) eq(r(t3), "(1, 2, 3)") r2 = Repr() r2.maxtuple = 2 expected = repr(t3)[:-2] + "...)" eq(r2.repr(t3), expected)
def test_tuple(self): eq = self.assertEquals eq(r((1,)), "(1,)") t3 = (1, 2, 3) eq(r(t3), "(1, 2, 3)") r2 = Repr() r2.maxtuple = 2 expected = repr(t3)[:-2] + "...)" eq(r2.repr(t3), expected)
class FrameViewer: def __init__(self, root, frame): self.root = root self.frame = frame self.top = Toplevel(self.root) self.repr = Repr() self.repr.maxstring = 60 self.load_variables() def load_variables(self): row = 0 if self.frame.f_locals is not self.frame.f_globals: l = Label(self.top, text="Local Variables", borderwidth=2, relief="raised") l.grid(row=row, column=0, columnspan=2, sticky="ew") row = self.load_names(self.frame.f_locals, row + 1) l = Label(self.top, text="Global Variables", borderwidth=2, relief="raised") l.grid(row=row, column=0, columnspan=2, sticky="ew") row = self.load_names(self.frame.f_globals, row + 1) def load_names(self, dict, row): names = dict.keys() names.sort() for name in names: value = dict[name] svalue = self.repr.repr(value) l = Label(self.top, text=name) l.grid(row=row, column=0, sticky="w") l = Entry(self.top, width=60, borderwidth=0) l.insert(0, svalue) l.grid(row=row, column=1, sticky="w") row = row + 1 return row
class CommandProcessor(Mprocessor.Processor): def __init__(self, core_obj, opts=None): get_option = lambda key: \ Mmisc.option_set(opts, key, DEFAULT_PROC_OPTS) Mprocessor.Processor.__init__(self, core_obj) self.continue_running = False # True if we should leave command loop self.event2short = dict(EVENT2SHORT) self.event2short['signal'] = '?!' self.event2short['brkpt'] = 'xx' self.optional_modules = ('ipython', 'bpy', 'deparse') self.optional_modules = ('ipython', 'bpy') self.cmd_instances = self._populate_commands() # command argument string. Is like current_command, but the part # after cmd_name has been removed. self.cmd_argstr = '' # command name before alias or macro resolution self.cmd_name = '' self.cmd_queue = [] # Queued debugger commands self.completer = lambda text, state: \ Mcomplete.completer(self, text, state) self.current_command = '' # Current command getting run self.debug_nest = 1 self.display_mgr = Mdisplay.DisplayMgr() self.intf = core_obj.debugger.intf self.last_command = None # Initially a no-op self.precmd_hooks = [] self.location = lambda : print_location(self) self.preloop_hooks = [] self.postcmd_hooks = [] self._populate_cmd_lists() # Note: prompt_str's value set below isn't used. It is # computed dynamically. The value is suggestive of what it # looks like. self.prompt_str = '(trepan2) ' # Stop only if line/file is different from last time self.different_line = None # These values updated on entry. Set initial values. self.curframe = None self.event = None self.event_arg = None self.frame = None self.list_lineno = 0 # last list number used in "list" self.list_filename = None # last filename used in list self.macros = {} # Debugger Macros # Create a custom safe Repr instance and increase its maxstring. # The default of 30 truncates error messages too easily. self._repr = Repr() self._repr.maxstring = 100 self._repr.maxother = 60 self._repr.maxset = 10 self._repr.maxfrozen = 10 self._repr.array = 10 self.stack = [] self.thread_name = None self.frame_thread_name = None initfile_list = get_option('initfile_list') for init_cmdfile in initfile_list: self.queue_startfile(init_cmdfile) return def _saferepr(self, str, maxwidth=None): if maxwidth is None: maxwidth = self.debugger.settings['width'] return self._repr.repr(str)[:maxwidth] def add_preloop_hook(self, hook, position=-1, nodups = True): if hook in self.preloop_hooks: return False self.preloop_hooks.insert(position, hook) return True # To be overridden in derived debuggers def defaultFile(self): """Produce a reasonable default.""" filename = self.curframe.f_code.co_filename # Consider using is_exec_stmt(). I just don't understand # the conditions under which the below test is true. if filename == '<string>' and self.debugger.mainpyfile: filename = self.debugger.mainpyfile pass return filename def set_prompt(self, prompt='trepan2'): if self.thread_name and self.thread_name != 'MainThread': prompt += ':' + self.thread_name pass self.prompt_str = '%s%s%s' % ('(' * self.debug_nest, prompt, ')' * self.debug_nest) highlight = self.debugger.settings['highlight'] if highlight and highlight in ('light', 'dark'): self.prompt_str = colorize('underline', self.prompt_str) self.prompt_str += ' ' def event_processor(self, frame, event, event_arg, prompt='trepan2'): 'command event processor: reading a commands do something with them.' self.frame = frame self.event = event self.event_arg = event_arg filename = frame.f_code.co_filename lineno = frame.f_lineno if sys.version_info[0] == 2 and sys.version_info[1] <= 4: line = None else: line = linecache.getline(filename, lineno, frame.f_globals) pass if not line: opts = {'output': 'plain', 'reload_on_change': self.settings('reload'), 'strip_nl': False} line = pyficache.getline(filename, lineno, opts) self.current_source_text = line if self.settings('skip') is not None: if Mbytecode.is_def_stmt(line, frame): return True if Mbytecode.is_class_def(line, frame): return True pass self.thread_name = Mthread.current_thread_name() self.frame_thread_name = self.thread_name self.set_prompt(prompt) self.process_commands() if filename == '<string>': pyficache.remove_remap_file('<string>') return True def forget(self): """ Remove memory of state variables set in the command processor """ self.stack = [] self.curindex = 0 self.curframe = None self.thread_name = None self.frame_thread_name = None return def eval(self, arg): """Eval string arg in the current frame context.""" try: return eval(arg, self.curframe.f_globals, self.curframe.f_locals) except: t, v = sys.exc_info()[:2] if isinstance(t, str): exc_type_name = t pass else: exc_type_name = t.__name__ self.errmsg(str("%s: %s" % (exc_type_name, arg))) raise return None # Not reached def exec_line(self, line): if self.curframe: local_vars = self.curframe.f_locals global_vars = self.curframe.f_globals else: local_vars = None # FIXME: should probably have place where the # user can store variables inside the debug session. # The setup for this should be elsewhere. Possibly # in interaction. global_vars = None try: code = compile(line + '\n', '"%s"' % line, 'single') exec(code, global_vars, local_vars) except: t, v = sys.exc_info()[:2] if type(t) == str: exc_type_name = t else: exc_type_name = t.__name__ self.errmsg('%s: %s' % (str(exc_type_name), str(v))) pass return def parse_position(self, arg, old_mod=None): """parse_position(self, arg)->(fn, name, lineno) Parse arg as [filename:]lineno | function | module Make sure it works for C:\foo\bar.py:12 """ colon = arg.rfind(':') if colon >= 0: # First handle part before the colon arg1 = arg[:colon].rstrip() lineno_str = arg[colon+1:].lstrip() (mf, filename, lineno) = self.parse_position_one_arg(arg1, old_mod, False) if filename is None: filename = self.core.canonic(arg1) # Next handle part after the colon val = self.get_an_int(lineno_str, "Bad line number: %s" % lineno_str) if val is not None: lineno = val else: (mf, filename, lineno) = self.parse_position_one_arg(arg, old_mod) pass return mf, filename, lineno def parse_position_one_arg(self, arg, old_mod=None, show_errmsg=True): """parse_position_one_arg(self, arg, show_errmsg) -> (module/function, file, lineno) See if arg is a line number, function name, or module name. Return what we've found. None can be returned as a value in the triple. """ modfunc, filename, lineno = (None, None, None) if self.curframe: g = self.curframe.f_globals l = self.curframe.f_locals else: g = globals() l = locals() pass try: # First see if argument is an integer lineno = int(eval(arg, g, l)) if old_mod is None: filename = self.curframe.f_code.co_filename pass except: try: modfunc = eval(arg, g, l) except: modfunc = arg pass msg = ('Object %s is not known yet as a function, module, ' 'or is not found along sys.path, ' 'and not a line number.') % str(repr(arg)) try: # See if argument is a module or function if inspect.isfunction(modfunc): pass elif inspect.ismodule(modfunc): filename = Mfile.file_pyc2py(modfunc.__file__) filename = self.core.canonic(filename) return(modfunc, filename, None) elif hasattr(modfunc, 'im_func'): modfunc = modfunc.im_func pass else: if show_errmsg: self.errmsg(msg) return(None, None, None) code = modfunc.func_code lineno = code.co_firstlineno filename = code.co_filename except: if show_errmsg: self.errmsg(msg) return (None, None, None) pass return (modfunc, self.core.canonic(filename), lineno) def get_an_int(self, arg, msg_on_error, min_value=None, max_value=None): """Like cmdfns.get_an_int(), but if there's a stack frame use that in evaluation.""" ret_value = self.get_int_noerr(arg) if ret_value is None: if msg_on_error: self.errmsg(msg_on_error) else: self.errmsg('Expecting an integer, got: %s.' % str(arg)) pass return None if min_value and ret_value < min_value: self.errmsg('Expecting integer value to be at least %d, got: %d.' % (min_value, ret_value)) return None elif max_value and ret_value > max_value: self.errmsg('Expecting integer value to be at most %d, got: %d.' % (max_value, ret_value)) return None return ret_value def get_int_noerr(self, arg): """Eval arg and it is an integer return the value. Otherwise return None""" if self.curframe: g = self.curframe.f_globals l = self.curframe.f_locals else: g = globals() l = locals() pass try: val = int(eval(arg, g, l)) except (SyntaxError, NameError, ValueError, TypeError): return None return val def get_int(self, arg, min_value=0, default=1, cmdname=None, at_most=None): """If no argument use the default. If arg is a an integer between least min_value and at_most, use that. Otherwise report an error. If there's a stack frame use that in evaluation.""" if arg is None: return default default = self.get_int_noerr(arg) if default is None: if cmdname: self.errmsg(("Command '%s' expects an integer; " + "got: %s.") % (cmdname, str(arg))) else: self.errmsg('Expecting a positive integer, got: %s' % str(arg)) pass return None pass if default < min_value: if cmdname: self.errmsg(("Command '%s' expects an integer at least" + ' %d; got: %d.') % (cmdname, min_value, default)) else: self.errmsg(("Expecting a positive integer at least" + ' %d; got: %d') % (min_value, default)) pass return None elif at_most and default > at_most: if cmdname: self.errmsg(("Command '%s' expects an integer at most" + ' %d; got: %d.') % (cmdname, at_most, default)) else: self.errmsg(("Expecting an integer at most %d; got: %d") % (at_most, default)) pass pass return default def getval(self, arg): try: return eval(arg, self.curframe.f_globals, self.curframe.f_locals) except: t, v = sys.exc_info()[:2] if isinstance(t, str): exc_type_name = t else: exc_type_name = t.__name__ self.errmsg(str("%s: %s" % (exc_type_name, arg))) raise return def ok_for_running(self, cmd_obj, name, nargs): '''We separate some of the common debugger command checks here: whether it makes sense to run the command in this execution state, if the command has the right number of arguments and so on. ''' if hasattr(cmd_obj, 'execution_set'): if not (self.core.execution_status in cmd_obj.execution_set): part1 = ("Command '%s' is not available for execution status:" % name) mess = Mmisc.wrapped_lines(part1, self.core.execution_status, self.debugger.settings['width']) self.errmsg(mess) return False pass if self.frame is None and cmd_obj.need_stack: self.intf[-1].errmsg("Command '%s' needs an execution stack." % name) return False if nargs < cmd_obj.min_args: self.errmsg(("Command '%s' needs at least %d argument(s); " + "got %d.") % (name, cmd_obj.min_args, nargs)) return False elif cmd_obj.max_args is not None and nargs > cmd_obj.max_args: self.errmsg(("Command '%s' can take at most %d argument(s);" + " got %d.") % (name, cmd_obj.max_args, nargs)) return False return True def process_commands(self): """Handle debugger commands.""" if self.core.execution_status != 'No program': self.setup() self.location() pass leave_loop = run_hooks(self, self.preloop_hooks) self.continue_running = False while not leave_loop: try: run_hooks(self, self.precmd_hooks) # bdb had a True return to leave loop. # A more straight-forward way is to set # instance variable self.continue_running. leave_loop = self.process_command() if leave_loop or self.continue_running: break except EOFError: # If we have stacked interfaces, pop to the next # one. If this is the last one however, we'll # just stick with that. FIXME: Possibly we should # check to see if we are interactive. and not # leave if that's the case. Is this the right # thing? investigate and fix. if len(self.debugger.intf) > 1: del self.debugger.intf[-1] self.last_command = '' else: if self.debugger.intf[-1].output: self.debugger.intf[-1].output.writeline('Leaving') raise Mexcept.DebuggerQuit pass break pass pass return run_hooks(self, self.postcmd_hooks) def process_command(self): # process command if len(self.cmd_queue) > 0: current_command = self.cmd_queue[0].strip() del self.cmd_queue[0] else: current_command = ( self.intf[-1].read_command(self.prompt_str).strip()) if '' == current_command and self.intf[-1].interactive: current_command = self.last_command pass pass # Look for comments if '' == current_command: if self.intf[-1].interactive: self.errmsg("No previous command registered, " + "so this is a no-op.") pass return False if current_command is None or current_command[0] == '#': return False try: args_list = arg_split(current_command) except: self.errmsg("bad parse %s"< sys.exc_info()[0]) return False for args in args_list: if len(args): while True: if len(args) == 0: return False macro_cmd_name = args[0] if macro_cmd_name not in self.macros: break try: current_command = \ self.macros[macro_cmd_name][0](*args[1:]) except TypeError: t, v = sys.exc_info()[:2] self.errmsg("Error expanding macro %s" % macro_cmd_name) return False if self.settings('debugmacro'): print(current_command) pass if isinstance(types.ListType, type(current_command)): for x in current_command: if bytes != type(x): self.errmsg(("macro %s should return a List " + "of Strings. Has %s of type %s") % (macro_cmd_name, x, repr(current_command), type(x))) return False pass first = current_command[0] args = first.split() self.cmd_queue + [current_command[1:]] current_command = first elif type(current_command) == str: args = current_command.split() else: self.errmsg(("macro %s should return a List " + "of Strings or a String. Got %s") % (macro_cmd_name, repr(current_command))) return False pass self.cmd_name = args[0] cmd_name = resolve_name(self, self.cmd_name) self.cmd_argstr = current_command[len(self.cmd_name):].lstrip() if cmd_name: self.last_command = current_command cmd_obj = self.commands[cmd_name] if self.ok_for_running(cmd_obj, cmd_name, len(args)-1): try: self.current_command = current_command result = cmd_obj.run(args) if result: return result except (Mexcept.DebuggerQuit, Mexcept.DebuggerRestart, SystemExit): # Let these exceptions propagate through raise except: self.errmsg("INTERNAL ERROR: " + traceback.format_exc()) pass pass pass elif not self.settings('autoeval'): self.undefined_cmd(current_command) else: self.exec_line(current_command) pass pass pass return False def remove_preloop_hook(self, hook): try: position = self.preloop_hooks.index(hook) except ValueError: return False del self.preloop_hooks[position] return True def setup(self): """Initialization done before entering the debugger-command loop. In particular we set up the call stack used for local variable lookup and frame/up/down commands. We return True if we should NOT enter the debugger-command loop.""" self.forget() if self.settings('dbg_trepan'): self.frame = inspect.currentframe() pass if self.event in ['exception', 'c_exception']: exc_type, exc_value, exc_traceback = self.event_arg else: _, _, exc_traceback = (None, None, None,) # NOQA pass if self.frame or exc_traceback: self.stack, self.curindex = \ get_stack(self.frame, exc_traceback, None, self) self.curframe = self.stack[self.curindex][0] self.thread_name = Mthread.current_thread_name() if exc_traceback: self.list_lineno = traceback.extract_tb(exc_traceback, 1)[0][1] else: self.stack = self.curframe = \ self.botframe = None pass if self.curframe: self.list_lineno = \ max(1, inspect.getlineno(self.curframe) - int(self.settings('listsize') / 2)) - 1 self.list_filename = self.curframe.f_code.co_filename else: if not exc_traceback: self.list_lineno = None pass # if self.execRcLines()==1: return True return False def queue_startfile(self, cmdfile): '''Arrange for file of debugger commands to get read in the process-command loop.''' expanded_cmdfile = os.path.expanduser(cmdfile) is_readable = Mfile.readable(expanded_cmdfile) if is_readable: self.cmd_queue.append('source ' + expanded_cmdfile) elif is_readable is None: self.errmsg("source file '%s' doesn't exist" % expanded_cmdfile) else: self.errmsg("source file '%s' is not readable" % expanded_cmdfile) pass return def undefined_cmd(self, cmd): """Error message when a command doesn't exist""" self.errmsg('Undefined command: "%s". Try "help".' % cmd) return def read_history_file(self): """Read the command history file -- possibly.""" histfile = self.debugger.intf[-1].histfile try: import readline readline.read_history_file(histfile) except IOError: pass except ImportError: pass return def write_history_file(self): """Write the command history file -- possibly.""" settings = self.debugger.settings histfile = self.debugger.intf[-1].histfile if settings['hist_save']: try: import readline try: readline.write_history_file(histfile) except IOError: pass except ImportError: pass pass return def _populate_commands(self): """ Create an instance of each of the debugger commands. Commands are found by importing files in the directory 'command'. Some files are excluded via an array set in __init__. For each of the remaining files, we import them and scan for class names inside those files and for each class name, we will create an instance of that class. The set of DebuggerCommand class instances form set of possible debugger commands.""" import trepan.processor.command as Mcommand if hasattr(Mcommand, '__modules__'): return self.populate_commands_easy_install(Mcommand) else: return self.populate_commands_pip(Mcommand) def populate_commands_pip(self, Mcommand): cmd_instances = [] eval_cmd_template = 'command_mod.%s(self)' for mod_name in Mcommand.__dict__.keys(): if mod_name.startswith('__'): continue import_name = "trepan.processor.command." + mod_name imp = __import__(import_name) if imp.__name__ == 'trepan': command_mod = imp.processor.command else: if mod_name in ('info_sub', 'set_sub', 'show_sub',): pass try: command_mod = getattr(__import__(import_name), mod_name) except: # Don't need to warn about optional modules if mod_name not in self.optional_modules: print('Error importing %s: %s' % (mod_name, sys.exc_info()[0])) pass continue pass classnames = [ tup[0] for tup in inspect.getmembers(command_mod, inspect.isclass) if ('DebuggerCommand' != tup[0] and tup[0].endswith('Command')) ] for classname in classnames: eval_cmd = eval_cmd_template % classname try: instance = eval(eval_cmd) cmd_instances.append(instance) except ImportError: pass except: print('Error loading %s from %s: %s' % (classname, mod_name, sys.exc_info()[0])) pass pass pass return cmd_instances def populate_commands_easy_install(self, Mcommand): cmd_instances = [] for mod_name in Mcommand.__modules__: if mod_name in ('info_sub', 'set_sub', 'show_sub',): pass import_name = 'trepan.processor.command.' + mod_name try: command_mod = __import__(import_name, None, None, ['*']) except: if mod_name not in self.optional_modules: print('Error importing %s: %s' % (mod_name, sys.exc_info()[0])) pass continue classnames = [ tup[0] for tup in inspect.getmembers(command_mod, inspect.isclass) if ('DebuggerCommand' != tup[0] and tup[0].endswith('Command')) ] for classname in classnames: if False: instance = getattr(command_mod, classname)(self) cmd_instances.append(instance) else: try: instance = getattr(command_mod, classname)(self) cmd_instances.append(instance) except: print('Error loading %s from %s: %s' % (classname, mod_name, sys.exc_info()[0])) pass pass pass pass return cmd_instances def _populate_cmd_lists(self): """ Populate self.lists and hashes: self.commands, and self.aliases, self.category """ self.commands = {} self.aliases = {} self.category = {} # self.short_help = {} for cmd_instance in self.cmd_instances: if not hasattr(cmd_instance, 'aliases'): continue alias_names = cmd_instance.aliases cmd_name = cmd_instance.name self.commands[cmd_name] = cmd_instance for alias_name in alias_names: self.aliases[alias_name] = cmd_name pass cat = getattr(cmd_instance, 'category') if cat and self.category.get(cat): self.category[cat].append(cmd_name) else: self.category[cat] = [cmd_name] pass # sh = getattr(cmd_instance, 'short_help') # if sh: # self.short_help[cmd_name] = getattr(c, 'short_help') # pass pass for k in list(self.category.keys()): self.category[k].sort() pass return pass
class CommandProcessor(Mprocessor.Processor): def __init__(self, core_obj, opts=None): get_option = lambda key: \ Mmisc.option_set(opts, key, DEFAULT_PROC_OPTS) Mprocessor.Processor.__init__(self, core_obj) self.continue_running = False # True if we should leave command loop self.event2short = dict(EVENT2SHORT) self.event2short['signal'] = '?!' self.event2short['brkpt'] = 'xx' self.optional_modules = ('ipython', 'bpy', 'deparse') self.optional_modules = ('ipython', 'bpy') self.cmd_instances = self._populate_commands() # command argument string. Is like current_command, but the part # after cmd_name has been removed. self.cmd_argstr = '' # command name before alias or macro resolution self.cmd_name = '' self.cmd_queue = [] # Queued debugger commands self.completer = lambda text, state: \ Mcomplete.completer(self, text, state) self.current_command = '' # Current command getting run self.debug_nest = 1 self.display_mgr = Mdisplay.DisplayMgr() self.intf = core_obj.debugger.intf self.last_command = None # Initially a no-op self.precmd_hooks = [] self.location = lambda: print_location(self) self.preloop_hooks = [] self.postcmd_hooks = [] self._populate_cmd_lists() # Note: prompt_str's value set below isn't used. It is # computed dynamically. The value is suggestive of what it # looks like. self.prompt_str = '(trepan2) ' # Stop only if line/file is different from last time self.different_line = None # These values updated on entry. Set initial values. self.curframe = None self.event = None self.event_arg = None self.frame = None self.list_lineno = 0 # last list number used in "list" self.list_filename = None # last filename used in list self.macros = {} # Debugger Macros # Create a custom safe Repr instance and increase its maxstring. # The default of 30 truncates error messages too easily. self._repr = Repr() self._repr.maxstring = 100 self._repr.maxother = 60 self._repr.maxset = 10 self._repr.maxfrozen = 10 self._repr.array = 10 self.stack = [] self.thread_name = None self.frame_thread_name = None initfile_list = get_option('initfile_list') for init_cmdfile in initfile_list: self.queue_startfile(init_cmdfile) return def _saferepr(self, str, maxwidth=None): if maxwidth is None: maxwidth = self.debugger.settings['width'] return self._repr.repr(str)[:maxwidth] def add_preloop_hook(self, hook, position=-1, nodups=True): if hook in self.preloop_hooks: return False self.preloop_hooks.insert(position, hook) return True # To be overridden in derived debuggers def defaultFile(self): """Produce a reasonable default.""" filename = self.curframe.f_code.co_filename # Consider using is_exec_stmt(). I just don't understand # the conditions under which the below test is true. if filename == '<string>' and self.debugger.mainpyfile: filename = self.debugger.mainpyfile pass return filename def set_prompt(self, prompt='trepan2'): if self.thread_name and self.thread_name != 'MainThread': prompt += ':' + self.thread_name pass self.prompt_str = '%s%s%s' % ('(' * self.debug_nest, prompt, ')' * self.debug_nest) highlight = self.debugger.settings['highlight'] if highlight and highlight in ('light', 'dark'): self.prompt_str = colorize('underline', self.prompt_str) self.prompt_str += ' ' def event_processor(self, frame, event, event_arg, prompt='trepan2'): 'command event processor: reading a commands do something with them.' self.frame = frame self.event = event self.event_arg = event_arg filename = frame.f_code.co_filename lineno = frame.f_lineno if sys.version_info[0] == 2 and sys.version_info[1] <= 4: line = None else: line = linecache.getline(filename, lineno, frame.f_globals) pass if not line: opts = { 'output': 'plain', 'reload_on_change': self.settings('reload'), 'strip_nl': False } line = pyficache.getline(filename, lineno, opts) self.current_source_text = line if self.settings('skip') is not None: if Mbytecode.is_def_stmt(line, frame): return True if Mbytecode.is_class_def(line, frame): return True pass self.thread_name = Mthread.current_thread_name() self.frame_thread_name = self.thread_name self.set_prompt(prompt) self.process_commands() if filename == '<string>': pyficache.remove_remap_file('<string>') return True def forget(self): """ Remove memory of state variables set in the command processor """ self.stack = [] self.curindex = 0 self.curframe = None self.thread_name = None self.frame_thread_name = None return def eval(self, arg): """Eval string arg in the current frame context.""" try: return eval(arg, self.curframe.f_globals, self.curframe.f_locals) except: t, v = sys.exc_info()[:2] if isinstance(t, str): exc_type_name = t pass else: exc_type_name = t.__name__ self.errmsg(str("%s: %s" % (exc_type_name, arg))) raise return None # Not reached def exec_line(self, line): if self.curframe: local_vars = self.curframe.f_locals global_vars = self.curframe.f_globals else: local_vars = None # FIXME: should probably have place where the # user can store variables inside the debug session. # The setup for this should be elsewhere. Possibly # in interaction. global_vars = None try: code = compile(line + '\n', '"%s"' % line, 'single') exec(code, global_vars, local_vars) except: t, v = sys.exc_info()[:2] if type(t) == str: exc_type_name = t else: exc_type_name = t.__name__ self.errmsg('%s: %s' % (str(exc_type_name), str(v))) pass return def parse_position(self, arg, old_mod=None): """parse_position(self, arg)->(fn, name, lineno) Parse arg as [filename:]lineno | function | module Make sure it works for C:\foo\bar.py:12 """ colon = arg.rfind(':') if colon >= 0: # First handle part before the colon arg1 = arg[:colon].rstrip() lineno_str = arg[colon + 1:].lstrip() (mf, filename, lineno) = self.parse_position_one_arg(arg1, old_mod, False) if filename is None: filename = self.core.canonic(arg1) # Next handle part after the colon val = self.get_an_int(lineno_str, "Bad line number: %s" % lineno_str) if val is not None: lineno = val else: (mf, filename, lineno) = self.parse_position_one_arg(arg, old_mod) pass return mf, filename, lineno def parse_position_one_arg(self, arg, old_mod=None, show_errmsg=True): """parse_position_one_arg(self, arg, show_errmsg) -> (module/function, file, lineno) See if arg is a line number, function name, or module name. Return what we've found. None can be returned as a value in the triple. """ modfunc, filename, lineno = (None, None, None) if self.curframe: g = self.curframe.f_globals l = self.curframe.f_locals else: g = globals() l = locals() pass try: # First see if argument is an integer lineno = int(eval(arg, g, l)) if old_mod is None: filename = self.curframe.f_code.co_filename pass except: try: modfunc = eval(arg, g, l) except: modfunc = arg pass msg = ('Object %s is not known yet as a function, module, ' 'or is not found along sys.path, ' 'and not a line number.') % str(repr(arg)) try: # See if argument is a module or function if inspect.isfunction(modfunc): pass elif inspect.ismodule(modfunc): filename = pyficache.pyc2py(modfunc.__file__) filename = self.core.canonic(filename) return (modfunc, filename, None) elif hasattr(modfunc, 'im_func'): modfunc = modfunc.im_func pass else: if show_errmsg: self.errmsg(msg) return (None, None, None) code = modfunc.func_code lineno = code.co_firstlineno filename = code.co_filename except: if show_errmsg: self.errmsg(msg) return (None, None, None) pass return (modfunc, self.core.canonic(filename), lineno) def get_an_int(self, arg, msg_on_error, min_value=None, max_value=None): """Like cmdfns.get_an_int(), but if there's a stack frame use that in evaluation.""" ret_value = self.get_int_noerr(arg) if ret_value is None: if msg_on_error: self.errmsg(msg_on_error) else: self.errmsg('Expecting an integer, got: %s.' % str(arg)) pass return None if min_value and ret_value < min_value: self.errmsg('Expecting integer value to be at least %d, got: %d.' % (min_value, ret_value)) return None elif max_value and ret_value > max_value: self.errmsg('Expecting integer value to be at most %d, got: %d.' % (max_value, ret_value)) return None return ret_value def get_int_noerr(self, arg): """Eval arg and it is an integer return the value. Otherwise return None""" if self.curframe: g = self.curframe.f_globals l = self.curframe.f_locals else: g = globals() l = locals() pass try: val = int(eval(arg, g, l)) except (SyntaxError, NameError, ValueError, TypeError): return None return val def get_int(self, arg, min_value=0, default=1, cmdname=None, at_most=None): """If no argument use the default. If arg is a an integer between least min_value and at_most, use that. Otherwise report an error. If there's a stack frame use that in evaluation.""" if arg is None: return default default = self.get_int_noerr(arg) if default is None: if cmdname: self.errmsg( ("Command '%s' expects an integer; " + "got: %s.") % (cmdname, str(arg))) else: self.errmsg('Expecting a positive integer, got: %s' % str(arg)) pass return None pass if default < min_value: if cmdname: self.errmsg(("Command '%s' expects an integer at least" + ' %d; got: %d.') % (cmdname, min_value, default)) else: self.errmsg( ("Expecting a positive integer at least" + ' %d; got: %d') % (min_value, default)) pass return None elif at_most and default > at_most: if cmdname: self.errmsg(("Command '%s' expects an integer at most" + ' %d; got: %d.') % (cmdname, at_most, default)) else: self.errmsg(("Expecting an integer at most %d; got: %d") % (at_most, default)) pass pass return default def getval(self, arg): try: return eval(arg, self.curframe.f_globals, self.curframe.f_locals) except: t, v = sys.exc_info()[:2] if isinstance(t, str): exc_type_name = t else: exc_type_name = t.__name__ self.errmsg(str("%s: %s" % (exc_type_name, arg))) raise return def ok_for_running(self, cmd_obj, name, nargs): """We separate some of the common debugger command checks here: whether it makes sense to run the command in this execution state, if the command has the right number of arguments and so on. """ if hasattr(cmd_obj, 'execution_set'): if not (self.core.execution_status in cmd_obj.execution_set): part1 = ( "Command '%s' is not available for execution status:" % name) mess = Mmisc.wrapped_lines(part1, self.core.execution_status, self.debugger.settings['width']) self.errmsg(mess) return False pass if self.frame is None and cmd_obj.need_stack: self.intf[-1].errmsg("Command '%s' needs an execution stack." % name) return False if nargs < cmd_obj.min_args: self.errmsg( ("Command '%s' needs at least %d argument(s); " + "got %d.") % (name, cmd_obj.min_args, nargs)) return False elif cmd_obj.max_args is not None and nargs > cmd_obj.max_args: self.errmsg( ("Command '%s' can take at most %d argument(s);" + " got %d.") % (name, cmd_obj.max_args, nargs)) return False return True def process_commands(self): """Handle debugger commands.""" if self.core.execution_status != 'No program': self.setup() self.location() pass leave_loop = run_hooks(self, self.preloop_hooks) self.continue_running = False while not leave_loop: try: run_hooks(self, self.precmd_hooks) # bdb had a True return to leave loop. # A more straight-forward way is to set # instance variable self.continue_running. leave_loop = self.process_command() if leave_loop or self.continue_running: break except EOFError: # If we have stacked interfaces, pop to the next # one. If this is the last one however, we'll # just stick with that. FIXME: Possibly we should # check to see if we are interactive. and not # leave if that's the case. Is this the right # thing? investigate and fix. if len(self.debugger.intf) > 1: del self.debugger.intf[-1] self.last_command = '' else: if self.debugger.intf[-1].output: self.debugger.intf[-1].output.writeline('Leaving') raise Mexcept.DebuggerQuit pass break pass pass return run_hooks(self, self.postcmd_hooks) def process_command(self): # process command if len(self.cmd_queue) > 0: current_command = self.cmd_queue[0].strip() del self.cmd_queue[0] else: current_command = (self.intf[-1].read_command( self.prompt_str).strip()) if '' == current_command and self.intf[-1].interactive: current_command = self.last_command pass pass # Look for comments if '' == current_command: if self.intf[-1].interactive: self.errmsg("No previous command registered, " + "so this is a no-op.") pass return False if current_command is None or current_command[0] == '#': return False try: args_list = arg_split(current_command) except: self.errmsg("bad parse %s" < sys.exc_info()[0]) return False for args in args_list: if len(args): while True: if len(args) == 0: return False macro_cmd_name = args[0] if macro_cmd_name not in self.macros: break try: current_command = \ self.macros[macro_cmd_name][0](*args[1:]) except TypeError: t, v = sys.exc_info()[:2] self.errmsg("Error expanding macro %s" % macro_cmd_name) return False if self.settings('debugmacro'): print(current_command) pass if isinstance(types.ListType, type(current_command)): for x in current_command: if bytes != type(x): self.errmsg(("macro %s should return a List " + "of Strings. Has %s of type %s") % (macro_cmd_name, x, repr(current_command), type(x))) return False pass first = current_command[0] args = first.split() self.cmd_queue + [current_command[1:]] current_command = first elif type(current_command) == str: args = current_command.split() else: self.errmsg(("macro %s should return a List " + "of Strings or a String. Got %s") % (macro_cmd_name, repr(current_command))) return False pass self.cmd_name = args[0] cmd_name = resolve_name(self, self.cmd_name) self.cmd_argstr = current_command[len(self.cmd_name):].lstrip() if cmd_name: self.last_command = current_command cmd_obj = self.commands[cmd_name] if self.ok_for_running(cmd_obj, cmd_name, len(args) - 1): try: self.current_command = current_command result = cmd_obj.run(args) if result: return result except (Mexcept.DebuggerQuit, Mexcept.DebuggerRestart, SystemExit): # Let these exceptions propagate through raise except: self.errmsg("INTERNAL ERROR: " + traceback.format_exc()) pass pass pass elif not self.settings('autoeval'): self.undefined_cmd(current_command) else: self.exec_line(current_command) pass pass pass return False def remove_preloop_hook(self, hook): try: position = self.preloop_hooks.index(hook) except ValueError: return False del self.preloop_hooks[position] return True def setup(self): """Initialization done before entering the debugger-command loop. In particular we set up the call stack used for local variable lookup and frame/up/down commands. We return True if we should NOT enter the debugger-command loop.""" self.forget() if self.settings('dbg_trepan'): self.frame = inspect.currentframe() pass if self.event in ['exception', 'c_exception']: exc_type, exc_value, exc_traceback = self.event_arg else: _, _, exc_traceback = ( None, None, None, ) # NOQA pass if self.frame or exc_traceback: self.stack, self.curindex = \ get_stack(self.frame, exc_traceback, None, self) self.curframe = self.stack[self.curindex][0] self.thread_name = Mthread.current_thread_name() if exc_traceback: self.list_lineno = traceback.extract_tb(exc_traceback, 1)[0][1] else: self.stack = self.curframe = \ self.botframe = None pass if self.curframe: self.list_lineno = \ max(1, inspect.getlineno(self.curframe) - int(self.settings('listsize') / 2)) - 1 self.list_filename = self.curframe.f_code.co_filename else: if not exc_traceback: self.list_lineno = None pass # if self.execRcLines()==1: return True return False def queue_startfile(self, cmdfile): """Arrange for file of debugger commands to get read in the process-command loop.""" expanded_cmdfile = os.path.expanduser(cmdfile) is_readable = Mfile.readable(expanded_cmdfile) if is_readable: self.cmd_queue.append('source ' + expanded_cmdfile) elif is_readable is None: self.errmsg("source file '%s' doesn't exist" % expanded_cmdfile) else: self.errmsg("source file '%s' is not readable" % expanded_cmdfile) pass return def undefined_cmd(self, cmd): """Error message when a command doesn't exist""" self.errmsg('Undefined command: "%s". Try "help".' % cmd) return def read_history_file(self): """Read the command history file -- possibly.""" histfile = self.debugger.intf[-1].histfile try: import readline readline.read_history_file(histfile) except IOError: pass except ImportError: pass return def write_history_file(self): """Write the command history file -- possibly.""" settings = self.debugger.settings histfile = self.debugger.intf[-1].histfile if settings['hist_save']: try: import readline try: readline.write_history_file(histfile) except IOError: pass except ImportError: pass pass return def _populate_commands(self): """ Create an instance of each of the debugger commands. Commands are found by importing files in the directory 'command'. Some files are excluded via an array set in __init__. For each of the remaining files, we import them and scan for class names inside those files and for each class name, we will create an instance of that class. The set of DebuggerCommand class instances form set of possible debugger commands.""" import trepan.processor.command as Mcommand if hasattr(Mcommand, '__modules__'): return self.populate_commands_easy_install(Mcommand) else: return self.populate_commands_pip(Mcommand) def populate_commands_pip(self, Mcommand): cmd_instances = [] eval_cmd_template = 'command_mod.%s(self)' for mod_name in Mcommand.__dict__.keys(): if mod_name.startswith('__'): continue import_name = "trepan.processor.command." + mod_name imp = __import__(import_name) if imp.__name__ == 'trepan': command_mod = imp.processor.command else: if mod_name in ( 'info_sub', 'set_sub', 'show_sub', ): pass try: command_mod = getattr(__import__(import_name), mod_name) except: # Don't need to warn about optional modules if mod_name not in self.optional_modules: print('Error importing %s: %s' % (mod_name, sys.exc_info()[0])) pass continue pass classnames = [ tup[0] for tup in inspect.getmembers(command_mod, inspect.isclass) if ('DebuggerCommand' != tup[0] and tup[0].endswith('Command')) ] for classname in classnames: eval_cmd = eval_cmd_template % classname try: instance = eval(eval_cmd) cmd_instances.append(instance) except ImportError: pass except: print('Error loading %s from %s: %s' % (classname, mod_name, sys.exc_info()[0])) pass pass pass return cmd_instances def populate_commands_easy_install(self, Mcommand): cmd_instances = [] for mod_name in Mcommand.__modules__: if mod_name in ( 'info_sub', 'set_sub', 'show_sub', ): pass import_name = 'trepan.processor.command.' + mod_name try: command_mod = __import__(import_name, None, None, ['*']) except: if mod_name not in self.optional_modules: print('Error importing %s: %s' % (mod_name, sys.exc_info()[0])) pass continue classnames = [ tup[0] for tup in inspect.getmembers(command_mod, inspect.isclass) if ('DebuggerCommand' != tup[0] and tup[0].endswith('Command')) ] for classname in classnames: if False: instance = getattr(command_mod, classname)(self) cmd_instances.append(instance) else: try: instance = getattr(command_mod, classname)(self) cmd_instances.append(instance) except: print('Error loading %s from %s: %s' % (classname, mod_name, sys.exc_info()[0])) pass pass pass pass return cmd_instances def _populate_cmd_lists(self): """ Populate self.lists and hashes: self.commands, and self.aliases, self.category """ self.commands = {} self.aliases = {} self.category = {} # self.short_help = {} for cmd_instance in self.cmd_instances: if not hasattr(cmd_instance, 'aliases'): continue alias_names = cmd_instance.aliases cmd_name = cmd_instance.name self.commands[cmd_name] = cmd_instance for alias_name in alias_names: self.aliases[alias_name] = cmd_name pass cat = getattr(cmd_instance, 'category') if cat and self.category.get(cat): self.category[cat].append(cmd_name) else: self.category[cat] = [cmd_name] pass # sh = getattr(cmd_instance, 'short_help') # if sh: # self.short_help[cmd_name] = getattr(c, 'short_help') # pass pass for k in list(self.category.keys()): self.category[k].sort() pass return pass
def log_repr(result): r = Repr() r.maxstring = 60 r.maxother = 60 return r.repr(result)
class CommandProcessor(Mprocessor.Processor): def __init__(self, core_obj, opts=None): get_option = lambda key: Mmisc.option_set(opts, key, DEFAULT_PROC_OPTS) Mprocessor.Processor.__init__(self, core_obj) self.continue_running = False # True if we should leave command loop self.event2short = dict(EVENT2SHORT) self.event2short["signal"] = "?!" self.event2short["brkpt"] = "xx" self.optional_modules = ("ipython", "bpy") self.cmd_instances = self._populate_commands() # command argument string. Is like current_command, but the part # after cmd_name has been removed. self.cmd_argstr = "" # command name before alias or macro resolution self.cmd_name = "" self.cmd_queue = [] # Queued debugger commands self.completer = lambda text, state: Mcomplete.completer( self, text, state) self.current_command = "" # Current command getting run self.debug_nest = 1 self.display_mgr = Mdisplay.DisplayMgr() self.file2file_remap = {} self.intf = core_obj.debugger.intf self.last_command = None # Initially a no-op self.precmd_hooks = [] self.location = lambda: print_location(self) self.preloop_hooks = [] self.postcmd_hooks = [] self.remap_file_re = None self._populate_cmd_lists() # Note: prompt_str's value set below isn't used. It is # computed dynamically. The value is suggestive of what it # looks like. self.prompt_str = "(trepan2) " # Stop only if line/file is different from last time self.different_line = None # These values updated on entry. Set initial values. self.curframe = None self.event = None self.event_arg = None self.frame = None self.list_lineno = 0 # last list number used in "list" self.list_offset = -1 # last list number used in "disassemble" self.list_obj = None self.list_filename = None # last filename used in list self.list_orig_lineno = 0 # line number of frame or exception on setup self.list_filename = None # filename of frame or exception on setup self.macros = {} # Debugger Macros # Create a custom safe Repr instance and increase its maxstring. # The default of 30 truncates error messages too easily. self._repr = Repr() self._repr.maxstring = 100 self._repr.maxother = 60 self._repr.maxset = 10 self._repr.maxfrozen = 10 self._repr.array = 10 self.stack = [] self.thread_name = None self.frame_thread_name = None initfile_list = get_option("initfile_list") for init_cmdfile in initfile_list: self.queue_startfile(init_cmdfile) return def add_remap_pat(self, pat, replace, clear_remap=True): pyficache.main.add_remap_pat(pat, replace, clear_remap) if clear_remap: self.file2file_remap = {} pyficache.file2file_remap = {} def _saferepr(self, str, maxwidth=None): if maxwidth is None: maxwidth = self.debugger.settings["width"] return self._repr.repr(str)[:maxwidth] def add_preloop_hook(self, hook, position=-1, nodups=True): if hook in self.preloop_hooks: return False self.preloop_hooks.insert(position, hook) return True # To be overridden in derived debuggers def defaultFile(self): """Produce a reasonable default.""" filename = self.curframe.f_code.co_filename # Consider using is_exec_stmt(). I just don't understand # the conditions under which the below test is true. if filename == "<string>" and self.debugger.mainpyfile: filename = self.debugger.mainpyfile pass return filename def set_prompt(self, prompt="trepan2"): if self.thread_name and self.thread_name != "MainThread": prompt += ":" + self.thread_name pass self.prompt_str = "%s%s%s" % ( "(" * self.debug_nest, prompt, ")" * self.debug_nest, ) highlight = self.debugger.settings["highlight"] if highlight and highlight in ("light", "dark"): self.prompt_str = colorize("underline", self.prompt_str) self.prompt_str += " " def event_processor(self, frame, event, event_arg, prompt="trepan2"): "command event processor: reading a commands do something with them." self.frame = frame self.event = event self.event_arg = event_arg filename = frame.f_code.co_filename lineno = frame.f_lineno if sys.version_info[0] == 2 and sys.version_info[1] <= 4: line = None else: line = linecache.getline(filename, lineno, frame.f_globals) pass if not line: opts = { "output": "plain", "reload_on_change": self.settings("reload"), "strip_nl": False, } line = pyficache.getline(filename, lineno, opts) self.current_source_text = line if self.settings("skip") is not None: if Mbytecode.is_def_stmt(line, frame): return True if Mbytecode.is_class_def(line, frame): return True pass self.thread_name = Mthread.current_thread_name() self.frame_thread_name = self.thread_name self.set_prompt(prompt) self.process_commands() if filename == "<string>": pyficache.remove_remap_file("<string>") return True def forget(self): """ Remove memory of state variables set in the command processor """ self.stack = [] self.curindex = 0 self.curframe = None self.thread_name = None self.frame_thread_name = None return def eval(self, arg): """Eval string arg in the current frame context.""" try: return eval(arg, self.curframe.f_globals, self.curframe.f_locals) except: t, v = sys.exc_info()[:2] if isinstance(t, str): exc_type_name = t pass else: exc_type_name = t.__name__ self.errmsg(str("%s: %s" % (exc_type_name, arg))) raise return None # Not reached def exec_line(self, line): if self.curframe: local_vars = self.curframe.f_locals global_vars = self.curframe.f_globals else: local_vars = None # FIXME: should probably have place where the # user can store variables inside the debug session. # The setup for this should be elsewhere. Possibly # in interaction. global_vars = None try: code = compile(line + "\n", '"%s"' % line, "single") exec(code, global_vars, local_vars) except: t, v = sys.exc_info()[:2] if type(t) == str: exc_type_name = t else: exc_type_name = t.__name__ self.errmsg("%s: %s" % (str(exc_type_name), str(v))) pass return def get_an_int(self, arg, msg_on_error, min_value=None, max_value=None): """Like cmdfns.get_an_int(), but if there's a stack frame use that in evaluation.""" ret_value = self.get_int_noerr(arg) if ret_value is None: if msg_on_error: self.errmsg(msg_on_error) else: self.errmsg("Expecting an integer, got: %s." % str(arg)) pass return None if min_value and ret_value < min_value: self.errmsg("Expecting integer value to be at least %d, got: %d." % (min_value, ret_value)) return None elif max_value and ret_value > max_value: self.errmsg("Expecting integer value to be at most %d, got: %d." % (max_value, ret_value)) return None return ret_value def get_int_noerr(self, arg): """Eval arg and it is an integer return the value. Otherwise return None""" if self.curframe: g = self.curframe.f_globals l = self.curframe.f_locals else: g = globals() l = locals() pass try: val = int(eval(arg, g, l)) except (SyntaxError, NameError, ValueError, TypeError): return None return val def get_int(self, arg, min_value=0, default=1, cmdname=None, at_most=None): """If no argument use the default. If arg is a an integer between least min_value and at_most, use that. Otherwise report an error. If there's a stack frame use that in evaluation.""" if arg is None: return default default = self.get_int_noerr(arg) if default is None: if cmdname: self.errmsg( ("Command '%s' expects an integer; " + "got: %s.") % (cmdname, str(arg))) else: self.errmsg("Expecting a positive integer, got: %s" % str(arg)) pass return None pass if default < min_value: if cmdname: self.errmsg(("Command '%s' expects an integer at least" + " %d; got: %d.") % (cmdname, min_value, default)) else: self.errmsg( ("Expecting a positive integer at least" + " %d; got: %d") % (min_value, default)) pass return None elif at_most and default > at_most: if cmdname: self.errmsg(("Command '%s' expects an integer at most" + " %d; got: %d.") % (cmdname, at_most, default)) else: self.errmsg(("Expecting an integer at most %d; got: %d") % (at_most, default)) pass pass return default def getval(self, arg, locals=None): if not locals: locals = self.curframe.f_locals try: return eval(arg, self.curframe.f_globals, locals) except: t, v = sys.exc_info()[:2] if isinstance(t, str): exc_type_name = t else: exc_type_name = t.__name__ self.errmsg(str("%s: %s" % (exc_type_name, arg))) raise return def ok_for_running(self, cmd_obj, name, nargs): """We separate some of the common debugger command checks here: whether it makes sense to run the command in this execution state, if the command has the right number of arguments and so on. """ if hasattr(cmd_obj, "execution_set"): if not (self.core.execution_status in cmd_obj.execution_set): part1 = "Command '%s' is not available for execution status:" % name mess = Mmisc.wrapped_lines(part1, self.core.execution_status, self.debugger.settings["width"]) self.errmsg(mess) return False pass if self.frame is None and cmd_obj.need_stack: self.intf[-1].errmsg("Command '%s' needs an execution stack." % name) return False if nargs < cmd_obj.min_args: self.errmsg( ("Command '%s' needs at least %d argument(s); " + "got %d.") % (name, cmd_obj.min_args, nargs)) return False elif cmd_obj.max_args is not None and nargs > cmd_obj.max_args: self.errmsg( ("Command '%s' can take at most %d argument(s);" + " got %d.") % (name, cmd_obj.max_args, nargs)) return False return True def process_commands(self): """Handle debugger commands.""" if self.core.execution_status != "No program": self.setup() self.location() pass leave_loop = run_hooks(self, self.preloop_hooks) self.continue_running = False while not leave_loop: try: run_hooks(self, self.precmd_hooks) # bdb had a True return to leave loop. # A more straight-forward way is to set # instance variable self.continue_running. leave_loop = self.process_command() if leave_loop or self.continue_running: break except EOFError: # If we have stacked interfaces, pop to the next # one. If this is the last one however, we'll # just stick with that. FIXME: Possibly we should # check to see if we are interactive. and not # leave if that's the case. Is this the right # thing? investigate and fix. if len(self.debugger.intf) > 1: del self.debugger.intf[-1] self.last_command = "" else: if self.debugger.intf[-1].output: self.debugger.intf[-1].output.writeline("Leaving") raise Mexcept.DebuggerQuit pass break pass pass return run_hooks(self, self.postcmd_hooks) def process_command(self): # process command if len(self.cmd_queue) > 0: current_command = self.cmd_queue[0].strip() del self.cmd_queue[0] else: current_command = self.intf[-1].read_command( self.prompt_str).strip() if "" == current_command and self.intf[-1].interactive: current_command = self.last_command pass pass # Look for comments if "" == current_command: if self.intf[-1].interactive: self.errmsg("No previous command registered, " + "so this is a no-op.") pass return False if current_command is None or current_command[0] == "#": return False try: args_list = arg_split(current_command) except: self.errmsg("bad parse %s" % sys.exc_info()[0]) return False for args in args_list: if len(args): while True: if len(args) == 0: return False macro_cmd_name = args[0] if macro_cmd_name not in self.macros: break try: current_command = self.macros[macro_cmd_name][0]( *args[1:]) except TypeError: t, v = sys.exc_info()[:2] self.errmsg("Error expanding macro %s" % macro_cmd_name) return False if self.settings("debugmacro"): print(current_command) pass if isinstance(types.ListType, type(current_command)): for x in current_command: if bytes != type(x): self.errmsg( ("macro %s should return a List " + "of Strings. Has %s of type %s") % ( macro_cmd_name, x, repr(current_command), type(x), )) return False pass first = current_command[0] args = first.split() self.cmd_queue + [current_command[1:]] current_command = first elif type(current_command) == str: args = current_command.split() else: self.errmsg(("macro %s should return a List " + "of Strings or a String. Got %s") % (macro_cmd_name, repr(current_command))) return False pass self.cmd_name = args[0] cmd_name = resolve_name(self, self.cmd_name) self.cmd_argstr = current_command[len(self.cmd_name):].lstrip() if cmd_name: self.last_command = current_command cmd_obj = self.commands[cmd_name] if self.ok_for_running(cmd_obj, cmd_name, len(args) - 1): try: self.current_command = current_command result = cmd_obj.run(args) if result: return result except ( Mexcept.DebuggerQuit, Mexcept.DebuggerRestart, SystemExit, ): # Let these exceptions propagate through raise except: self.errmsg("INTERNAL ERROR: " + traceback.format_exc()) pass pass pass elif not self.settings("autoeval"): self.undefined_cmd(current_command) else: self.exec_line(current_command) pass pass pass return False def remove_preloop_hook(self, hook): try: position = self.preloop_hooks.index(hook) except ValueError: return False del self.preloop_hooks[position] return True def setup(self): """Initialization done before entering the debugger-command loop. In particular we set up the call stack used for local variable lookup and frame/up/down commands. We return True if we should NOT enter the debugger-command loop.""" self.forget() if self.settings("dbg_trepan"): self.frame = inspect.currentframe() pass if self.event in ["exception", "c_exception"]: exc_type, exc_value, exc_traceback = self.event_arg else: _, _, exc_traceback = ( None, None, None, ) # NOQA pass if self.frame or exc_traceback: self.stack, self.curindex = get_stack(self.frame, exc_traceback, None, self) self.curframe = self.stack[self.curindex][0] self.thread_name = Mthread.current_thread_name() if exc_traceback: self.list_lineno = traceback.extract_tb(exc_traceback, 1)[0][1] self.list_offset = self.curframe.f_lasti self.list_object = self.curframe else: self.stack = self.curframe = self.botframe = None pass if self.curframe: self.list_lineno = (max( 1, inspect.getlineno(self.curframe) - int(self.settings("listsize") / 2), ) - 1) self.list_offset = self.curframe.f_lasti self.list_filename = self.curframe.f_code.co_filename self.list_object = self.curframe else: if not exc_traceback: self.list_lineno = None pass # if self.execRcLines()==1: return True # FIXME: do we want to save self.list_lineno a second place # so that we can do 'list .' and go back to the first place we listed? return False def queue_startfile(self, cmdfile): """Arrange for file of debugger commands to get read in the process-command loop.""" expanded_cmdfile = osp.expanduser(cmdfile) is_readable = Mfile.readable(expanded_cmdfile) if is_readable: self.cmd_queue.append("source " + expanded_cmdfile) elif is_readable is None: self.errmsg("source file '%s' doesn't exist" % expanded_cmdfile) else: self.errmsg("source file '%s' is not readable" % expanded_cmdfile) pass return def undefined_cmd(self, cmd): """Error message when a command doesn't exist""" self.errmsg('Undefined command: "%s". Try "help".' % cmd) return def read_history_file(self): """Read the command history file -- possibly.""" histfile = self.debugger.intf[-1].histfile try: import readline readline.read_history_file(histfile) except IOError: pass except ImportError: pass return def write_history_file(self): """Write the command history file -- possibly.""" settings = self.debugger.settings histfile = self.debugger.intf[-1].histfile if settings["hist_save"]: try: import readline try: readline.write_history_file(histfile) except IOError: pass except ImportError: pass pass return def _populate_commands(self): """ Create an instance of each of the debugger commands. Commands are found by importing files in the directory 'command'. Some files are excluded via an array set in __init__. For each of the remaining files, we import them and scan for class names inside those files and for each class name, we will create an instance of that class. The set of DebuggerCommand class instances form set of possible debugger commands.""" import trepan.processor.command as Mcommand if hasattr(Mcommand, "__modules__"): return self.populate_commands_easy_install(Mcommand) else: return self.populate_commands_pip(Mcommand) def populate_commands_pip(self, Mcommand): cmd_instances = [] eval_cmd_template = "command_mod.%s(self)" for mod_name in Mcommand.__dict__.keys(): if mod_name.startswith("__"): continue import_name = "trepan.processor.command." + mod_name imp = __import__(import_name) if imp.__name__ == "trepan": command_mod = imp.processor.command else: if mod_name in ( "info_sub", "set_sub", "show_sub", ): pass try: command_mod = getattr(__import__(import_name), mod_name) except: # Don't need to warn about optional modules if mod_name not in self.optional_modules: print("Error importing %s: %s" % (mod_name, sys.exc_info()[0])) pass continue pass classnames = [ tup[0] for tup in inspect.getmembers(command_mod, inspect.isclass) if ("DebuggerCommand" != tup[0] and tup[0].endswith("Command")) ] for classname in classnames: eval_cmd = eval_cmd_template % classname try: instance = eval(eval_cmd) cmd_instances.append(instance) except ImportError: pass except: print("Error loading %s from %s: %s" % (classname, mod_name, sys.exc_info()[0])) pass pass pass return cmd_instances def populate_commands_easy_install(self, Mcommand): cmd_instances = [] for mod_name in Mcommand.__modules__: if mod_name in ( "info_sub", "set_sub", "show_sub", ): pass import_name = "trepan.processor.command." + mod_name try: command_mod = __import__(import_name, None, None, ["*"]) except: if mod_name not in self.optional_modules: print("Error importing %s: %s" % (mod_name, sys.exc_info()[0])) pass continue classnames = [ tup[0] for tup in inspect.getmembers(command_mod, inspect.isclass) if ("DebuggerCommand" != tup[0] and tup[0].endswith("Command")) ] for classname in classnames: if False: instance = getattr(command_mod, classname)(self) cmd_instances.append(instance) else: try: instance = getattr(command_mod, classname)(self) cmd_instances.append(instance) except: print("Error loading %s from %s: %s" % (classname, mod_name, sys.exc_info()[0])) pass pass pass pass return cmd_instances def _populate_cmd_lists(self): """ Populate self.lists and hashes: self.commands, and self.aliases, self.category """ self.commands = {} self.aliases = {} self.category = {} # self.short_help = {} for cmd_instance in self.cmd_instances: if not hasattr(cmd_instance, "aliases"): continue alias_names = cmd_instance.aliases cmd_name = cmd_instance.name self.commands[cmd_name] = cmd_instance for alias_name in alias_names: self.aliases[alias_name] = cmd_name pass cat = getattr(cmd_instance, "category") if cat and self.category.get(cat): self.category[cat].append(cmd_name) else: self.category[cat] = [cmd_name] pass # sh = getattr(cmd_instance, 'short_help') # if sh: # self.short_help[cmd_name] = getattr(c, 'short_help') # pass pass for k in list(self.category.keys()): self.category[k].sort() pass return pass