Hi Ian, all,

If you run the attached script with 'python pudb.py pudb.py' and hit 'h', 
you'll see a help screen that's supposed to be scrollable. For me, it scrolls 
just a little bit, but doesn't show all the text it's supposed to show.

Looks like a ListBox bug...

Andreas

PS: I'm attaching a bunch of fixes I've made against the hg version. This 
includes the prior patch.
#! /usr/bin/env python
# -*- coding: utf-8 -*-

import urwid.raw_display
import urwid
import bdb

# TODO: Breakpoint setting and deleting
# TODO: Breakpoint listing
# TODO: File open
# TODO: Stack display 
# TODO: Stack browsing
# TODO: Show stuff when exception happens
# TODO: Postmortem
# TODO: set_trace
# TODO: help dialog working




HELP_TEXT = """\
Welcome to PuDB, the Python Urwid debugger.
-------------------------------------------

Keys:
    n - next
    s - step
    c - continue
    f - finish

    v - view output

    b - breakpoint

    j/k - up/down
    g/G - start/end

License:
--------

PuDB is licensed to you under the MIT/X Consortium license:

Copyright (c) 2008 Andreas Klöckner

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
"""



# debugger interface ----------------------------------------------------------
class Debugger(bdb.Bdb):
    def __init__(self):
        bdb.Bdb.__init__(self)
        self.ui = DebuggerUI(self)

        self.mainpyfile = ''
        self._wait_for_mainpyfile = False

    def reset(self):
        bdb.Bdb.reset(self)
        self.forget()

    def forget(self):
        self.curframe = None

    def interaction(self, frame, traceback):
        self.stack, self.curindex = self.get_stack(frame, traceback)
        self.curframe = self.stack[self.curindex][0]
        self.ui.set_current_line(
                self.curframe.f_code.co_filename, 
                self.curframe.f_lineno)
        self.ui.set_locals(self.curframe.f_locals)
        self.ui.call_with_ui(self.ui.event_loop)

    def user_call(self, frame, argument_list):
        """This method is called when there is the remote possibility
        that we ever need to stop in this function."""
        if self._wait_for_mainpyfile:
            return
        if self.stop_here(frame):
            #print '--Call--'
            self.interaction(frame, None)

    def user_line(self, frame):
        """This function is called when we stop or break at this line."""
        if self._wait_for_mainpyfile:
            if (self.mainpyfile != self.canonic(frame.f_code.co_filename)
                or frame.f_lineno<= 0):
                return
            self._wait_for_mainpyfile = False
        self.interaction(frame, None)

    def user_return(self, frame, return_value):
        """This function is called when a return trap is set here."""
        frame.f_locals['__return__'] = return_value
        #print '--Return--'
        self.interaction(frame, None)

    def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
        """This function is called if an exception occurs,
        but only if we are to stop at or just below this level."""
        frame.f_locals['__exception__'] = exc_type, exc_value

        if type(exc_type) == type(''):
            exc_type_name = exc_type
        else: 
            exc_type_name = exc_type.__name__

        #print exc_type_name + ':', _saferepr(exc_value)
        self.interaction(frame, exc_traceback)

    def _runscript(self, filename):
        # Start with fresh empty copy of globals and locals and tell the script
        # that it's being run as __main__ to avoid scripts being able to access
        # the debugger's namespace.
        globals_ = {"__name__" : "__main__"}
        locals_ = globals_

        # When bdb sets tracing, a number of call and line events happens
        # BEFORE debugger even reaches user's code (and the exact sequence of
        # events depends on python version). So we take special measures to
        # avoid stopping before we reach the main script (see user_line and
        # user_call for details).
        self._wait_for_mainpyfile = 1
        self.mainpyfile = self.canonic(filename)
        statement = 'execfile( "%s")' % filename
        self.run(statement, globals=globals_, locals=locals_)





# UI stuff --------------------------------------------------------------------
class SelectableText(urwid.Text):
    def selectable(self):
        return True

    def keypress(self, size, key):
        return key




class SignalWrap(urwid.WidgetWrap):
    def __init__(self, w):
        urwid.WidgetWrap.__init__(self, w)
        self.event_listeners = []

    def listen(self, mask, handler):
        self.event_listeners.append((mask, handler))

    def keypress(self, size, key):
        for mask, handler in self.event_listeners:
            if mask is None or mask == key:
                return handler(self, size, key)

        return self._w.keypress(size, key)


