]> scripts.mit.edu Git - wizard.git/blobdiff - wizard/prompt.py
Rewrite parametrize to use new parametrizeWithVars
[wizard.git] / wizard / prompt.py
index 2eb0f1254e97b54588b56402964ef03cb1f1380c..852dc4447fff432bca5265e1f695a27a2e328062 100644 (file)
@@ -1,10 +1,14 @@
 """
-Interface compatible with :class:`dialog.Dialog` for doing
+Interface compatible with :class:`PromptInterface` for doing
 non-ncurses interaction.
 
 By convention, the last line of a text parameter should be
 a short value with a trailing colon so that we can prompt a user
 for a value immediately after it.
+
+.. testsetup:: *
+
+    from wizard.prompt import *
 """
 
 import sys
@@ -23,9 +27,17 @@ except ImportError:
     has_dialog = False
 
 def fill(text, width=60, **kwargs):
+    """
+    Convenience wrapper for :func:`textwrap.fill` that preserves
+    paragraphs.
+    """
     return "\n\n".join(textwrap.fill(p, width=width, **kwargs) for p in text.split("\n\n"))
 
 def guess_dimensions(text, width=60):
+    """
+    Guesses the dimensions that any given piece of text will
+    need to display on terminal, given some width.
+    """
     # +1 for the fact that there's no trailing newline from fill
     # +2 for the borders
     # +1 as a buffer in case we underestimate
@@ -33,6 +45,10 @@ def guess_dimensions(text, width=60):
     return width, fill(text, width-2).count("\n") + 1 + 2 + 1
 
 def make(prompt, non_interactive):
+    """
+    Makes a :class:`dialog.Dialog` compatible class based on
+    configuration.
+    """
     if non_interactive:
         return FailPrompt()
     if prompt or os.getenv('TERM') == 'dumb' or not has_dialog:
@@ -43,14 +59,65 @@ def make(prompt, non_interactive):
         return Prompt()
 
 def join_or(items):
+    """
+    Joins a list of disjunctions into a human readable sentence.
+
+    >>> join_or(['foo'])
+    'foo'
+    >>> join_or(['foo', 'bar', 'baz'])
+    'foo, bar or baz'
+    """
     if len(items) == 0:
         raise ValueError
     elif len(items) == 1:
         return items[0]
     return ', '.join(items[:-1]) + ' or ' + items[-1]
 
+class PromptInterface(object):
+    def inputbox(self, text, init='', **kwargs):
+        """
+        Request a free-form, single line of text from the user.
+        Prompt the user using ``text``; and ``init`` is the
+        initial value filling the field; not all implementations
+        support editing ``init``.  Returns the typed string.
+        """
+        raise NotImplementedError
+    def menu(self, text, choices=[], **kwargs):
+        """
+        Request a selection from a number of choices from the user.
+        Prompt the user using ``text``; ``choices`` is a list
+        of tuples of form ``(value to return, description)``, where
+        ``value to return`` is the value that this function will
+        return.
+        """
+        raise NotImplementedError
+    def passwordbox(self, text, **kwargs):
+        """
+        Securely requests a password from the user.  Prompts the user
+        using ``text``; return value is the password.
+        """
+        raise NotImplementedError
+    def msgbox(self, text, **kwargs):
+        """
+        Gives the user a message that they must dismiss before proceeding.
+        """
+        raise NotImplementedError
+    def infobox(self, text, **kwargs):
+        """
+        Gives the user a non-blocking message; useful if you are about
+        to do an operation that will take some time.
+        """
+        raise NotImplementedError
+
 @decorator.decorator
 def dialog_wrap(f, self, text, *args, **kwargs):
+    """
+    Convenience decorator that automatically:
+
+        1. Removes already handled keyword arguments,
+        2. Configures the dimensions of the dialog box, and
+        3. Handles the different ext possibilities of dialog.
+    """
     if 'cmdopt' in kwargs: del kwargs['cmdopt']
     if 'width' not in kwargs and 'height' not in kwargs:
         kwargs["width"], kwargs["height"] = guess_dimensions(text)
@@ -69,7 +136,7 @@ def dialog_wrap(f, self, text, *args, **kwargs):
         raise DialogError(exit)
     return value
 
-class Dialog(object):
+class Dialog(PromptInterface):
     """Ncurses interface using dialog."""
     interactive = True
     def __init__(self):
@@ -84,12 +151,12 @@ class Dialog(object):
         del kwargs['initerror']
         kwargs['height'] += 5 # for the text box
         exit, result = self.dialog.inputbox(*args, **kwargs)
-        if exit == self.dialog.DIALOG_OK:
+        if exit == self.dialog.DIALOG_OK: # pylint: disable-msg=E1101
             # do some funny munging
             kwargs.setdefault('init', '')
             if result[0:len(kwargs['init'])] != kwargs['init']:
                 self.msgbox(initerror, height=10, width=50)
-                exit = self.dialog.DIALOG_OK
+                exit = self.dialog.DIALOG_OK # pylint: disable-msg=E1101
                 result = self.inputbox(*args, initerror=initerror, **kwargs)
             else:
                 result = result[len(kwargs['init']):]
@@ -112,12 +179,14 @@ class Dialog(object):
 
 @decorator.decorator
 def prompt_wrap(f, self, *args, **kwargs):
+    """Convenience decorator that handles end-of-document and interrupts."""
     try:
         return f(self, *args, **kwargs)
     except (EOFError, KeyboardInterrupt):
         raise UserCancel
 
-class Prompt(object):
+class Prompt(PromptInterface):
+    """Simple stdin/stdout prompt object."""
     interactive = True
     @prompt_wrap
     def inputbox(self, text, init='', **kwargs):
@@ -150,7 +219,7 @@ class Prompt(object):
         print ""
         print fill(text.strip())
 
-class FailPrompt(object):
+class FailPrompt(PromptInterface):
     """
     Prompt that doesn't actually ask the user; just fails with
     an error message.
@@ -173,11 +242,13 @@ class FailPrompt(object):
         print fill(text.strip(), break_long_words=False)
 
 class Error(wizard.Error):
+    """Base error class."""
     pass
 
 class MissingRequiredParam(Error):
     """Non-interactive, but we needed more info."""
     def __init__(self, cmdopt):
+        """``cmdopt`` is the command line option that should be specified."""
         self.cmdopt = cmdopt
     def __str__(self):
         return """
@@ -192,6 +263,7 @@ class UserCancel(Error):
 class DialogError(Error):
     """Dialog returned a mysterious error."""
     def __init__(self, exit):
+        """``exit`` is the mysterious exit code."""
         self.exitcode = exit
     def __str__(self):
         return """