Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add saved settings, themes, etc.

  • Loading branch information...
commit 548d29215044cb806a1f124843c04558296b7d65 1 parent dd6daa3
@inducer inducer authored
View
1  MANIFEST.in
@@ -1,3 +1,4 @@
include debug_me.py
include ez_setup.py
include try-the-debugger.sh
+include example-theme.py
View
3  example-theme.py
@@ -0,0 +1,3 @@
+palette.update({
+ "source": (add_setting("black", "underline"), "dark green"),
+ })
View
7 pudb/__init__.py
@@ -4,6 +4,13 @@
+from pudb.settings import load_config, save_config
+CONFIG = load_config()
+save_config(CONFIG)
+
+
+
+
CURRENT_DEBUGGER = []
def _get_debugger():
if not CURRENT_DEBUGGER:
View
107 pudb/debugger.py
@@ -13,6 +13,8 @@
-------------------------------------------
Keys:
+ Ctrl-p - edit preferences
+
n - step over ("next")
s - step into
c - continue
@@ -135,7 +137,7 @@ def set_frame_index(self, index):
self.ui.set_current_line(lineno, self.curframe.f_code.co_filename)
self.ui.update_var_view()
self.ui.update_stack()
-
+
def move_up_frame(self):
if self.curindex > 0:
self.set_frame_index(self.curindex-1)
@@ -346,6 +348,9 @@ def change_var_state(w, size, key):
def edit_inspector_detail(w, size, key):
var, pos = self.var_list._w.get_focus()
+ if var is None:
+ return
+
fvi = self.get_frame_var_info(read_only=False)
iinfo = fvi.get_inspect_info(var.id_path, read_only=False)
@@ -465,7 +470,11 @@ def move_stack_down(w, size, key):
# stack listeners -----------------------------------------------------
def examine_breakpoint(w, size, key):
- _, pos = self.bp_list._w.get_focus()
+ bp_entry, pos = self.bp_list._w.get_focus()
+
+ if bp_entry is None:
+ return
+
bp = self._get_bp_list()[pos]
if bp.cond is None:
@@ -870,50 +879,6 @@ def show_traceback(w, size, key):
self.message("No exception available.")
def run_shell(w, size, key):
- import pudb.shell as shell
-
- if shell.HAVE_IPYTHON and shell.USE_IPYTHON == "ask":
- def ipython(w, size, key):
- self.quit_event_loop = ["ipython"]
- def classic(w, size, key):
- self.quit_event_loop = ["classic"]
- def cancel(w, size, key):
- self.quit_event_loop = [False]
-
- result = dbg.ui.dialog(
- urwid.ListBox([urwid.Text(
- "You've asked to enter a Python shell.\n\n"
- "You appear to have IPython installed. If you wish to use it, "
- "you may hit 'i' or select the corresponding button on the "
- "right. If you prefer the 'classic' Python shell, hit '!' again or "
- "select that button instead.\n\n"
- "Sorry for bothering you, I won't ask again in this session."
- )]),
- [
- ("Classic", "classic"),
- ("IPython", "ipython"),
- ("Cancel", False),
- ],
- focus_buttons=True,
- bind_enter_esc=False,
- title="Shell Requested",
- extra_bindings=[
- ("!", classic),
- ("i", ipython),
- ("esc", cancel),
- ])
-
- if result == False:
- return
- elif result == "ipython":
- shell.USE_IPYTHON = use_ipython = True
- elif result == "classic":
- shell.USE_IPYTHON = use_ipython = False
-
- elif shell.HAVE_IPYTHON and shell.USE_IPYTHON:
- use_ipython = True
- else:
- use_ipython = False
self.screen.stop()
@@ -925,12 +890,14 @@ def cancel(w, size, key):
curframe = self.debugger.curframe
- if use_ipython:
+ from pudb import CONFIG
+ import pudb.shell as shell
+ if shell.HAVE_IPYTHON and CONFIG["shell"] == "ipython":
runner = shell.run_ipython_shell
else:
runner = shell.run_classic_shell
- runner(curframe.f_locals, curframe.f_globals,
+ runner(curframe.f_locals, curframe.f_globals,
first_shell_run)
self.screen.start()
@@ -965,9 +932,20 @@ def quit(w, size, key):
self.debugger.set_quit()
end()
+ def do_edit_config(w, size, key):
+ from pudb.settings import edit_config, save_config
+ from pudb import CONFIG
+ edit_config(self, CONFIG)
+ save_config(CONFIG)
+ self.setup_palette(self.screen)
+
+ for sl in self.source:
+ sl._invalidate()
+
def help(w, size, key):
self.message(HELP_TEXT, title="PuDB Help")
+
self.top.listen("o", show_output)
self.top.listen("!", run_shell)
self.top.listen("e", show_traceback)
@@ -979,6 +957,7 @@ def help(w, size, key):
self.top.listen("B", RHColumnFocuser(2))
self.top.listen("q", quit)
+ self.top.listen("ctrl p", do_edit_config)
self.top.listen("H", help)
self.top.listen("f1", help)
self.top.listen("?", help)
@@ -1068,8 +1047,10 @@ def setup_palette(screen):
may_use_fancy_formats = isinstance(screen, RawScreen) and \
not hasattr(urwid.escape, "_fg_attr_xterm")
+ from pudb import CONFIG
from pudb.theme import get_palette
- screen.register_palette(get_palette(may_use_fancy_formats))
+ screen.register_palette(
+ get_palette(may_use_fancy_formats, CONFIG["theme"]))
# UI enter/exit -----------------------------------------------------------
def show(self):
@@ -1101,6 +1082,30 @@ def event_loop(self, toplevel=None):
self.message("Package 'pygments' not found. "
"Syntax highlighting disabled.")
+ from pudb import CONFIG
+ WELCOME_LEVEL = "b"
+ if CONFIG["seen_welcome"] < WELCOME_LEVEL:
+ CONFIG["seen_welcome"] = WELCOME_LEVEL
+ from pudb import VERSION
+ self.message("Welcome to PudB %s!\n\n"
+ "PuDB is a full-screen, console-based visual debugger for Python. "
+ " Its goal is to provide all the niceties of modern GUI-based "
+ "debuggers in a more lightweight and keyboard-friendly package. "
+ "PuDB allows you to debug code right where you write and test it--in "
+ "a terminal. If you've worked with the excellent (but nowadays "
+ "ancient) DOS-based Turbo Pascal or C tools, PuDB's UI might "
+ "look familiar.\n\n"
+ "New features in version 0.93:\n\n"
+ "- Stored preferences (no more pesky IPython prompt!)\n"
+ "- Themes\n"
+ "- Line numbers (optional)\n"
+ "\nHit Ctrl-P to set up PuDB.\n\n"
+ "If you're new here, welcome! The help screen (invoked by hitting "
+ "'?' after this message) should get you on your way." % VERSION)
+
+ from pudb.settings import save_config
+ save_config(CONFIG)
+
try:
if toplevel is None:
toplevel = self.top
@@ -1130,8 +1135,8 @@ def interaction(self, exc_tuple):
from pudb import VERSION
caption = [(None,
- u"PuDB %s - The Python Urwid debugger - Hit ? for help"
- u" - (C) Andreas Kloeckner 2009"
+ u"PuDB %s - ?:help, n:next, s:step into, b:breakpoint, o:console,"
+ "t:run to cursor, !:python shell"
% VERSION)]
if self.debugger.post_mortem:
View
145 pudb/settings.py
@@ -0,0 +1,145 @@
+import os
+from ConfigParser import ConfigParser
+
+# minor LGPL violation: stolen from python-xdg
+
+_home = os.environ.get('HOME', '/')
+xdg_data_home = os.environ.get('XDG_DATA_HOME',
+ os.path.join(_home, '.local', 'share'))
+
+xdg_config_home = os.environ.get('XDG_CONFIG_HOME',
+ os.path.join(_home, '.config'))
+
+xdg_config_dirs = [xdg_config_home] + \
+ os.environ.get('XDG_CONFIG_DIRS', '/etc/xdg').split(':')
+
+def save_config_path(*resource):
+ resource = os.path.join(*resource)
+ assert not resource.startswith('/')
+ path = os.path.join(xdg_config_home, resource)
+ if not os.path.isdir(path):
+ os.makedirs(path, 0700)
+ return path
+
+# end LGPL violation
+
+CONF_SECTION = "pudb"
+
+def load_config():
+ from os.path import join, isdir
+
+ cparser = ConfigParser()
+
+ conf_dict = {}
+ try:
+ cparser.read([
+ join(cdir, 'pudb', 'pudb.cfg')
+ for cdir in xdg_config_dirs if isdir(cdir)])
+
+ if cparser.has_section(CONF_SECTION):
+ conf_dict.update(dict(cparser.items(CONF_SECTION)))
+ except:
+ pass
+
+ conf_dict.setdefault("shell", "classic")
+ conf_dict.setdefault("theme", "classic")
+ conf_dict.setdefault("line_numbers", False)
+ conf_dict.setdefault("seen_welcome", "a")
+
+ def hack_bool(name):
+ try:
+ if conf_dict[name].lower() in ["0", "false", "off"]:
+ conf_dict[name] = False
+ except:
+ pass
+
+ hack_bool("line_numbers")
+
+ return conf_dict
+
+
+
+
+def save_config(conf_dict):
+ from os.path import join
+
+ cparser = ConfigParser()
+ cparser.add_section(CONF_SECTION)
+
+ for key, val in conf_dict.iteritems():
+ cparser.set(CONF_SECTION, key, val)
+
+ try:
+ outf = open(join(save_config_path("pudb"), "pudb.cfg"), "w")
+ cparser.write(outf)
+ outf.close()
+ except:
+ pass
+
+
+
+
+
+def edit_config(ui, conf_dict):
+ import urwid
+
+ cb_line_numbers = urwid.CheckBox("Show Line Numbers",
+ bool(conf_dict["line_numbers"]))
+
+ shells = ["classic", "ipython"]
+
+ shell_rb_grp = []
+ shell_rbs = [
+ urwid.RadioButton(shell_rb_grp, name,
+ conf_dict["shell"] == name)
+ for name in shells]
+
+ from pudb.theme import THEMES
+
+ known_theme = conf_dict["theme"] in THEMES
+
+ theme_rb_grp = []
+ theme_edit = urwid.Edit(edit_text=conf_dict["theme"])
+ theme_rbs = [
+ urwid.RadioButton(theme_rb_grp, name,
+ conf_dict["theme"] == name)
+ for name in THEMES]+[
+ urwid.RadioButton(theme_rb_grp, "Custom:",
+ not known_theme),
+ urwid.Padding(
+ urwid.AttrWrap(theme_edit, "value"),
+ left=4),
+
+ urwid.Text("\nTo use a custom theme, see example-theme.py in the "
+ "pudb distribution. Enter the full path to a file like it in the "
+ "box above."),
+ ]
+
+ if ui.dialog(
+ urwid.ListBox(
+ [cb_line_numbers]
+ + [urwid.Text("")]
+ + [urwid.AttrWrap(urwid.Text("Shell:\n"), "group head")] + shell_rbs
+ + [urwid.AttrWrap(urwid.Text("\nTheme:\n"), "group head")] + theme_rbs,
+ ),
+ [
+ ("OK", True),
+ ("Cancel", False),
+ ],
+ title="Edit Preferences"):
+ for shell, shell_rb in zip(shells, shell_rbs):
+ if shell_rb.get_state():
+ conf_dict["shell"] = shell
+
+ saw_theme = False
+ for theme, theme_rb in zip(THEMES, theme_rbs):
+ if theme_rb.get_state():
+ conf_dict["theme"] = theme
+ saw_theme = True
+
+ if not saw_theme:
+ conf_dict["theme"] = theme_edit.get_edit_text()
+
+ conf_dict["line_numbers"] = cb_line_numbers.get_state()
+
+
View
1  pudb/shell.py
@@ -4,7 +4,6 @@
HAVE_IPYTHON = False
else:
HAVE_IPYTHON = True
- USE_IPYTHON = "ask"
View
22 pudb/source_view.py
@@ -4,11 +4,11 @@
class SourceLine(urwid.FlowWidget):
- def __init__(self, dbg_ui, text, lineno='', attr=None, has_breakpoint=False):
+ def __init__(self, dbg_ui, text, line_nr='', attr=None, has_breakpoint=False):
self.dbg_ui = dbg_ui
self.text = text
self.attr = attr
- self.lineno = lineno
+ self.line_nr = line_nr
self.has_breakpoint = has_breakpoint
self.is_current = False
self.highlight = False
@@ -32,6 +32,9 @@ def rows(self, (maxcol,), focus=False):
return 1
def render(self, (maxcol,), focus=False):
+ from pudb import CONFIG
+ render_line_nr = CONFIG["line_numbers"]
+
hscroll = self.dbg_ui.source_hscroll_start
attrs = []
if self.is_current:
@@ -53,7 +56,9 @@ def render(self, (maxcol,), focus=False):
attrs.append("highlighted")
if not attrs and self.attr is not None:
- attr = [("lineno", len(self.lineno))]+self.attr
+ attr = self.attr
+ if render_line_nr:
+ attr = [("line number", len(self.line_nr))] + attr
else:
attr = [(" ".join(attrs+["source"]), hscroll+maxcol-2)]
@@ -66,7 +71,10 @@ def render(self, (maxcol,), focus=False):
self.dbg_ui.source_hscroll_start,
rle_len(attr))
- text = crnt+bp+self.lineno+text
+ if render_line_nr:
+ text = self.line_nr + text
+
+ text = crnt+bp+text
attr = [("source", 1), ("bp_star", 1)] + attr
# clipping ------------------------------------------------------------
@@ -88,13 +96,13 @@ def keypress(self, size, key):
def format_source(debugger_ui, lines, breakpoints):
- lineno_str = "%%%dd "%(len(str(len(lines))))
+ lineno_format = "%%%dd "%(len(str(len(lines))))
try:
import pygments
except ImportError:
return [SourceLine(debugger_ui,
line.rstrip("\n\r").replace("\t", 8*" "),
- lineno_str%(i+1), None,
+ lineno_format % (i+1), None,
has_breakpoint=i+1 in breakpoints)
for i, line in enumerate(lines)]
else:
@@ -143,7 +151,7 @@ def shipout_line():
result.append(
SourceLine(debugger_ui,
subself.current_line,
- lineno_str%subself.lineno,
+ lineno_format % subself.lineno,
subself.current_attr,
has_breakpoint=subself.lineno in breakpoints))
subself.current_line = ""
View
46 pudb/theme.py
@@ -1,4 +1,9 @@
-def get_palette(may_use_fancy_formats):
+THEMES = ["classic", "vim"]
+
+
+
+
+def get_palette(may_use_fancy_formats, theme="classic"):
if may_use_fancy_formats:
def add_setting(color, setting):
return color+","+setting
@@ -6,7 +11,7 @@ def add_setting(color, setting):
def add_setting(color, setting):
return color
- return [
+ palette = [
("header", "black", "light gray", "standout"),
("breakpoint source", "yellow", "dark red"),
@@ -75,6 +80,7 @@ def add_setting(color, setting):
("label", "black", "light gray"),
("value", "yellow", "dark blue"),
("fixed value", "light gray", "dark blue"),
+ ("group head", add_setting("black", "bold"), "light gray"),
("search box", "black", "dark cyan"),
("search not found", "white", "dark red"),
@@ -89,7 +95,7 @@ def add_setting(color, setting):
("current focused source", "white", "dark cyan"),
("current highlighted source", "white", "dark cyan"),
- ("lineno", "light gray", "dark blue"),
+ ("line number", "light gray", "dark blue"),
("keyword", add_setting("white", "bold"), "dark blue"),
("name", "light cyan", "dark blue"),
("literal", "light magenta", "dark blue"),
@@ -99,3 +105,37 @@ def add_setting(color, setting):
]
+ palette_dict = dict(
+ (entry[0], entry[1:]) for entry in palette)
+
+ if theme == "classic":
+ pass
+ elif theme == "vim":
+ palette_dict.update({
+ "source": ("black", "default"),
+ "keyword": ("brown", "default"),
+ "kw_namespace": ("dark magenta", "default"),
+ "literal": ("black", "default"),
+ "string": ("dark red", "default"),
+ "punctuation": ("black", "default"),
+ "comment": ("dark blue", "default"),
+ "classname": ("dark cyan", "default"),
+ "name": ("dark cyan", "default"),
+ "line number": ("dark gray", "default"),
+ "bp_star": ("dark red", "default"),
+ })
+ else:
+ try:
+ symbols = {
+ "palette": palette_dict,
+ "add_setting": add_setting,
+ }
+ execfile(theme, symbols)
+ except:
+ print "Error when importing theme:"
+ from traceback import print_exc
+ print_exc()
+ raw_input("Hit enter:")
+
+ return [(key,)+value for key, value in palette_dict.iteritems()]
+
View
7 setup.py
@@ -70,12 +70,15 @@
python -m pudb.run my-script.py
- Documentation
- -------------
+ Documentation and Support
+ -------------------------
PuDB has a `wiki <http://wiki.tiker.net/PuDB>`_, where documentation and
debugging wisdom are collected.
+ PuDB also has a `mailing list <http://lists.tiker.net/listinfo/pudb>`_ that
+ you may use to submit patches and requests for help.
+
Programming PuDB
----------------
Please sign in to comment.
Something went wrong with that request. Please try again.