class SourceLine(urwid.FlowWidget):
    def __init__(self, text):
        self.text = text
        self.has_breakpoint = False
        self.is_current = False

    def selectable(self):
        return True

    def set_current(self, is_current):
        self.is_current = is_current
        self._invalidate()

    def set_breakpoint(self, has_breakpoint):
        self.has_breakpoint = has_breakpoint
        self._invalidate()

    def rows(self, (maxcol,), focus=False):
        return 1

    def render(self, (maxcol,), focus=False):
        attrs = []
        if self.is_current:
            crnt = ">"
            attrs.append("current")
        else:
            crnt = " "

        if self.has_breakpoint:
            bp = "*"
            attrs.append("breakpoint")
        else:
            bp = " "

        line = crnt+bp+self.text

        if focus:
            attrs.append("focused")

        return urwid.TextCanvas(
                [line], 
                attr=[[(" ".join(attrs+["source"]), maxcol)]],
                maxcol=maxcol) 

    def keypress(self, size, key):
        return key




class DebuggerUI(object):
    def __init__(self, dbg):
        self.debugger = dbg
        Attr = urwid.AttrWrap

        self.source = urwid.SimpleListWalker([])
        self.source_list = urwid.ListBox(self.source)

        self.locals = urwid.SimpleListWalker([])
        self.var_list = urwid.ListBox(self.locals)

        self.stack_walker = urwid.SimpleListWalker(
                [Attr(SelectableText(fname),
                    None, "focused frame")
                    for fname in []])
        self.stack_list = urwid.ListBox(self.stack_walker)

        self.bp_walker = urwid.SimpleListWalker(
                [Attr(SelectableText(fname),
                    None, "focused breakpoint")
                    for fname in []])
        self.bp_list = urwid.ListBox(self.bp_walker)

        rhs_col = urwid.Pile([
            Attr(urwid.Pile([
                ("flow", urwid.Text("Locals:")),
                Attr(self.var_list, "variables"), 
                ]), None, "focused sidebar"),
            Attr(urwid.Pile([
                ("flow", urwid.Text("Stack:")),
                Attr(self.stack_list, "stack"), 
                ]), None, "focused sidebar"),
            Attr(urwid.Pile([
                ("flow", urwid.Text("Breakpoints:")),
                Attr(self.bp_list, "breakpoints"), 
                ]), None, "focused sidebar"),
            ])

        columns = urwid.AttrWrap(
                urwid.Columns(
                    [
                        ("weight", 3, 
                            urwid.AttrWrap(self.source_list, "source")), 
                        ("weight", 1, rhs_col), 
                        ],
                    dividechars=1),
                "background")

        instruct = urwid.Text(u"PuDB - The Python Urwid debugger - F1 for help"
                u" - © Andreas Klöckner 2008")
        header = urwid.AttrWrap(instruct, "header")
        self.top = SignalWrap(urwid.Frame(columns, header))

        # listeners -----------------------------------------------------------
        def end():
            self.quit_event_loop = True

        def next(w, size, key):
            self.debugger.set_next(self.debugger.curframe)
            end()

        def step(w, size, key):
            self.debugger.set_step()
            end()

        def view(w, size, key):
            self.screen.stop()
            raw_input("Hit Enter to return:")
            self.screen.start()

        def finish(w, size, key):
            self.debugger.set_return(self.debugger.curframe)
            end()


        def cont(w, size, key):
            self.debugger.set_continue()
            end()

        def move_home(w, size, key):
            self.source.set_focus(0)

        def move_end(w, size, key):
            self.source.set_focus(len(self.source))

        def move_down(w, size, key):
            w.keypress(size, "down")

        def move_up(w, size, key):
            w.keypress(size, "up")

        def breakpoint(w, size, key):
            if self.shown_file:
                sline, pos = self.source.get_focus()
                err = self.debugger.set_break(self.shown_file, pos+1, 
                        temporary=False, cond=None, funcname=None)
                sline.set_breakpoint(True)
                self.update_breakpoints()
            else:
                raise RuntimeError, "no valid current file"

        def quit(w, size, key):
            self.debugger.set_quit()
            end()

        def help(w, size, key):
            l = urwid.ListBox([urwid.Text(HELP_TEXT)])
            ok = Attr(urwid.Button("OK", lambda btn: end()), "button")
            w = urwid.Columns([
                l, 
                ("fixed", 10, urwid.ListBox([ok])),
                ])
            #w = urwid.Padding(w, ('fixed left', 1), ('fixed right', 0))
            w = urwid.LineBox(w)

            w = urwid.Overlay(w, self.top,
                    align="center",
                    valign="middle",
                    width=('relative', 50),
                    height=('relative', 50),
                    )
            w = Attr(w, "background")

            self.event_loop(w)

        self.top.listen("n", next)
        self.top.listen("s", step)
        self.top.listen("v", view)
        self.top.listen("f", finish)
        self.top.listen("c", cont)

        self.top.listen("j", move_down)
        self.top.listen("k", move_up)
        self.top.listen("home", move_home)
        self.top.listen("end", move_end)
        self.top.listen("g", move_home)
        self.top.listen("G", move_end)

        self.top.listen("b", breakpoint)

        self.top.listen("q", quit)
        self.top.listen("h", help)
        self.top.listen("f1", help)

        # setup ---------------------------------------------------------------
        self.screen = urwid.raw_display.Screen()
        self.setup_palette(self.screen)

        self.show_count = 0
        self.shown_file = None

        self.current_line = None

        self.quit_event_loop = False

    @staticmethod
    def setup_palette(screen):
        screen.register_palette([
            ("header", "black", "dark cyan", "standout"),

            ("source", "yellow", "dark blue", "standout"),
            ("focused source", "black", "dark green"),
            ("current source", "black", "dark cyan"),
            ("current focused source", "white", "dark cyan"),

            ("breakpoint source", "yellow", "dark red", "standout"),
            ("breakpoint focused source", "black", "dark red"),
            ("current breakpoint source", "black", "dark red"),
            ("current breakpoint focused source", "white", "dark red"),

            ("variables", "black", "dark cyan", "standout"),
            ("focused variable", "black", "dark green", "standout"),

            ("stack", "black", "dark cyan", "standout"),
            ("focused frame", "black", "dark green", "standout"),

            ("breakpoints", "black", "dark cyan", "standout"),
            ("focused breakpoints", "black", "dark green", "standout"),

            ("button", "white", "dark blue", "standout"),

            ("background", "black", "light gray", "standout"),
            ("focused sidebar", "yellow", "light gray", "standout"),
            ])
    
    # UI enter/exit -----------------------------------------------------------
    def show(self):
        if self.show_count == 0:
            self.screen.start()
        self.show_count += 1

    def hide(self):
        self.show_count -= 1
        if self.show_count == 0:
            self.screen.stop()

    def call_with_ui(self, f, *args, **kwargs):
        self.show()
        try:
            f(*args, **kwargs)
        finally:
            self.hide()

    # interaction -------------------------------------------------------------
    def event_loop(self, toplevel=None):
        prev_quit_loop = self.quit_event_loop

        try:
            if toplevel is None:
                toplevel = self.top

            self.size = self.screen.get_cols_rows()

            self.quit_event_loop = False

            while not self.quit_event_loop:
                canvas = toplevel.render(self.size, focus=True)
                self.screen.draw_screen(self.size, canvas)
                keys = self.screen.get_input()

                for k in keys:
                    if k == "window resize":
                        self.size = self.screen.get_cols_rows()
                    else:
                        toplevel.keypress(self.size, k)
        finally:
            self.quit_event_loop = prev_quit_loop

    # debugger-facing interface -----------------------------------------------
    def set_current_file(self, fname):
        if self.shown_file != fname:
            try:
                inf = open(fname)
            except IOError:
                self.source[:] = [SourceLine(fname)]
            else:
                self.source[:] = [
                        SourceLine(line.rstrip("\n"))
                        for line in inf.readlines()]
            self.shown_file = fname

            self.current_line = None

    def set_current_line(self, fname, line):
        self.set_current_file(fname)

        line = line-1
        if self.current_line is not None:
            self.current_line.set_current(False)

        if line >= 0 and line < len(self.source):
            self.current_line = self.source[line]
            self.current_line.set_current(True)
            self.source.set_focus(line)
            #self.source_list.shi('middle')

    def set_locals(self, locals):
        vars = locals.keys()
        vars.sort()

        self.locals[:] = [
                urwid.AttrWrap(
                    SelectableText("%s: %s" % (var, locals[var])), 
                    None, "focused variable")
                for var in vars
                if not var.startswith("_")]

    def update_breakpoints(self):
        # FIXME
        pass




