Commits

Rob Lanphier committed aa962c3

Initial patch series

Comments (0)

Files changed (9)

+urwid1
+urwid2
+urwid3
+urwid4
+urwid5
+urwid6
+urwid7
+urwid8
+# HG changeset patch
+# Parent 29f1a7a640ab3abadb31cc44bc0a32af5606252c
+moving NoDefault to its own file for now
+
+diff --git a/paste/script/command.py b/paste/script/command.py
+--- a/paste/script/command.py
++++ b/paste/script/command.py
+@@ -14,6 +14,11 @@
+     import subprocess
+ except ImportError:
+     from paste.script.util import subprocess24 as subprocess
++
++import pastevars
++
++NoDefault = pastevars.NoDefault
++    
+ difflib = None
+ 
+ if sys.version_info >= (2, 6):
+@@ -46,9 +51,6 @@
+     # precedence.
+     message = property(_get_message, _set_message)
+ 
+-class NoDefault(object):
+-    pass
+-
+ dist = pkg_resources.get_distribution('PasteScript')
+ 
+ python_version = sys.version.splitlines()[0].strip()
+diff --git a/paste/script/pastevars.py b/paste/script/pastevars.py
+new file mode 100644
+--- /dev/null
++++ b/paste/script/pastevars.py
+@@ -0,0 +1,44 @@
++# (c) 2005-2010 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
++# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
++
++class NoDefault(object):
++    pass
++
++class var(object):
++
++    def __init__(self, name, description,
++                 default='', should_echo=True):
++        self.name = name
++        self.description = description
++        self.default = default
++        self.should_echo = should_echo
++
++    def __repr__(self):
++        return '<%s %s default=%r should_echo=%s>' % (
++            self.__class__.__name__,
++            self.name, self.default, self.should_echo)
++
++    def full_description(self):
++        if self.description:
++            return '%s (%s)' % (self.name, self.description)
++        else:
++            return self.name
++
++    def print_vars(cls, vars, indent=0):
++        max_name = max([len(v.name) for v in vars])
++        for var in vars:
++            if var.description:
++                print '%s%s%s  %s' % (
++                    ' '*indent,
++                    var.name,
++                    ' '*(max_name-len(var.name)),
++                    var.description)
++            else:
++                print '  %s' % var.name
++            if var.default is not command.NoDefault:
++                print '      default: %r' % var.default
++            if var.should_echo is True:
++                print '      should_echo: %s' % var.should_echo
++        print
++
++    print_vars = classmethod(print_vars)
+diff --git a/paste/script/templates.py b/paste/script/templates.py
+--- a/paste/script/templates.py
++++ b/paste/script/templates.py
+@@ -5,9 +5,12 @@
+ import inspect
+ import copydir
+ import command
++import pastevars
+ 
+ from paste.util.template import paste_script_template_renderer
+ 
++var = pastevars.var
++
+ class Template(object):
+ 
+     # Subclasses must define:
+@@ -142,46 +145,6 @@
+         """
+         pass
+ 
+-NoDefault = command.NoDefault
+-
+-class var(object):
+-
+-    def __init__(self, name, description,
+-                 default='', should_echo=True):
+-        self.name = name
+-        self.description = description
+-        self.default = default
+-        self.should_echo = should_echo
+-
+-    def __repr__(self):
+-        return '<%s %s default=%r should_echo=%s>' % (
+-            self.__class__.__name__,
+-            self.name, self.default, self.should_echo)
+-
+-    def full_description(self):
+-        if self.description:
+-            return '%s (%s)' % (self.name, self.description)
+-        else:
+-            return self.name
+-
+-    def print_vars(cls, vars, indent=0):
+-        max_name = max([len(v.name) for v in vars])
+-        for var in vars:
+-            if var.description:
+-                print '%s%s%s  %s' % (
+-                    ' '*indent,
+-                    var.name,
+-                    ' '*(max_name-len(var.name)),
+-                    var.description)
+-            else:
+-                print '  %s' % var.name
+-            if var.default is not command.NoDefault:
+-                print '      default: %r' % var.default
+-            if var.should_echo is True:
+-                print '      should_echo: %s' % var.should_echo
+-        print
+-
+-    print_vars = classmethod(print_vars)
+ 
+ class BasicPackage(Template):
+ 
+# HG changeset patch
+# Parent c549a67072fb299fb7385fec1057fdcd7b042851
+Created new UserInterface class, with BasicUI as first method
+
+diff --git a/paste/script/command.py b/paste/script/command.py
+--- a/paste/script/command.py
++++ b/paste/script/command.py
+@@ -16,6 +16,7 @@
+     from paste.script.util import subprocess24 as subprocess
+ 
+ import pastevars
++import userinterface
+ 
+ NoDefault = pastevars.NoDefault
+     
+@@ -149,6 +150,7 @@
+ 
+     def __init__(self, name):
+         self.command_name = name
++        self.ui = userinterface.get_user_interface()
+ 
+     max_args = None
+     max_args_error = 'You must provide no more than %(max_args)s arguments'
+@@ -308,22 +310,8 @@
+         """
+         Prompt the user for a variable.
+         """
+-        if default is not NoDefault:
+-            prompt += ' [%r]' % default
+-        prompt += ': '
+-        while 1:
+-            if should_echo:
+-                prompt_method = raw_input
+-            else:
+-                prompt_method = getpass.getpass
+-            response = prompt_method(prompt).strip()
+-            if not response:
+-                if default is not NoDefault:
+-                    return default
+-                else:
+-                    continue
+-            else:
+-                return response
++        return self.ui.challenge(prompt, default=default, should_echo=should_echo)
++
+         
+     def pad(self, s, length, dir='left'):
+         if len(s) >= length:
+diff --git a/paste/script/userinterface.py b/paste/script/userinterface.py
+new file mode 100644
+--- /dev/null
++++ b/paste/script/userinterface.py
+@@ -0,0 +1,43 @@
++# (c) 2005-2010 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
++# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
++
++from pastevars import NoDefault
++
++def get_user_interface():
++    """
++    Class factory for UserInterface and friends
++    """
++    return BasicUI()
++
++
++class UserInterface(object):
++    """
++    Abstract base class for all user interface classes
++    """
++    pass
++
++
++class BasicUI(UserInterface):    
++    def challenge(self, prompt, default=NoDefault, should_echo=True):
++        """
++        Prompt the user for a variable.
++        """
++        if default is not NoDefault:
++            prompt += ' [%r]' % default
++        prompt += ': '
++        while 1:
++            if should_echo:
++                prompt_method = raw_input
++            else:
++                prompt_method = getpass.getpass
++            response = prompt_method(prompt).strip()
++            if not response:
++                if default is not NoDefault:
++                    return default
++                else:
++                    continue
++            else:
++                return response
++
++
++
+# HG changeset patch
+# Parent 60d93a96e3c1a4971d27b6e9c4b2e95f7cbf0302
+Implementing challenge_batch in BasicUI
+
+diff --git a/paste/script/command.py b/paste/script/command.py
+--- a/paste/script/command.py
++++ b/paste/script/command.py
+@@ -312,7 +312,12 @@
+         """
+         return self.ui.challenge(prompt, default=default, should_echo=should_echo)
+ 
+-        
++    def challenge_batch(self, challenges):
++        """
++        Prompt the user for a set of variables
++        """
++        return self.ui.challenge_batch(challenges)
++
+     def pad(self, s, length, dir='left'):
+         if len(s) >= length:
+             return s
+diff --git a/paste/script/templates.py b/paste/script/templates.py
+--- a/paste/script/templates.py
++++ b/paste/script/templates.py
+@@ -69,12 +69,12 @@
+         converted_vars = {}
+         unused_vars = vars.copy()
+         errors = []
++        if cmd.interactive:
++            challenges = []
+         for var in expect_vars:
+             if var.name not in unused_vars:
+                 if cmd.interactive:
+-                    prompt = 'Enter %s' % var.full_description()
+-                    response = cmd.challenge(prompt, var.default, var.should_echo)
+-                    converted_vars[var.name] = response
++                    challenges.append(var)
+                 elif var.default is command.NoDefault:
+                     errors.append('Required variable missing: %s'
+                                   % var.full_description())
+@@ -82,6 +82,9 @@
+                     converted_vars[var.name] = var.default
+             else:
+                 converted_vars[var.name] = unused_vars.pop(var.name)
++        if cmd.interactive:
++            converted_vars = cmd.challenge_batch(challenges)
++        
+         if errors:
+             raise command.BadCommand(
+                 'Errors in variables:\n%s' % '\n'.join(errors))
+diff --git a/paste/script/userinterface.py b/paste/script/userinterface.py
+--- a/paste/script/userinterface.py
++++ b/paste/script/userinterface.py
+@@ -1,7 +1,7 @@
+ # (c) 2005-2010 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
+ # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
+ 
+-from pastevars import NoDefault
++from pastevars import NoDefault, var
+ 
+ def get_user_interface():
+     """
+@@ -39,5 +39,15 @@
+             else:
+                 return response
+ 
++    def challenge_batch(self, challenges):
++        """
++        Return a series of responses
++        """
++        converted_vars = {}
++        for var in challenges:
++            prompt = 'Enter %s' % var.full_description()
++            response = self.challenge(prompt, var.default, var.should_echo)
++            converted_vars[var.name] = response
++        return converted_vars
+ 
+ 
+# HG changeset patch
+# Parent 3ae996a49ffa1fe16fe25b176212e282e7c120e6
+Added UrwidDialog implementation, and added display_vars to UserInterface class
+
+diff --git a/paste/script/create_distro.py b/paste/script/create_distro.py
+--- a/paste/script/create_distro.py
++++ b/paste/script/create_distro.py
+@@ -286,13 +286,7 @@
+         return self._entry_points
+ 
+     def display_vars(self, vars):
+-        vars = vars.items()
+-        vars.sort()
+-        print 'Variables:'
+-        max_var = max([len(n) for n, v in vars])
+-        for name, value in vars:
+-            print '  %s:%s  %s' % (
+-                name, ' '*(max_var-len(name)), value)
++        self.ui.display_vars(vars)
+         
+     def list_templates(self):
+         templates = []
+diff --git a/paste/script/userinterface.py b/paste/script/userinterface.py
+--- a/paste/script/userinterface.py
++++ b/paste/script/userinterface.py
+@@ -3,11 +3,28 @@
+ 
+ from pastevars import NoDefault, var
+ 
++try:
++    import urwid
++    import urwid.raw_display
++except ImportError:
++    pass
++
++
+ def get_user_interface():
+     """
+     Class factory for UserInterface and friends
+     """
+-    return BasicUI()
++    try:
++        import urwid
++        import urwid.raw_display
++        has_urwid = True
++    except ImportError:
++        has_urwid = False
++    
++    if has_urwid:
++        return UrwidUI()
++    else:
++        return BasicUI()
+ 
+ 
+ class UserInterface(object):
+@@ -22,6 +39,8 @@
+         """
+         Prompt the user for a variable.
+         """
++        
++        
+         if default is not NoDefault:
+             prompt += ' [%r]' % default
+         prompt += ': '
+@@ -50,4 +69,219 @@
+             converted_vars[var.name] = response
+         return converted_vars
+ 
++    def display_vars(self, vars):
++        vars = vars.items()
++        vars.sort()
++        print 'Variables:'
++        max_var = max([len(n) for n, v in vars])
++        for name, value in vars:
++            print '  %s:%s  %s' % (
++                name, ' '*(max_var-len(name)), value)
+ 
++
++class UrwidUI(UserInterface):    
++    def challenge(self, prompt, default=NoDefault, should_echo=True):
++        """
++        Prompt the user for a variable.
++        """
++        if default is not NoDefault:
++            prompt += ' [%r]' % default
++        prompt += ': '
++        while 1:
++            if should_echo:
++                prompt_method = raw_input
++            else:
++                prompt_method = getpass.getpass
++            response = prompt_method(prompt).strip()
++            if not response:
++                if default is not NoDefault:
++                    return default
++                else:
++                    continue
++            else:
++                return response
++
++    def challenge_batch(self, challenges):
++        """
++        Return a series of responses
++        """
++        dialog = UrwidDialog(challenges)
++        return dialog.run()
++
++    def display_vars(self, vars):
++        vars = vars.items()
++        vars.sort()
++        print 'Variables:'
++        max_var = max([len(n) for n, v in vars])
++        for name, value in vars:
++            print '  %s:%s  %s' % (
++                name, ' '*(max_var-len(name)), value)
++        raw_input("Press enter to continue...")
++                
++
++class UrwidDialogException(Exception):
++    pass
++
++
++class ExitUrwidUI(Exception):
++    def __init__(self, exit_token=None):
++        self.exit_token = exit_token
++
++
++class UrwidDialog(object):
++    """
++    Takes a set of fields, displays a dialog corresponding to those fields, and
++    returns the values corresponding to those fields.
++    """
++    def __init__(self, challenges):
++        self.getters = {}
++        self.challenges = challenges
++
++    def run(self):
++        """
++        Invoke Urwid MainLoop to display the dialog, populating self.getters
++        """
++
++        #  Our main loop is going to need four things: 
++        #  1. frame - the UI with all of its widgets
++        #  2. palette - style information for the UI
++        #  3. screen - the engine used to render everything
++        
++        #  1. frame - the UI with all of its widgets
++        header = self._get_dialog_header()
++        body = self._get_dialog_body()
++        frame = urwid.Frame(body, header=header)
++
++        #  2. palette - style information for the UI
++        palette = [
++            ('body','black','white', 'standout'),
++            ('header','black','light gray', 'bold'),
++            ('labelfocus','black', 'white', 'bold, underline'),
++            ('label','dark blue', 'white'),
++            ('fieldfocus','black,underline', 'white', 'bold, underline'),
++            ('field','black', 'white'),
++            ('button','black','white'),
++            ('buttonfocus','black','light gray','bold'),
++            ]
++
++        #  3. screen - the engine used to render everything
++        screen = urwid.raw_display.Screen()
++
++        # Putting it all together and running it
++        try:
++            urwid.MainLoop(frame, palette, screen).run()
++        except ExitUrwidUI as inst:
++            self.exit_token = inst.exit_token
++            if self.exit_token == 'cancel':
++                raise
++        return self._get_value_dict()
++
++    def _get_value_dict(self):
++        """
++        Dump everything we've got.
++        """
++        retval = {}
++        for key in self.getters:
++            retval[key] = self.getters[key]()
++        return retval
++
++    def _set_getter(self, name, function):
++        """ 
++        This is where we collect all of the field getter functions.
++        """
++        self.getters[name] = function
++        
++    def _get_field(self, challenge):
++        """ Build a field in our form.  Called from _get_dialog_body()"""
++        # we don't have hanging indent, but we can stick a bullet out into the 
++        # left column.
++        asterisk = urwid.Text(('label', '* '))
++        label = urwid.Text(('label', challenge.full_description()))
++        colon = urwid.Text(('label', ': '))
++
++        if isinstance(challenge.default, str):
++            field = urwid.Edit('', challenge.default)
++            def getter():
++                """ 
++                Closure around urwid.Edit.get_edit_text(), which we'll
++                use to scrape the value out when we're all done.
++                """
++                return field.get_edit_text()
++            self._set_getter(challenge.name, getter)
++        elif isinstance(challenge.default, bool):
++            field = urwid.CheckBox('')
++            def getter():
++                """ 
++                Closure around urwid.CheckBox.get_state(), which we'll
++                use to scrape the value out when we're all done. 
++                """
++                return field.get_state()
++            self._set_getter(challenge.name, getter)
++        else:
++            raise UrwidDialogException("")
++
++
++        field = urwid.AttrWrap(field, 'field', 'fieldfocus')
++
++        # put everything together.  Each column is either 'fixed' for a fixed width,
++        # or given a 'weight' to help determine the relative width of the column
++        # such that it can fill the row.
++        editwidget = urwid.Columns([('fixed', 2, asterisk),
++                                    ('weight', 3, label),
++                                    ('fixed', 2, colon),
++                                    ('weight', 4, field)])
++
++        wrapper = urwid.AttrWrap(editwidget, None, {'label':'labelfocus'})
++        return urwid.Padding(wrapper, ('fixed left', 3), ('fixed right', 3))
++
++    def _get_buttons(self):
++        """ renders the ok and cancel buttons.  Called from get_body() """
++
++        # this is going to be what we actually do when someone clicks the button
++        def ok_button_callback(button):
++            raise ExitUrwidUI(exit_token='ok')
++
++        # leading spaces to center it....seems like there should be a better way
++        b = urwid.Button('  OK', on_press=ok_button_callback)
++        okbutton = urwid.AttrWrap(b, 'button', 'buttonfocus')
++
++        # second verse, same as the first....
++        def cancel_button_callback(button):
++            raise ExitUrwidUI(exit_token='cancel')
++
++        b = urwid.Button('Cancel', on_press=cancel_button_callback)
++        cancelbutton = urwid.AttrWrap(b, 'button', 'buttonfocus')
++
++        return urwid.GridFlow([okbutton, cancelbutton], 10, 7, 1, 'center')
++
++    def _get_dialog_header(self):
++        """ the header of our form, called from run() """
++        text_header = ("'paster create' Configuration"
++            " - Use arrow keys to select a field to edit, select 'OK'"
++            " when finished or select 'Cancel' to exit")
++        header = urwid.Text(text_header)
++        return urwid.AttrWrap(header, 'header')
++
++    def _get_dialog_body(self):
++        """ the body of our form, called from run() """
++
++        # build the list of field widgets
++        fieldwidgets = [urwid.Divider(bottom=2)]
++        for chal in self.challenges:
++            fieldwidgets.append(self._get_field(chal))
++        
++        fieldwidgets.append(urwid.Divider(bottom=1)) 
++
++        fieldwidgets.append(self._get_buttons())
++
++        # SimpleListWalker provides simple linear navigation between the widgets
++        listwalker = urwid.SimpleListWalker(fieldwidgets)
++        
++        # ListBox is a scrollable frame around a list of elements
++        listbox = urwid.ListBox(listwalker)
++        return urwid.AttrWrap(listbox, 'body')
++
++
++if '__main__'==__name__:
++    main()
++
+# HG changeset patch
+# Parent 98a06a776240f554a81e07e8a1bd2fa5fc85248e
+Split out UrwidFieldSet from UrwidDialog
+
+diff --git a/paste/script/userinterface.py b/paste/script/userinterface.py
+--- a/paste/script/userinterface.py
++++ b/paste/script/userinterface.py
+@@ -105,8 +105,10 @@
+         """
+         Return a series of responses
+         """
+-        dialog = UrwidDialog(challenges)
+-        return dialog.run()
++        fieldset = UrwidFieldSet(challenges)
++        dialog = UrwidDialog(fieldset)
++        dialog.run()
++        return fieldset.get_value_dict()
+ 
+     def display_vars(self, vars):
+         vars = vars.items()
+@@ -128,7 +130,7 @@
+         self.exit_token = exit_token
+ 
+ 
+-class UrwidDialog(object):
++class UrwidFieldSet(object):
+     """
+     Takes a set of fields, displays a dialog corresponding to those fields, and
+     returns the values corresponding to those fields.
+@@ -137,46 +139,7 @@
+         self.getters = {}
+         self.challenges = challenges
+ 
+-    def run(self):
+-        """
+-        Invoke Urwid MainLoop to display the dialog, populating self.getters
+-        """
+-
+-        #  Our main loop is going to need four things: 
+-        #  1. frame - the UI with all of its widgets
+-        #  2. palette - style information for the UI
+-        #  3. screen - the engine used to render everything
+-        
+-        #  1. frame - the UI with all of its widgets
+-        header = self._get_dialog_header()
+-        body = self._get_dialog_body()
+-        frame = urwid.Frame(body, header=header)
+-
+-        #  2. palette - style information for the UI
+-        palette = [
+-            ('body','black','white', 'standout'),
+-            ('header','black','light gray', 'bold'),
+-            ('labelfocus','black', 'white', 'bold, underline'),
+-            ('label','dark blue', 'white'),
+-            ('fieldfocus','black,underline', 'white', 'bold, underline'),
+-            ('field','black', 'white'),
+-            ('button','black','white'),
+-            ('buttonfocus','black','light gray','bold'),
+-            ]
+-
+-        #  3. screen - the engine used to render everything
+-        screen = urwid.raw_display.Screen()
+-
+-        # Putting it all together and running it
+-        try:
+-            urwid.MainLoop(frame, palette, screen).run()
+-        except ExitUrwidUI as inst:
+-            self.exit_token = inst.exit_token
+-            if self.exit_token == 'cancel':
+-                raise
+-        return self._get_value_dict()
+-
+-    def _get_value_dict(self):
++    def get_value_dict(self):
+         """
+         Dump everything we've got.
+         """
+@@ -234,6 +197,59 @@
+         wrapper = urwid.AttrWrap(editwidget, None, {'label':'labelfocus'})
+         return urwid.Padding(wrapper, ('fixed left', 3), ('fixed right', 3))
+ 
++    def get_widgets(self):
++        fieldwidgets = []
++        for chal in self.challenges:
++            fieldwidgets.append(self._get_field(chal))
++        return fieldwidgets
++
++
++class UrwidDialog(object):
++    """
++    Takes a set of fields, displays a dialog corresponding to those fields, and
++    returns the values corresponding to those fields.
++    """
++    def __init__(self, bodyobj):
++        self.bodyobj = bodyobj
++
++    def run(self):
++        """
++        Invoke Urwid MainLoop to display the dialog, populating self.getters
++        """
++
++        #  Our main loop is going to need four things: 
++        #  1. frame - the UI with all of its widgets
++        #  2. palette - style information for the UI
++        #  3. screen - the engine used to render everything
++        
++        #  1. frame - the UI with all of its widgets
++        header = self._get_dialog_header()
++        body = self._get_dialog_body()
++        frame = urwid.Frame(body, header=header)
++
++        #  2. palette - style information for the UI
++        palette = [
++            ('body','black','white', 'standout'),
++            ('header','black','light gray', 'bold'),
++            ('labelfocus','black', 'white', 'bold, underline'),
++            ('label','dark blue', 'white'),
++            ('fieldfocus','black,underline', 'white', 'bold, underline'),
++            ('field','black', 'white'),
++            ('button','black','white'),
++            ('buttonfocus','black','light gray','bold'),
++            ]
++
++        #  3. screen - the engine used to render everything
++        screen = urwid.raw_display.Screen()
++
++        # Putting it all together and running it
++        try:
++            urwid.MainLoop(frame, palette, screen).run()
++        except ExitUrwidUI as inst:
++            self.exit_token = inst.exit_token
++            if self.exit_token == 'cancel':
++                raise
++
+     def _get_buttons(self):
+         """ renders the ok and cancel buttons.  Called from get_body() """
+ 
+@@ -266,16 +282,16 @@
+         """ the body of our form, called from run() """
+ 
+         # build the list of field widgets
+-        fieldwidgets = [urwid.Divider(bottom=2)]
+-        for chal in self.challenges:
+-            fieldwidgets.append(self._get_field(chal))
++        bodywidgets = [urwid.Divider(bottom=2)]
++
++        bodywidgets.extend(self.bodyobj.get_widgets())
+         
+-        fieldwidgets.append(urwid.Divider(bottom=1)) 
++        bodywidgets.append(urwid.Divider(bottom=1)) 
+ 
+-        fieldwidgets.append(self._get_buttons())
++        bodywidgets.append(self._get_buttons())
+ 
+         # SimpleListWalker provides simple linear navigation between the widgets
+-        listwalker = urwid.SimpleListWalker(fieldwidgets)
++        listwalker = urwid.SimpleListWalker(bodywidgets)
+         
+         # ListBox is a scrollable frame around a list of elements
+         listbox = urwid.ListBox(listwalker)
+# HG changeset patch
+# Parent 003373390283ba3aed8355d055659dee22b78c53
+Created UrwidDictDisplay and used it to implement display_vars
+
+diff --git a/paste/script/userinterface.py b/paste/script/userinterface.py
+--- a/paste/script/userinterface.py
++++ b/paste/script/userinterface.py
+@@ -113,13 +113,10 @@
+     def display_vars(self, vars):
+         vars = vars.items()
+         vars.sort()
+-        print 'Variables:'
+-        max_var = max([len(n) for n, v in vars])
+-        for name, value in vars:
+-            print '  %s:%s  %s' % (
+-                name, ' '*(max_var-len(name)), value)
+-        raw_input("Press enter to continue...")
+-                
++        bodyobj = UrwidDictDisplay(vars)
++        dialog = UrwidDialog(bodyobj)
++        dialog.run()
++
+ 
+ class UrwidDialogException(Exception):
+     pass
+@@ -130,6 +127,36 @@
+         self.exit_token = exit_token
+ 
+ 
++class UrwidDictDisplay(object):
++    def __init__(self, showdict):
++        self.showdict = showdict
++
++    def _get_field(self, name, value):
++        """ Display one name/value pair from the dict """
++        # we don't have hanging indent, but we can stick a bullet out into the 
++        # left column.
++        asterisk = urwid.Text(('label', '* '))
++        label = urwid.Text(('label', name))
++        colon = urwid.Text(('label', ': '))
++        field = urwid.Text(('field', value))
++
++        # put everything together.  Each column is either 'fixed' for a fixed width,
++        # or given a 'weight' to help determine the relative width of the column
++        # such that it can fill the row.
++        row = urwid.Columns([('fixed', 2, asterisk),
++                             ('weight', 3, label),
++                             ('fixed', 2, colon),
++                             ('weight', 4, field)])
++
++        return urwid.Padding(row, ('fixed left', 3), ('fixed right', 3))
++
++    def get_widgets(self):
++        widgets = []
++        for name, value in self.showdict:
++            widgets.append(self._get_field(name, value))
++        return widgets
++
++
+ class UrwidFieldSet(object):
+     """
+     Takes a set of fields, displays a dialog corresponding to those fields, and
+# HG changeset patch
+# Parent 5a482f49174e54912387ce4d24c62b87f8b82e0e
+Made header_text more flexible
+
+diff --git a/paste/script/userinterface.py b/paste/script/userinterface.py
+--- a/paste/script/userinterface.py
++++ b/paste/script/userinterface.py
+@@ -39,8 +39,6 @@
+         """
+         Prompt the user for a variable.
+         """
+-        
+-        
+         if default is not NoDefault:
+             prompt += ' [%r]' % default
+         prompt += ': '
+@@ -58,7 +56,7 @@
+             else:
+                 return response
+ 
+-    def challenge_batch(self, challenges):
++    def challenge_batch(self, challenges, title=None):
+         """
+         Return a series of responses
+         """
+@@ -69,7 +67,7 @@
+             converted_vars[var.name] = response
+         return converted_vars
+ 
+-    def display_vars(self, vars):
++    def display_vars(self, vars, title=None):
+         vars = vars.items()
+         vars.sort()
+         print 'Variables:'
+@@ -101,20 +99,23 @@
+             else:
+                 return response
+ 
+-    def challenge_batch(self, challenges):
++    def challenge_batch(self, challenges, 
++                        title="'paster create' Configuration"):
+         """
+         Return a series of responses
+         """
+         fieldset = UrwidFieldSet(challenges)
+         dialog = UrwidDialog(fieldset)
++        dialog.header_text = title
+         dialog.run()
+         return fieldset.get_value_dict()
+ 
+-    def display_vars(self, vars):
++    def display_vars(self, vars, title="Variables:"):
+         vars = vars.items()
+         vars.sort()
+         bodyobj = UrwidDictDisplay(vars)
+         dialog = UrwidDialog(bodyobj)
++        dialog.header_text = title
+         dialog.run()
+ 
+ 
+@@ -238,6 +239,7 @@
+     """
+     def __init__(self, bodyobj):
+         self.bodyobj = bodyobj
++        self.header_text = "'paster create' Configuration"
+ 
+     def run(self):
+         """
+@@ -299,10 +301,9 @@
+ 
+     def _get_dialog_header(self):
+         """ the header of our form, called from run() """
+-        text_header = ("'paster create' Configuration"
+-            " - Use arrow keys to select a field to edit, select 'OK'"
+-            " when finished or select 'Cancel' to exit")
+-        header = urwid.Text(text_header)
++        header_text = self.header_text
++        header_text += " (select 'OK' to continue, 'Cancel' to abort)"
++        header = urwid.Text(header_text)
+         return urwid.AttrWrap(header, 'header')
+ 
+     def _get_dialog_body(self):
+# HG changeset patch
+# Parent fcc1d60e69ed4f9bcda55969acaf4b1347ddaead
+Made UrwidUI.challenge work a lot better
+
+diff --git a/paste/script/pastevars.py b/paste/script/pastevars.py
+--- a/paste/script/pastevars.py
++++ b/paste/script/pastevars.py
+@@ -7,11 +7,19 @@
+ class var(object):
+ 
+     def __init__(self, name, description,
+-                 default='', should_echo=True):
++                 default='', should_echo=True, vartype=None):
+         self.name = name
+         self.description = description
+         self.default = default
+         self.should_echo = should_echo
++        if vartype is not None:
++            self.type = vartype
++        else:
++            if default is NoDefault or default is None:
++                self.type = str
++            else:
++                self.type = type(default)
++        
+ 
+     def __repr__(self):
+         return '<%s %s default=%r should_echo=%s>' % (
+diff --git a/paste/script/userinterface.py b/paste/script/userinterface.py
+--- a/paste/script/userinterface.py
++++ b/paste/script/userinterface.py
+@@ -82,15 +82,10 @@
+         """
+         Prompt the user for a variable.
+         """
+-        if default is not NoDefault:
+-            prompt += ' [%r]' % default
+-        prompt += ': '
++        
+         while 1:
+-            if should_echo:
+-                prompt_method = raw_input
+-            else:
+-                prompt_method = getpass.getpass
+-            response = prompt_method(prompt).strip()
++            chals = [var('variable', prompt, default=default, should_echo=should_echo)]
++            response = self.challenge_batch(chals, use_names=False)['variable']
+             if not response:
+                 if default is not NoDefault:
+                     return default
+@@ -100,11 +95,12 @@
+                 return response
+ 
+     def challenge_batch(self, challenges, 
+-                        title="'paster create' Configuration"):
++                        title="'paster create' Configuration",
++                        use_names=True):
+         """
+         Return a series of responses
+         """
+-        fieldset = UrwidFieldSet(challenges)
++        fieldset = UrwidFieldSet(challenges, use_names=use_names)
+         dialog = UrwidDialog(fieldset)
+         dialog.header_text = title
+         dialog.run()
+@@ -163,9 +159,10 @@
+     Takes a set of fields, displays a dialog corresponding to those fields, and
+     returns the values corresponding to those fields.
+     """
+-    def __init__(self, challenges):
++    def __init__(self, challenges, use_names=True):
+         self.getters = {}
+         self.challenges = challenges
++        self.use_names = use_names
+ 
+     def get_value_dict(self):
+         """
+@@ -187,11 +184,18 @@
+         # we don't have hanging indent, but we can stick a bullet out into the 
+         # left column.
+         asterisk = urwid.Text(('label', '* '))
+-        label = urwid.Text(('label', challenge.full_description()))
++        if self.use_names:
++            label_text = challenge.full_description()
++        else:
++            label_text = challenge.description
++        label = urwid.Text(('label', label_text))
+         colon = urwid.Text(('label', ': '))
+ 
+-        if isinstance(challenge.default, str):
+-            field = urwid.Edit('', challenge.default)
++        if issubclass(challenge.type, str):
++            if challenge.default is NoDefault:
++                field = urwid.Edit('', '')
++            else:
++                field = urwid.Edit('', challenge.default)
+             def getter():
+                 """ 
+                 Closure around urwid.Edit.get_edit_text(), which we'll
+@@ -199,7 +203,7 @@
+                 """
+                 return field.get_edit_text()
+             self._set_getter(challenge.name, getter)
+-        elif isinstance(challenge.default, bool):
++        elif issubclass(challenge.type, bool):
+             field = urwid.CheckBox('')
+             def getter():
+                 """ 
+@@ -209,7 +213,7 @@
+                 return field.get_state()
+             self._set_getter(challenge.name, getter)
+         else:
+-            raise UrwidDialogException("")
++            raise UrwidDialogException(str(challenge.type))
+ 
+ 
+         field = urwid.AttrWrap(field, 'field', 'fieldfocus')