From 605d9e1c2ec223c506b954eb6ae6b819d0aa5599 Mon Sep 17 00:00:00 2001 From: "Bradley M. Froehle" Date: Fri, 13 Jul 2012 17:44:44 -0700 Subject: [PATCH] Simultaneous Python 2/3 compatibility. --- debug_me.py | 6 +++--- pudb/__init__.py | 9 +++++---- pudb/debugger.py | 29 +++++++++++++++++++---------- pudb/ipython.py | 4 ++-- pudb/lowlevel.py | 9 ++++++++- pudb/py3compat.py | 16 ++++++++++++++++ pudb/run.py | 2 +- pudb/settings.py | 16 +++++++++++----- pudb/source_view.py | 5 +++-- pudb/theme.py | 7 +++---- pudb/ui_tools.py | 10 ++++++---- pudb/var_view.py | 22 +++++++++++++++------- 12 files changed, 92 insertions(+), 43 deletions(-) create mode 100644 pudb/py3compat.py diff --git a/debug_me.py b/debug_me.py index 328fc2d..1fda077 100644 --- a/debug_me.py +++ b/debug_me.py @@ -35,9 +35,9 @@ def fermat(n): if x**n + y**n == z**n: yield x, y, z -print "SF", simple_func(10) +print("SF %s" % simple_func(10)) for i in fermat(2): - print i + print(i) -print "FINISHED" +print("FINISHED") diff --git a/pudb/__init__.py b/pudb/__init__.py index 1bb1015..50429d3 100644 --- a/pudb/__init__.py +++ b/pudb/__init__.py @@ -1,7 +1,7 @@ NUM_VERSION = (2012, 2, 1) VERSION = ".".join(str(nv) for nv in NUM_VERSION) - +from pudb.py3compat import raw_input CURRENT_DEBUGGER = [] @@ -46,14 +46,15 @@ def runscript(mainpyfile, args=None, pre_run="", steal_output=False): from subprocess import call retcode = call(pre_run, close_fds=True, shell=True) if retcode: - print "*** WARNING: pre-run process exited with code %d." % retcode + print("*** WARNING: pre-run process exited with code %d." % retcode) raw_input("[Hit Enter]") status_msg = "" try: dbg._runscript(mainpyfile) - except SystemExit, se: + except SystemExit: + se = sys.exc_info()[1] status_msg = "The debuggee exited normally with status code %s.\n\n" % se.code except: dbg.post_mortem = True @@ -149,4 +150,4 @@ def pm(): if __name__ == "__main__": - print "You now need to type 'python -m pudb.run'. Sorry." + print("You now need to type 'python -m pudb.run'. Sorry.") diff --git a/pudb/debugger.py b/pudb/debugger.py index 2029d21..0dd753e 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -4,14 +4,17 @@ from __future__ import division import urwid import bdb +import sys from pudb.settings import load_config, save_config CONFIG = load_config() save_config(CONFIG) - - - +from pudb.py3compat import PY3 +if PY3: + _next = "__next__" +else: + _next = "next" HELP_TEXT = """\ @@ -124,7 +127,10 @@ class Debugger(bdb.Bdb): if steal_output: raise NotImplementedError("output stealing") import sys - from cStringIO import StringIO + if PY3: + from io import StringIO + else: + from cStringIO import StringIO self.stolen_output = sys.stderr = sys.stdout = StringIO() sys.stdin = StringIO("") # avoid spurious hangs @@ -132,7 +138,7 @@ class Debugger(bdb.Bdb): from pudb.settings import save_breakpoints save_breakpoints([ bp - for fn, bp_lst in self.get_all_breaks().iteritems() + for fn, bp_lst in self.get_all_breaks().items() for lineno in bp_lst for bp in self.get_breaks(fn, lineno) if not bp.temporary]) @@ -280,7 +286,10 @@ class Debugger(bdb.Bdb): # user_call for details). self._wait_for_mainpyfile = 1 self.mainpyfile = self.canonic(filename) - statement = 'execfile( "%s")' % filename + if PY3: + statement = 'exec(compile(open("%s").read(), "%s", "exec"))' % (filename, filename) + else: + statement = 'execfile( "%s")' % filename self.run(statement, globals=globals_, locals=locals_) # }}} @@ -834,7 +843,7 @@ class DebuggerUI(FrameVarInfoKeeper): self.update_breakpoints() else: - raise RuntimeError, "no valid current file" + raise RuntimeError("no valid current file") def pick_module(w, size, key): from os.path import splitext @@ -1344,7 +1353,7 @@ class DebuggerUI(FrameVarInfoKeeper): from pudb import VERSION caption = [(None, - u"PuDB %s - ?:help n:next s:step into b:breakpoint o:output " + "PuDB %s - ?:help n:next s:step into b:breakpoint o:output " "t:run to cursor !:python shell" % VERSION)] @@ -1386,7 +1395,7 @@ class DebuggerUI(FrameVarInfoKeeper): lines = getlines(fname) from pudb.lowlevel import detect_encoding - source_enc, _ = detect_encoding(iter(lines).next) + source_enc, _ = detect_encoding(getattr(iter(lines), _next)) decoded_lines = [] for l in lines: @@ -1446,7 +1455,7 @@ class DebuggerUI(FrameVarInfoKeeper): def _get_bp_list(self): return [bp - for fn, bp_lst in self.debugger.get_all_breaks().iteritems() + for fn, bp_lst in self.debugger.get_all_breaks().items() for lineno in bp_lst for bp in self.debugger.get_breaks(fn, lineno) if not bp.temporary] diff --git a/pudb/ipython.py b/pudb/ipython.py index 56a0944..bd22530 100644 --- a/pudb/ipython.py +++ b/pudb/ipython.py @@ -24,7 +24,7 @@ def pudb_f_v10(self, arg): """ if not arg.strip(): - print __doc__ + print(__doc__) return from IPython.genutils import arg_split @@ -52,7 +52,7 @@ def pudb_f_v11(self, arg): # Get the running instance if not arg.strip(): - print __doc__ + print(__doc__) return from IPython.utils.process import arg_split diff --git a/pudb/lowlevel.py b/pudb/lowlevel.py index 7af2d5b..af013bb 100644 --- a/pudb/lowlevel.py +++ b/pudb/lowlevel.py @@ -1,3 +1,5 @@ +from pudb.py3compat import PY3 + # breakpoint validity --------------------------------------------------------- def generate_executable_lines_for_code(code): l = code.co_firstlineno @@ -80,6 +82,8 @@ def lookup_module(filename): import re cookie_re = re.compile("^\s*#.*coding[:=]\s*([-\w.]+)") from codecs import lookup, BOM_UTF8 +if PY3: + BOM_UTF8 = BOM_UTF8.decode() def detect_encoding(readline): """ @@ -108,7 +112,10 @@ def detect_encoding(readline): def find_cookie(line): try: - line_string = line.decode('ascii') + if PY3: + line_string = line + else: + line_string = line.decode('ascii') except UnicodeDecodeError: return None diff --git a/pudb/py3compat.py b/pudb/py3compat.py new file mode 100644 index 0000000..4595789 --- /dev/null +++ b/pudb/py3compat.py @@ -0,0 +1,16 @@ +import sys + +PY3 = sys.version_info[0] >= 3 +if PY3: + raw_input = input + xrange = range + integer_types = (int,) + string_types = (str,) + def execfile(fname, globs, locs=None): + exec(compile(open(fname).read(), fname, 'exec'), globs, locs or globs) +else: + raw_input = raw_input + xrange = xrange + integer_types = (int, long) + string_types = (basestring,) + execfile = execfile diff --git a/pudb/run.py b/pudb/run.py index 3440d54..c6fb6c6 100644 --- a/pudb/run.py +++ b/pudb/run.py @@ -19,7 +19,7 @@ def main(): mainpyfile = args[0] from os.path import exists, dirname if not exists(mainpyfile): - print 'Error:', mainpyfile, 'does not exist' + print('Error: %s does not exist' % mainpyfile) sys.exit(1) sys.argv = args diff --git a/pudb/settings.py b/pudb/settings.py index 015b176..43a1b3b 100644 --- a/pudb/settings.py +++ b/pudb/settings.py @@ -1,5 +1,11 @@ import os -from ConfigParser import ConfigParser +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 @@ -20,7 +26,7 @@ def get_save_config_path(*resource): assert not resource.startswith('/') path = os.path.join(xdg_config_home, resource) if not os.path.isdir(path): - os.makedirs(path, 0700) + os.makedirs(path, 448) # 0o700 return path # end LGPL violation @@ -90,8 +96,8 @@ def save_config(conf_dict): cparser = ConfigParser() cparser.add_section(CONF_SECTION) - for key, val in conf_dict.iteritems(): - cparser.set(CONF_SECTION, key, val) + for key, val in conf_dict.items(): + cparser.set(CONF_SECTION, key, str(val)) try: outf = open(join(get_save_config_path(), @@ -353,7 +359,7 @@ def parse_breakpoints(lines): arg = arg[colon+1:].lstrip() try: lineno = int(arg) - except ValueError, msg: + except ValueError: continue else: continue diff --git a/pudb/source_view.py b/pudb/source_view.py index 6c1a1e6..9266e8b 100644 --- a/pudb/source_view.py +++ b/pudb/source_view.py @@ -28,13 +28,14 @@ class SourceLine(urwid.FlowWidget): self.has_breakpoint = has_breakpoint self._invalidate() - def rows(self, (maxcol,), focus=False): + def rows(self, size, focus=False): return 1 - def render(self, (maxcol,), focus=False): + def render(self, size, focus=False): from pudb.debugger import CONFIG render_line_nr = CONFIG["line_numbers"] + maxcol = size[0] hscroll = self.dbg_ui.source_hscroll_start attrs = [] if self.is_current: diff --git a/pudb/theme.py b/pudb/theme.py index 7eacf03..602b872 100644 --- a/pudb/theme.py +++ b/pudb/theme.py @@ -1,7 +1,6 @@ THEMES = ["classic", "vim", "dark vim", "midnight"] - - +from pudb.py3compat import execfile, raw_input def get_palette(may_use_fancy_formats, theme="classic"): if may_use_fancy_formats: @@ -288,10 +287,10 @@ def get_palette(may_use_fancy_formats, theme="classic"): from os.path import expanduser execfile(expanduser(theme), symbols) except: - print "Error when importing theme:" + 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()] + return [(key,)+value for key, value in palette_dict.items()] diff --git a/pudb/ui_tools.py b/pudb/ui_tools.py index c148021..7bd5bdc 100644 --- a/pudb/ui_tools.py +++ b/pudb/ui_tools.py @@ -103,10 +103,11 @@ class StackFrame(urwid.FlowWidget): def selectable(self): return True - def rows(self, (maxcol,), focus=False): + def rows(self, size, focus=False): return 1 - def render(self, (maxcol,), focus=False): + def render(self, size, focus=False): + maxcol = size[0] if focus: apfx = "focused " else: @@ -143,10 +144,11 @@ class BreakpointFrame(urwid.FlowWidget): def selectable(self): return True - def rows(self, (maxcol,), focus=False): + def rows(self, size, focus=False): return 1 - def render(self, (maxcol,), focus=False): + def render(self, size, focus=False): + maxcol = size[0] if focus: apfx = "focused " else: diff --git a/pudb/var_view.py b/pudb/var_view.py index f7c2a29..a2f3a7f 100644 --- a/pudb/var_view.py +++ b/pudb/var_view.py @@ -2,6 +2,7 @@ # constants and imports ------------------------------------------------------- import urwid +import sys try: import numpy @@ -9,6 +10,13 @@ try: except ImportError: HAVE_NUMPY = 0 +from pudb.py3compat import PY3, execfile, raw_input, xrange, \ + integer_types, string_types +if PY3: + ELLIPSIS = '…' +else: + ELLIPSIS = unicode('…', 'utf-8') + from pudb.debugger import CONFIG # data ------------------------------------------------------------------------ @@ -179,7 +187,7 @@ class VariableWidget(urwid.FlowWidget): for i in xrange(len(text)): if len(text[i]) > maxcol: text[i] = (unicode(text[i][:maxcol-1]) - + unicode(u'…') + unicode(text[i][maxcol:])) + + ELLIPSIS + unicode(text[i][maxcol:])) # XXX: This doesn't work. It just gives a ? # Strangely, the following does work (it gives the … # three characters from the right): @@ -219,15 +227,15 @@ def get_stringifier(iinfo): from os.path import expanduser execfile(expanduser(iinfo.display_type), custom_stringifier_dict) except: - print "Error when importing custom stringifier:" + print("Error when importing custom stringifier:") from traceback import print_exc print_exc() raw_input("Hit enter:") return lambda value: "ERROR: Invalid custom stringifier file." else: if "pudb_stringifier" not in custom_stringifier_dict: - print "%s does not contain " % iinfo.display_type - "a function named pudb_stringifier at the module level." + print("%s does not contain a function named pudb_stringifier at" + "the module level." % iinfo.display_type) raw_input("Hit enter:") return lambda value: ("ERROR: Invalid custom stringifier file: " "pudb_stringifer not defined.") @@ -250,9 +258,9 @@ class ValueWalker: iinfo = self.frame_var_info.get_inspect_info(id_path, read_only=True) - if isinstance(value, (int, float, long, complex)): + if isinstance(value, integer_types + (float, complex)): self.add_item(prefix, label, repr(value), id_path, attr_prefix) - elif isinstance(value, (str, unicode)): + elif isinstance(value, string_types): self.add_item(prefix, label, repr(value), id_path, attr_prefix) else: try: @@ -444,7 +452,7 @@ class TopAndMainVariableWalker(ValueWalker): SEPARATOR = urwid.AttrMap(urwid.Text(""), "variable separator") def make_var_view(frame_var_info, locals, globals): - vars = locals.keys() + vars = list(locals.keys()) vars.sort(key=lambda n: n.lower()) tmv_walker = TopAndMainVariableWalker(frame_var_info)