def main():
    import sys
    if not sys.argv[1:]:
        print "usage: %s scriptfile [arg] ..." % sys.argv[0]
        sys.exit(2)

    mainpyfile =  sys.argv[1]
    from os.path import exists, dirname
    if not exists(mainpyfile):
        print 'Error:', mainpyfile, 'does not exist'
        sys.exit(1)

    # Hide "pdb.py" from argument list
    del sys.argv[0]         

    # Replace pdb's dir with script's dir in front of module search path.
    sys.path[0] = dirname(mainpyfile)

    # Note on saving/restoring sys.argv: it's a good idea when sys.argv was
    # modified by the script being debugged. It's a bad idea when it was
    # changed by the user from the command line. The best approach would be to
    # have a "restart" command which would allow explicit specification of
    # command line arguments.

    dbg = Debugger()
    dbg._runscript(mainpyfile)




if __name__=='__main__':
    main()
diff -r e8c070db8be7 graph.py
--- a/graph.py	Sun Nov 09 20:29:36 2008 -0500
+++ b/graph.py	Sat Nov 29 16:50:09 2008 -0500
@@ -195,7 +195,7 @@
 
 	def on_unicode_checkbox(self, w, state):
 		self.graph = self.bar_graph( state )
-		self.graph_wrap.w = self.graph
+		self.graph_wrap._w = self.graph
 		self.animate_progress = self.progress_bar( state )
 		self.animate_progress_wrap.w = self.animate_progress
 		
