Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
417 lines (323 sloc) 13.2 KB
import os
import sys
from pudb.py3compat import PY3
if PY3:
from configparser import ConfigParser
else:
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 get_save_config_path(*resource):
if not resource:
resource = [XDG_CONF_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, 448) # 0o700
return path
# end LGPL violation
CONF_SECTION = "pudb"
XDG_CONF_RESOURCE = "pudb"
CONF_FILE_NAME = "pudb.cfg"
SAVED_BREAKPOINTS_FILE_NAME = "saved-breakpoints"
BREAKPOINTS_FILE_NAME = "breakpoints"
def load_config():
from os.path import join, isdir
cparser = ConfigParser()
conf_dict = {}
try:
cparser.read([
join(cdir, XDG_CONF_RESOURCE, CONF_FILE_NAME)
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")
conf_dict.setdefault("sidebar_width", 0.5)
conf_dict.setdefault("variables_weight", 1)
conf_dict.setdefault("stack_weight", 1)
conf_dict.setdefault("breakpoints_weight", 1)
conf_dict.setdefault("current_stack_frame", "top")
conf_dict.setdefault("stringifier", "type")
conf_dict.setdefault("custom_theme", "")
conf_dict.setdefault("custom_stringifier", "")
conf_dict.setdefault("wrap_variables", True)
def normalize_bool_inplace(name):
try:
if conf_dict[name].lower() in ["0", "false", "off"]:
conf_dict[name] = False
else:
conf_dict[name] = True
except:
pass
normalize_bool_inplace("line_numbers")
normalize_bool_inplace("wrap_variables")
return conf_dict
def save_config(conf_dict):
from os.path import join
cparser = ConfigParser()
cparser.add_section(CONF_SECTION)
for key in sorted(conf_dict):
cparser.set(CONF_SECTION, key, str(conf_dict[key]))
try:
outf = open(join(get_save_config_path(),
CONF_FILE_NAME), "w")
cparser.write(outf)
outf.close()
except:
pass
def edit_config(ui, conf_dict):
import urwid
old_conf_dict = conf_dict.copy()
def _update_theme():
ui.setup_palette(ui.screen)
for sl in ui.source:
sl._invalidate()
def _update_line_numbers():
for sl in ui.source:
sl._invalidate()
def _update_current_stack_frame():
ui.update_stack()
def _update_stringifier():
import pudb.var_view
pudb.var_view.custom_stringifier_dict = {}
ui.update_var_view()
def _update_wrap_variables():
ui.update_var_view()
def _update_config(check_box, new_state, option_newvalue):
option, newvalue = option_newvalue
new_conf_dict = {option: newvalue}
if option == "theme":
# only activate if the new state of the radio button is 'on'
if new_state:
if newvalue is None:
# Select the custom theme entry dialog
lb.set_focus(lb_contents.index(theme_edit_list_item))
return
conf_dict.update(theme=newvalue)
_update_theme()
elif option == "line_numbers":
new_conf_dict["line_numbers"] = not check_box.get_state()
conf_dict.update(new_conf_dict)
_update_line_numbers()
elif option == "current_stack_frame":
# only activate if the new state of the radio button is 'on'
if new_state:
conf_dict.update(new_conf_dict)
_update_current_stack_frame()
elif option == "stringifier":
# only activate if the new state of the radio button is 'on'
if new_state:
if newvalue is None:
lb.set_focus(lb_contents.index(stringifier_edit_list_item))
return
conf_dict.update(stringifier=newvalue)
_update_stringifier()
elif option == "wrap_variables":
new_conf_dict["wrap_variables"] = not check_box.get_state()
conf_dict.update(new_conf_dict)
_update_wrap_variables()
heading = urwid.Text("This is the preferences screen for PuDB. "
"Hit Ctrl-P at any time to get back to it.\n\n"
"Configuration settings are saved in "
"%s.\n" % get_save_config_path())
cb_line_numbers = urwid.CheckBox("Show Line Numbers",
bool(conf_dict["line_numbers"]), on_state_change=_update_config,
user_data=("line_numbers", None))
shell_info = urwid.Text("This is the shell that will be used when you hit '!'.\n")
shells = ["classic", "ipython", "bpython"]
shell_rb_group = []
shell_rbs = [
urwid.RadioButton(shell_rb_group, name,
conf_dict["shell"] == name)
for name in shells]
from pudb.theme import THEMES
known_theme = conf_dict["theme"] in THEMES
theme_rb_group = []
theme_edit = urwid.Edit(edit_text=conf_dict["custom_theme"])
theme_edit_list_item = urwid.AttrMap(theme_edit, "value")
theme_rbs = [
urwid.RadioButton(theme_rb_group, name,
conf_dict["theme"] == name, on_state_change=_update_config,
user_data=("theme", name))
for name in THEMES]+[
urwid.RadioButton(theme_rb_group, "Custom:",
not known_theme, on_state_change=_update_config,
user_data=("theme", None)),
theme_edit_list_item,
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. '~' will be expanded to your home directory. "
"Note that a custom theme will not be applied until you close "
"this dialog."),
]
stack_rb_group = []
stack_opts = ["top", "bottom"]
stack_info = urwid.Text("Show the current stack frame at the\n")
stack_rbs = [
urwid.RadioButton(stack_rb_group, name,
conf_dict["current_stack_frame"] == name,
on_state_change=_update_config,
user_data=("current_stack_frame", name))
for name in stack_opts
]
stringifier_opts = ["type", "str", "repr"]
known_stringifier = conf_dict["stringifier"] in stringifier_opts
stringifier_rb_group = []
stringifier_edit = urwid.Edit(edit_text=conf_dict["custom_stringifier"])
stringifier_info = urwid.Text("This is the default function that will be "
"called on variables in the variables list. Note that you can change "
"this on a per-variable basis by selecting a variable and hitting Enter "
"or by typing t/s/r. Note that str and repr will be slower than type "
"and have the potential to crash PuDB.\n")
stringifier_edit_list_item = urwid.AttrMap(stringifier_edit, "value")
stringifier_rbs = [
urwid.RadioButton(stringifier_rb_group, name,
conf_dict["stringifier"] == name,
on_state_change=_update_config,
user_data=("stringifier", name))
for name in stringifier_opts
]+[
urwid.RadioButton(stringifier_rb_group, "Custom:",
not known_stringifier, on_state_change=_update_config,
user_data=("stringifier", None)),
stringifier_edit_list_item,
urwid.Text("\nTo use a custom stringifier, see example-stringifier.py "
"in the pudb distribution. Enter the full path to a file like "
"it in the box above. '~' will be expanded to your home directory. "
"The file should contain a function called pudb_stringifier() "
"at the module level, which should take a single argument and "
"return the desired string form of the object passed to it. "
"Note that if you choose a custom stringifier, the variables "
"view will not be updated until you close this dialog."),
]
cb_wrap_variables = urwid.CheckBox("Wrap variables",
bool(conf_dict["wrap_variables"]), on_state_change=_update_config,
user_data=("wrap_variables", None))
wrap_variables_info = urwid.Text("\nNote that you can change this option on "
"a per-variable basis by selecting the "
"variable and pressing 'w'.")
lb_contents =(
[heading]
+ [urwid.AttrMap(urwid.Text("Line Numbers:\n"), "group head")]
+ [cb_line_numbers]
+ [urwid.AttrMap(urwid.Text("\nShell:\n"), "group head")]
+ [shell_info]
+ shell_rbs
+ [urwid.AttrMap(urwid.Text("\nTheme:\n"), "group head")]
+ theme_rbs
+ [urwid.AttrMap(urwid.Text("\nStack Order:\n"), "group head")]
+ [stack_info]
+ stack_rbs
+ [urwid.AttrMap(urwid.Text("\nVariable Stringifier:\n"), "group head")]
+ [stringifier_info]
+ stringifier_rbs
+ [urwid.AttrMap(urwid.Text("\nWrap Variables:\n"), "group head")]
+ [cb_wrap_variables]
+ [wrap_variables_info]
)
lb = urwid.ListBox(lb_contents)
if ui.dialog(lb, [
("OK", True),
("Cancel", False),
],
title="Edit Preferences"):
# Only update the settings here that instant-apply (above) doesn't take
# care of.
# if we had a custom theme, it wasn't updated live
if theme_rb_group[-1].state:
newvalue = theme_edit.get_edit_text()
conf_dict.update(theme=newvalue, custom_theme=newvalue)
_update_theme()
# Ditto for custom stringifiers
if stringifier_rb_group[-1].state:
newvalue = stringifier_edit.get_edit_text()
conf_dict.update(stringifier=newvalue, custom_stringifier=newvalue)
_update_stringifier()
for shell, shell_rb in zip(shells, shell_rbs):
if shell_rb.get_state():
conf_dict["shell"] = shell
else: # The user chose cancel, revert changes
conf_dict.update(old_conf_dict)
_update_theme()
# _update_line_numbers() is equivalent to _update_theme()
_update_current_stack_frame()
_update_stringifier()
# {{{ breakpoint saving
def parse_breakpoints(lines):
# b [ (filename:lineno | function) [, "condition"] ]
breakpoints = []
for arg in lines:
if not arg:
continue
arg = arg[1:]
filename = None
lineno = None
cond = None
comma = arg.find(',')
if comma > 0:
# parse stuff after comma: "condition"
cond = arg[comma+1:].lstrip()
arg = arg[:comma].rstrip()
colon = arg.rfind(':')
funcname = None
if colon > 0:
filename = arg[:colon].strip()
from pudb.lowlevel import lookup_module
f = lookup_module(filename)
if not f:
continue
else:
filename = f
arg = arg[colon+1:].lstrip()
try:
lineno = int(arg)
except ValueError:
continue
else:
continue
from pudb.lowlevel import get_breakpoint_invalid_reason
if get_breakpoint_invalid_reason(filename, lineno) is None:
breakpoints.append((filename, lineno, False, cond, funcname))
return breakpoints
def load_breakpoints(dbg):
from os.path import join, isdir
file_names = [
join(cdir, XDG_CONF_RESOURCE, name)
for cdir in xdg_config_dirs if isdir(cdir)
for name in [SAVED_BREAKPOINTS_FILE_NAME, BREAKPOINTS_FILE_NAME]
]
lines = []
for fname in file_names:
try:
rcFile = open(fname)
except IOError:
pass
else:
lines.extend([l.strip() for l in rcFile.readlines()])
rcFile.close()
return parse_breakpoints(lines)
def save_breakpoints(bp_list):
"""
:arg bp_list: a list of tuples `(file_name, line)`
"""
from os.path import join
bp_histfile = join(get_save_config_path(), "saved-breakpoints")
histfile = open(bp_histfile, 'w')
bp_list = set([(bp.file, bp.line) for bp in bp_list])
for bp in bp_list:
histfile.write("b %s:%d\n" % (bp[0], bp[1]))
histfile.close()
# }}}
# vim:foldmethod=marker
Jump to Line
Something went wrong with that request. Please try again.