Adding new stuff
This commit is contained in:
parent
39ee792ad4
commit
a410da0e04
722 changed files with 331 additions and 189 deletions
|
|
@ -1,214 +0,0 @@
|
|||
# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
|
||||
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
|
||||
#
|
||||
# This file is part of logilab-common.
|
||||
#
|
||||
# logilab-common is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Lesser General Public License as published by the Free
|
||||
# Software Foundation, either version 2.1 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# logilab-common 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 Lesser General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with logilab-common. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""Customized version of pdb's default debugger.
|
||||
|
||||
- sets up a history file
|
||||
- uses ipython if available to colorize lines of code
|
||||
- overrides list command to search for current block instead
|
||||
of using 5 lines of context
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
__docformat__ = "restructuredtext en"
|
||||
|
||||
try:
|
||||
import readline
|
||||
except ImportError:
|
||||
readline = None
|
||||
import os
|
||||
import os.path as osp
|
||||
import sys
|
||||
from pdb import Pdb
|
||||
import inspect
|
||||
|
||||
from logilab.common.compat import StringIO
|
||||
|
||||
try:
|
||||
from IPython import PyColorize
|
||||
except ImportError:
|
||||
def colorize(source, *args):
|
||||
"""fallback colorize function"""
|
||||
return source
|
||||
def colorize_source(source, *args):
|
||||
return source
|
||||
else:
|
||||
def colorize(source, start_lineno, curlineno):
|
||||
"""colorize and annotate source with linenos
|
||||
(as in pdb's list command)
|
||||
"""
|
||||
parser = PyColorize.Parser()
|
||||
output = StringIO()
|
||||
parser.format(source, output)
|
||||
annotated = []
|
||||
for index, line in enumerate(output.getvalue().splitlines()):
|
||||
lineno = index + start_lineno
|
||||
if lineno == curlineno:
|
||||
annotated.append('%4s\t->\t%s' % (lineno, line))
|
||||
else:
|
||||
annotated.append('%4s\t\t%s' % (lineno, line))
|
||||
return '\n'.join(annotated)
|
||||
|
||||
def colorize_source(source):
|
||||
"""colorize given source"""
|
||||
parser = PyColorize.Parser()
|
||||
output = StringIO()
|
||||
parser.format(source, output)
|
||||
return output.getvalue()
|
||||
|
||||
|
||||
def getsource(obj):
|
||||
"""Return the text of the source code for an object.
|
||||
|
||||
The argument may be a module, class, method, function, traceback, frame,
|
||||
or code object. The source code is returned as a single string. An
|
||||
IOError is raised if the source code cannot be retrieved."""
|
||||
lines, lnum = inspect.getsourcelines(obj)
|
||||
return ''.join(lines), lnum
|
||||
|
||||
|
||||
################################################################
|
||||
class Debugger(Pdb):
|
||||
"""custom debugger
|
||||
|
||||
- sets up a history file
|
||||
- uses ipython if available to colorize lines of code
|
||||
- overrides list command to search for current block instead
|
||||
of using 5 lines of context
|
||||
"""
|
||||
def __init__(self, tcbk=None):
|
||||
Pdb.__init__(self)
|
||||
self.reset()
|
||||
if tcbk:
|
||||
while tcbk.tb_next is not None:
|
||||
tcbk = tcbk.tb_next
|
||||
self._tcbk = tcbk
|
||||
self._histfile = os.path.expanduser("~/.pdbhist")
|
||||
|
||||
def setup_history_file(self):
|
||||
"""if readline is available, read pdb history file
|
||||
"""
|
||||
if readline is not None:
|
||||
try:
|
||||
# XXX try..except shouldn't be necessary
|
||||
# read_history_file() can accept None
|
||||
readline.read_history_file(self._histfile)
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
def start(self):
|
||||
"""starts the interactive mode"""
|
||||
self.interaction(self._tcbk.tb_frame, self._tcbk)
|
||||
|
||||
def setup(self, frame, tcbk):
|
||||
"""setup hook: set up history file"""
|
||||
self.setup_history_file()
|
||||
Pdb.setup(self, frame, tcbk)
|
||||
|
||||
def set_quit(self):
|
||||
"""quit hook: save commands in the history file"""
|
||||
if readline is not None:
|
||||
readline.write_history_file(self._histfile)
|
||||
Pdb.set_quit(self)
|
||||
|
||||
def complete_p(self, text, line, begin_idx, end_idx):
|
||||
"""provide variable names completion for the ``p`` command"""
|
||||
namespace = dict(self.curframe.f_globals)
|
||||
namespace.update(self.curframe.f_locals)
|
||||
if '.' in text:
|
||||
return self.attr_matches(text, namespace)
|
||||
return [varname for varname in namespace if varname.startswith(text)]
|
||||
|
||||
|
||||
def attr_matches(self, text, namespace):
|
||||
"""implementation coming from rlcompleter.Completer.attr_matches
|
||||
Compute matches when text contains a dot.
|
||||
|
||||
Assuming the text is of the form NAME.NAME....[NAME], and is
|
||||
evaluatable in self.namespace, it will be evaluated and its attributes
|
||||
(as revealed by dir()) are used as possible completions. (For class
|
||||
instances, class members are also considered.)
|
||||
|
||||
WARNING: this can still invoke arbitrary C code, if an object
|
||||
with a __getattr__ hook is evaluated.
|
||||
|
||||
"""
|
||||
import re
|
||||
m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text)
|
||||
if not m:
|
||||
return
|
||||
expr, attr = m.group(1, 3)
|
||||
object = eval(expr, namespace)
|
||||
words = dir(object)
|
||||
if hasattr(object, '__class__'):
|
||||
words.append('__class__')
|
||||
words = words + self.get_class_members(object.__class__)
|
||||
matches = []
|
||||
n = len(attr)
|
||||
for word in words:
|
||||
if word[:n] == attr and word != "__builtins__":
|
||||
matches.append("%s.%s" % (expr, word))
|
||||
return matches
|
||||
|
||||
def get_class_members(self, klass):
|
||||
"""implementation coming from rlcompleter.get_class_members"""
|
||||
ret = dir(klass)
|
||||
if hasattr(klass, '__bases__'):
|
||||
for base in klass.__bases__:
|
||||
ret = ret + self.get_class_members(base)
|
||||
return ret
|
||||
|
||||
## specific / overridden commands
|
||||
def do_list(self, arg):
|
||||
"""overrides default list command to display the surrounding block
|
||||
instead of 5 lines of context
|
||||
"""
|
||||
self.lastcmd = 'list'
|
||||
if not arg:
|
||||
try:
|
||||
source, start_lineno = getsource(self.curframe)
|
||||
print(colorize(''.join(source), start_lineno,
|
||||
self.curframe.f_lineno))
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except IOError:
|
||||
Pdb.do_list(self, arg)
|
||||
else:
|
||||
Pdb.do_list(self, arg)
|
||||
do_l = do_list
|
||||
|
||||
def do_open(self, arg):
|
||||
"""opens source file corresponding to the current stack level"""
|
||||
filename = self.curframe.f_code.co_filename
|
||||
lineno = self.curframe.f_lineno
|
||||
cmd = 'emacsclient --no-wait +%s %s' % (lineno, filename)
|
||||
os.system(cmd)
|
||||
|
||||
do_o = do_open
|
||||
|
||||
def pm():
|
||||
"""use our custom debugger"""
|
||||
dbg = Debugger(sys.last_traceback)
|
||||
dbg.start()
|
||||
|
||||
def set_trace():
|
||||
Debugger().set_trace(sys._getframe().f_back)
|
||||
Loading…
Add table
Add a link
Reference in a new issue