diff -r e8c070db8be7 setup.py
--- a/setup.py	Sun Nov 09 20:29:36 2008 -0500
+++ b/setup.py	Sat Nov 29 16:50:09 2008 -0500
@@ -21,8 +21,10 @@
 
 try:
 	from setuptools import setup, Extension
+        have_setuptools = True
 except ImportError:
 	from distutils.core import setup, Extension
+        have_setuptools = False
 
 import os
 
@@ -83,6 +85,10 @@
 	'packages':['urwid'],
      }
 
+if have_setuptools:
+        setup_d['zip_safe'] = False
+
+
 try:
 	True
 except:
diff -r e8c070db8be7 urwid/container.py
--- a/urwid/container.py	Sun Nov 09 20:29:36 2008 -0500
+++ b/urwid/container.py	Sat Nov 29 16:50:09 2008 -0500
@@ -143,7 +143,7 @@
 		"""Set the focus to the item in focus in the display widget."""
 		if isinstance(w, Padding):
 			# unwrap padding
-			w = w.w
+			w = w._original_widget
 		w = w.get_focus()
 		if w in self.cells:
 			self.set_focus(w)
@@ -329,7 +329,7 @@
 				bottom = maxrow-top-height
 		elif self.height_type is None:
 			# top_w is a flow widget
-			height = self.body.rows((maxcol,),focus=focus)
+			height = self.top_w.rows((maxcol,),focus=focus)
 			top, bottom =  calculate_filler( self.valign_type,
 				self.valign_amount, 'fixed', height, 
 				None, maxrow )
diff -r e8c070db8be7 urwid/decoration.py
--- a/urwid/decoration.py	Sun Nov 09 20:29:36 2008 -0500
+++ b/urwid/decoration.py	Sat Nov 29 16:50:09 2008 -0500
@@ -412,31 +412,31 @@
 		return self._original_widget.rows((maxcol-left-right,), focus=focus)
 	
 	def keypress(self, size, key):
-		"""Pass keypress to self.w."""
+		"""Pass keypress to self._original_widget."""
 		maxcol = size[0]
 		left, right = self.padding_values(size, True)
 		maxvals = (maxcol-left-right,)+size[1:] 
-		return self.w.keypress(maxvals, key)
+		return self._original_widget.keypress(maxvals, key)
 
 	def get_cursor_coords(self,size):
