0001"""Input and output functions
0002"""
0003import sys, os, os.path
0004from terminate.abstract import color, stdout, stderr
0005
0006__all__ = ["rl", "casts", "queries",
0007 "input_object","query","query_list","file_chooser","error_message"]
0008
0009
0010
0011
0012ERROR_MESSAGE = ( color('bright','red') + 'Error: ' + color('default') +
0013 '%s' + '\a' + os.linesep )
0014NICE_INPUT_ERRORS = {
0015 float: "The input ('%s') must be a number",
0016 int: "The input ('%s') must be an integer (-1, 0, 1, 2, etc.)"
0017}
0018
0019DEFAULT_INPUT_ERRORS = "Bad input (%s)"
0020
0021def input_object(prompt_text, cast, default=None, castarg=[], castkwarg={}):
0022 """Gets input from the command line and validates it.
0023
0024 prompt_text
0025 A string. Used to prompt the user.
0026 cast
0027 This can be any callable object (class, function, type, etc). It
0028 simply calls the cast with the given arguements and returns the
0029 result. If a ValueError is raised, it
0030 will output an error message and prompt the user again.
0031
0032 Because some builtin python objects don't do casting in the way
0033 that we might like you can easily write a wrapper function that
0034 looks and the input and returns the appropriate object or exception.
0035 Look in the cast submodule for examples.
0036 default
0037 function returns this value if the user types nothing in. This is
0038 can be used to cancel the input so-to-speek
0039 castarg, castkwarg
0040 list and dictionary. Extra arguments passed on to the cast.
0041 """
0042 while True:
0043 try:
0044 t = raw_input(prompt_text)
0045 if t == '': return default
0046 value = cast(t, *castarg, **castkwarg)
0047 except ValueError, details:
0048 if cast in NICE_INPUT_ERRORS:
0049 stdout.write(ERROR_MESSAGE % (NICE_INPUT_ERRORS[cast] % t))
0050 else: stdout.write(ERROR_MESSAGE % (DEFAULT_INPUT_ERRORS % str(details)))
0051 continue
0052 return value
0053
0054def query(prompt_text, answers, default=None, list_values = False, ignorecase = True ):
0055 """Preset a few options
0056
0057 The prompt_text argument is a string, nothing magical.
0058
0059 The answers argument accepts input in two different forms. The simpler form
0060 (a tuple with strings) looks like:
0061
0062 .. code-block:: Python
0063
0064 ('Male','Female')
0065
0066 And it will pop up a question asking the user for a gender and requiring
0067 the user to enter either 'male' or 'female' (case doesn't matter unless
0068 you set the third arguement to false).
0069 The other form is something like:
0070
0071 .. code-block:: Python
0072
0073 ({'values':('Male','M'),'fg':'cyan'},
0074 {'values':('Female','F'),'fg':'magenta'})
0075
0076 This will pop up a question with Male/Female (each with appropriate
0077 colouring). Additionally, if the user types in just 'M', it will be
0078 treated as if 'Male' was typed in. The first item in the 'values' tuple
0079 is treated as default and is the one that is returned by the function
0080 if the user chooses one in that group.
0081 In addition the function can handle non-string objects quite fine. It
0082 simple displays the output object.__str__() and compares the user's input
0083 against that. So the the code
0084
0085 .. code-block:: Python
0086
0087 query("Python rocks? ",(True, False))
0088
0089 will return a bool (True) when the user types in the string 'True' (Of
0090 course there isn't any other reasonable answer than True anyways :P)
0091
0092 ``default`` is the value function returns if the user types nothing in. This is
0093 can be used to cancel the input so-to-speek
0094
0095 Using list_values = False will display a list, with descriptions printed out
0096 from the 'desc' keyword
0097 """
0098 answers = list(answers)
0099 for i in range(len(answers)):
0100 if not isinstance(answers[i], dict):
0101 answers[i] = {'values': [answers[i]]}
0102 try:
0103 import readline, rl
0104 wordlist = [ str(values) for answer in answers
0105 for values in answer['values']]
0106 completer = rl.ListCompleter(wordlist, ignorecase)
0107 readline.parse_and_bind("tab: complete")
0108 readline.set_completer(completer.complete)
0109 except ImportError:
0110 pass
0111 answerslist = []
0112 if list_values:
0113 for item in answers:
0114 answerslist.append(
0115 color('bright', fg=item.get('fg'), bg=item.get('bg')) +
0116 str(item['values'][0]) +
0117 color('default') +
0118 ' : ' + item['desc'])
0119 prompt_text += os.linesep + os.linesep.join(answerslist) + os.linesep
0120 else:
0121 for item in answers:
0122 answerslist.append(
0123 color('bright', fg=item.get('fg'), bg=item.get('bg')) +
0124 str(item['values'][0]) +
0125 color('default'))
0126 prompt_text += '[' + '/'.join(answerslist) + ']'
0127 while True:
0128 stdout.write(prompt_text)
0129 t = raw_input(': ')
0130 if t == '': return default
0131 if ignorecase: response = t.lower()
0132 else: response = t
0133 for item in answers:
0134 for a in item['values']:
0135 if ignorecase and (response == str(a).lower()):
0136 return item['values'][0]
0137 if response == a:
0138 return item['values'][0]
0139 stdout.write(ERROR_MESSAGE % (
0140 "Response '%s' not understood, please try again." % t))
0141
0142def file_chooser(prompt_text = "Enter File: ", default=None, filearg=[], filekwarg={}):
0143 """A simple tool to get a file from the user. Takes keyworded arguemnts
0144 and passes them to open().
0145
0146 If the user enters nothing the function will return the ``default`` value.
0147 Otherwise it continues to prompt the user until it get's a decent response.
0148
0149 filekwarg may contain arguements passed on to ``open()``.
0150 """
0151 try:
0152 import readline, rl
0153 completer = rl.PathCompleter()
0154 readline.set_completer_delims(completer.delims)
0155 readline.parse_and_bind("tab: complete")
0156 readline.set_completer(completer.complete)
0157 except ImportError:
0158 pass
0159 while True:
0160 f = raw_input(prompt_text)
0161 if f == '': return default
0162 f = os.path.expanduser(f)
0163 if len(f) != 0 and f[0] == os.path.sep:
0164 f = os.path.abspath(f)
0165 try:
0166 return open(f, *filearg, **filekwarg)
0167 except IOError, e:
0168 stdout.write(ERROR_MESSAGE % ("unable to open %s : %s" % (f, e)))