Add common helper for interactive prompts

This patch adds a common method, textui.prompt_helper, that handles
encoding, decoding and error handling for interactive prompts.
On EOFError (Ctrl+D) or  KeyboardInterrupt (Ctrl+C), it raises
a new InvocationError, PromptFailed.

The helper is used in prompt, prompt_yesno, and prompt_password,
each of which originally only handled one of Ctrl+C and Ctrl+D.
This fixes https://fedorahosted.org/freeipa/ticket/2345
And it means prompt_yesno will no longer return None on error.

A minor fix restores errors.py's ability print out the list of
errors when run as a script.
This commit is contained in:
Petr Viktorin
2012-02-09 09:09:03 -05:00
committed by Rob Crittenden
parent 8125c11a8d
commit efb37739ab
2 changed files with 40 additions and 30 deletions

View File

@@ -46,7 +46,9 @@ import frontend
import backend
import plugable
import util
from errors import PublicError, CommandError, HelpError, InternalError, NoSuchNamespaceError, ValidationError, NotFound, NotConfiguredError
from errors import (PublicError, CommandError, HelpError, InternalError,
NoSuchNamespaceError, ValidationError, NotFound, NotConfiguredError,
PromptFailed)
from constants import CLI_TAB
from parameters import Password, Bytes, File, Str, StrEnum
from text import _
@@ -515,6 +517,18 @@ class textui(backend.Backend):
def print_error(self, text):
print ' ** %s **' % unicode(text)
def prompt_helper(self, prompt, label, prompt_func=raw_input):
"""Prompt user for input
Handles encoding the prompt and decoding the input.
On end of stream or ctrl+c, raise PromptFailed.
"""
try:
return self.decode(prompt_func(self.encode(prompt)))
except (KeyboardInterrupt, EOFError):
print
raise PromptFailed(name=label)
def prompt(self, label, default=None, get_values=None, optional=False):
"""
Prompt user for input.
@@ -528,11 +542,7 @@ class textui(backend.Backend):
prompt = u'%s: ' % prompt
else:
prompt = u'%s [%s]: ' % (prompt, default)
try:
data = raw_input(self.encode(prompt))
except EOFError:
return None
return self.decode(data)
return self.prompt_helper(prompt, label)
def prompt_yesno(self, label, default=None):
"""
@@ -546,8 +556,6 @@ class textui(backend.Backend):
If Default parameter is None, user is asked for Yes/No answer until
a correct answer is provided. Answer is then returned.
In case of an error, a None value may returned
"""
default_prompt = None
@@ -563,10 +571,7 @@ class textui(backend.Backend):
prompt = u'%s Yes/No: ' % label
while True:
try:
data = raw_input(self.encode(prompt)).lower()
except EOFError:
return None
data = self.prompt_helper(prompt, label)
if data in (u'yes', u'y'):
return True
@@ -580,23 +585,19 @@ class textui(backend.Backend):
Prompt user for a password or read it in via stdin depending
on whether there is a tty or not.
"""
try:
if sys.stdin.isatty():
while True:
pw1 = getpass.getpass(u'%s: ' % unicode(label))
if not confirm:
return self.decode(pw1)
pw2 = getpass.getpass(
unicode(_('Enter %(label)s again to verify: ') % dict(label=label))
)
if pw1 == pw2:
return self.decode(pw1)
self.print_error( _('Passwords do not match!'))
else:
return self.decode(sys.stdin.readline().strip())
except KeyboardInterrupt:
print ''
self.print_error(_('Cancelled.'))
if sys.stdin.isatty():
prompt = u'%s: ' % unicode(label)
repeat_prompt = unicode(_('Enter %(label)s again to verify: ') % dict(label=label))
while True:
pw1 = self.prompt_helper(prompt, label, prompt_func=getpass.getpass)
if not confirm:
return pw1
pw2 = self.prompt_helper(repeat_prompt, label, prompt_func=getpass.getpass)
if pw1 == pw2:
return pw1
self.print_error( _('Passwords do not match!'))
else:
return self.decode(sys.stdin.readline().strip())
def select_entry(self, entries, format, attrs, display_count=True):
"""