-		"""Return the (x,y) coordinates of cursor within self.w."""
-		if not hasattr(self.w,'get_cursor_coords'):
+		"""Return the (x,y) coordinates of cursor within self._original_widget."""
+		if not hasattr(self._original_widget,'get_cursor_coords'):
 			return None
 		left, right = self.padding_values(size, True)
 		maxcol = size[0]
 		maxvals = (maxcol-left-right,)+size[1:] 
-		coords = self.w.get_cursor_coords(maxvals)
+		coords = self._original_widget.get_cursor_coords(maxvals)
 		if coords is None: 
 			return None
 		x, y = coords
 		return x+left, y
 
 	def move_cursor_to_coords(self, size, x, y):
-		"""Set the cursor position with (x,y) coordinates of self.w.
+		"""Set the cursor position with (x,y) coordinates of self._original_widget.
 
 		Returns True if move succeeded, False otherwise.
 		"""
-		if not hasattr(self.w,'move_cursor_to_coords'):
+		if not hasattr(self._original_widget,'move_cursor_to_coords'):
 			return True
 		left, right = self.padding_values(size, True)
 		maxcol = size[0]
@@ -447,29 +447,29 @@
 			elif x >= maxcol-right: 
 				x = maxcol-right-1
 			x -= left
-		return self.w.move_cursor_to_coords(maxvals, x, y)
+		return self._original_widget.move_cursor_to_coords(maxvals, x, y)
 	
 	def mouse_event(self, size, event, button, x, y, focus):
-		"""Send mouse event if position is within self.w."""
-		if not hasattr(self.w,'mouse_event'):
+		"""Send mouse event if position is within self._original_widget."""
+		if not hasattr(self._original_widget,'mouse_event'):
 			return False
 		left, right = self.padding_values(size, focus)
 		maxcol = size[0]
 		if x < left or x >= maxcol-right: 
 			return False
 		maxvals = (maxcol-left-right,)+size[1:] 
-		return self.w.mouse_event(maxvals, event, button, x-left, y,
+		return self._original_widget.mouse_event(maxvals, event, button, x-left, y,
 			focus)
 		
 
 	def get_pref_col(self, size):
-		"""Return the preferred column from self.w, or None."""
-		if not hasattr(self.w,'get_pref_col'):
+		"""Return the preferred column from self._original_widget, or None."""
+		if not hasattr(self._original_widget,'get_pref_col'):
 			return None
 		left, right = self.padding_values(size, True)
 		maxcol = size[0]
 		maxvals = (maxcol-left-right,)+size[1:] 
-		x = self.w.get_pref_col(maxvals)
+		x = self._original_widget.get_pref_col(maxvals)
 		if type(x) == type(0):
 			return x+left
 		return x
diff -r e8c070db8be7 urwid/display_common.py
--- a/urwid/display_common.py	Sun Nov 09 20:29:36 2008 -0500
+++ b/urwid/display_common.py	Sat Nov 29 16:50:09 2008 -0500
@@ -478,7 +478,7 @@
         if self.colours > colours:
             raise AttrSpecError(('foreground/background (%s/%s) require ' +
                 'more colours than have been specified (%d).') %
-                (repr(foreground), repr(background), colours))
+                (repr(fg), repr(bg), colours))
 
     foreground_basic = property(lambda s: s._value & _FG_BASIC_COLOUR != 0)
     foreground_high = property(lambda s: s._value & _FG_HIGH_COLOUR != 0)
diff -r e8c070db8be7 urwid/listbox.py
--- a/urwid/listbox.py	Sun Nov 09 20:29:36 2008 -0500
+++ b/urwid/listbox.py	Sat Nov 29 16:50:09 2008 -0500
@@ -385,6 +385,7 @@
 			('fixed bottom', rows)
 			('relative', percentage 0=top 100=bottom)
 		"""
+                from urwid.decoration import decompose_valign_height
 		vt,va,ht,ha=decompose_valign_height(valign,None,ListBoxError)
 		self.set_focus_valign_pending = vt,va
 

Attachment: signature.asc
Description: This is a digitally signed message part.

_______________________________________________
Urwid mailing list
[email protected]
http://lists.excess.org/mailman/listinfo/urwid

Reply via email to