From 5c8249fb37212edf6d7eb9f75549f4f00bdfbe94 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Wed, 12 May 2010 12:54:35 -0400 Subject: [PATCH] Removed hooke.libinput Functionality superseded by hooke.command.Request/.Response. --- hooke/command.py | 89 ++++++++++++++++++--- hooke/libinput.py | 172 ---------------------------------------- hooke/ui/commandline.py | 82 ++++++++++++++++--- 3 files changed, 150 insertions(+), 193 deletions(-) delete mode 100644 hooke/libinput.py diff --git a/hooke/command.py b/hooke/command.py index 5c0f1b2..0a6abc3 100644 --- a/hooke/command.py +++ b/hooke/command.py @@ -57,37 +57,108 @@ class UncaughtException (Failure): setattr(self, key, value) self.args = (self.traceback + str(self.exception),) +class InList (list): + """:class:`Request` validator class. + + Examples + -------- + + >>> i = InList(['abc', 'def', 5, True]) + >>> i('abc') + >>> i(5) + >>> i(False) + Traceback (most recent call last): + ... + ValueError: False + """ + def __init__(self, *args, **kwargs): + list.__init__(self, *args, **kwargs) + + def __call__(self, value): + """Raises ValueError if a given `value` is not in our internal + list. + """ + if value not in self: + raise ValueError(value) + class Interaction (object): """Mid-command inter-process interaction. + + Stores :attr:`type`, a string representing the interaction type + ('boolean', 'string', ...). """ - pass + def __init__(self, type): + self.type = type class Request (Interaction): """Command engine requests for information from the UI. """ - def __init__(self, msg, default=None): - super(Request, self).__init__() + def __init__(self, type, response_class, + msg, default=None, validator=None): + super(Request, self).__init__(type) + self.response_class = response_class self.msg = msg self.default = default + self.validator = validator + + def response(self, value): + if self.validator != None: + self.validator(value) + return self.response_class(value) class Response (Interaction): """UI response to a :class:`Request`. """ - def __init__(self, value): - super(Response, self).__init__() + def __init__(self, type, value): + super(Response, self).__init__(type) self.value = value class BooleanRequest (Request): - pass + def __init__(self, msg, default=None): + super(BooleanRequest, self).__init__( + 'boolean', BooleanResponse, msg, default, + validator = InList([True, False, default])) class BooleanResponse (Response): - pass + def __init__(self, value): + super(BooleanResponse, self).__init__('boolean', value) + +class StringRequest (Request): + def __init__(self, msg, default=None): + super(StringRequest, self).__init__( + 'string', StringResponse, msg, default) + +class StringResponse (Response): + def __init__(self, value): + super(StringResponse, self).__init__('string', value) + +class FloatRequest (Request): + def __init__(self, msg, default=None): + super(FloatRequest, self).__init__( + 'float', FloatResponse, msg, default) + +class FloatResponse (Response): + def __init__(self, value): + super(FloatResponse, self).__init__('float', value) + +class SelectionRequest (Request): + def __init__(self, msg, default=None, options=[]): + super(SelectionRequest, self).__init__( + 'selection', SelectionResponse, msg, default) + self.options = options + +class SelectionResponse (Response): + def __init__(self, value): + super(SelectionResponse, self).__init__('selection', value) + class Command (object): """One-line command description here. >>> c = Command(name='test', help='An example Command.') - >>> status = c.run(NullQueue(), PrintQueue(), help=True) # doctest: +REPORT_UDIFF + >>> hooke = None + >>> status = c.run(hooke, NullQueue(), PrintQueue(), + ... help=True) # doctest: +REPORT_UDIFF ITEM: Command: test @@ -97,7 +168,7 @@ class Command (object): An example Command. ITEM: - Success + """ def __init__(self, name, aliases=None, arguments=[], help=''): self.name = name diff --git a/hooke/libinput.py b/hooke/libinput.py deleted file mode 100644 index 21ca05f..0000000 --- a/hooke/libinput.py +++ /dev/null @@ -1,172 +0,0 @@ -''' -Input check routines. - -Copyright (C) 2008 Alberto Gomez-Casado (University of Twente). - -This program is released under the GNU General Public License version 2. - -This is just a collection of wrappers for raw_input(), I noticed using -hooke that unexpected answers from the user often triggered a nasty -crash, so what they do is provide some basic check of inputs to avoid -'voids', letters where numbers are expected, etc - -The basic call there is safeinput(message,[list]) -Message is a string that is shown to the user ('Enter filename:') -[list] can be present or not. - - If not present safeinput is just a raw_input shielded against void -inputs (it will keep asking until it gets some answer) - - If a list of only one element is provided, it is interpreted as a -default value (in case a void input that value will be returned) - - If a list of two or more 'string' elements is provided, user input -must match one of them to be valid - - If a list of two integer elements [a, b] is provided, user input -is required to be in the interval [a,b] - -More info about the underlying calls can be found in the code. However -I am still not very satisfied with them, That's why I made safeinput() -to wrap all so I can improve them without further fiddling with other -module's code. -''' - -from types import * - - -def safeinput (message, valid=[]): - ''' - friendlier frontend for alphainput and numinput - valid should be a list of 0...n values - ''' - - #if possible values are not listed we just ask for any non-null input - if len(valid)==0: - return alphainput(message, '',1,[]) - - - if len(valid)>0: - #if valid values are string we use alphainput, if it is only one we take as default - if type(valid[0]) is StringType: - if len(valid)==1: - return alphainput(message, valid[0], 0,[]) - else: - return alphainput(message,'', 1,valid) - - #if valid values are numbers we use numinput - if type(valid[0]) is IntType: - if len(valid)==1: - return numinput(message,valid[0],1,[]) - else: - return numinput(message,'',1,valid) - - -def alphainput (message, default, repeat, valid): - ''' - message: prompt for the user - default: return value if user input was not correct (and repeat=0) - repeat: keeps asking user till it gets a valid input - valid: list of allowed answers, empty list for "anything" - ''' - if default and not repeat: - print 'Press [enter] for default: ('+str(default)+')' - reply=raw_input(message) - if len(valid)>0: - if reply in valid: - return reply - else: - if repeat==1: - while reply not in valid: - reply=raw_input('You should enter any of these: '+ str(valid) +'\n'+ message) - return reply - else: - return default - else: - if len(reply)>0: - return reply - else: - if not repeat: - return default - else: - while len(reply)==0: - print 'Try again' - reply=raw_input(message) - return reply - - -def checkalphainput (test, default, valid): - #useful when input was taken form command args - if len(valid)>0: - if test in valid: - return test - else: - return default - else: - #TODO: raise exception? - if len(test)>0: - return test - else: - return default - - -def numinput(message, default, repeat, limits): - ''' - message: prompt for the user - default: return value if user input was not correct (and repeat=0) - repeat: keeps asking user till it gets a valid input - limits: pair of values, input is checked to be between them, empty list for "any number" - ''' - if default and not repeat: - print 'Press [enter] for default: '+str(default) - - reply=raw_input(message) - - try: - intreply=int(reply) - except: - intreply=None - - if len(limits)==2: - high=int(limits.pop()) - low=int(limits.pop()) - if intreply>=low and intreply <= high: - return intreply - else: - if repeat==1: - while intreplyhigh : - reply=raw_input('You should enter values between: '+ str(low)+' and '+str(high) +'\n'+ message) - try: - intreply=int(reply) - except: - intreply=None - return intreply - else: - return default - else: - if intreply!=None: - return intreply - else: - if not repeat: - return default - else: - while intreply==None: - print 'Try again' - reply=raw_input(message) - try: - intreply=int(reply) - except: - intreply=None - return intreply - - -def checknuminput(test,default,limits): - #useful when input was taken from command args - if len(limits)==2: - high=int(limits.pop()) - low=int(limits.pop()) - if test>=low and test <= high: - return int(test) - else: - return default - else: - if len(test)>0: - return int(test) - else: - return default diff --git a/hooke/ui/commandline.py b/hooke/ui/commandline.py index f42d014..c6520bd 100644 --- a/hooke/ui/commandline.py +++ b/hooke/ui/commandline.py @@ -7,7 +7,7 @@ import optparse import readline # including readline makes cmd.Cmd.cmdloop() smarter import shlex -from ..command import CommandExit, Exit, BooleanRequest, BooleanResponse, \ +from ..command import CommandExit, Exit, Request, BooleanRequest, \ Command, Argument, StoreValue from ..ui import UserInterface, CommandMessage @@ -91,8 +91,8 @@ class DoCommand (CommandMethod): self.cmd.stdout.write(msg.__class__.__name__+'\n') self.cmd.stdout.write(str(msg).rstrip()+'\n') break - elif isinstance(msg, BooleanRequest): - self._boolean_request(msg) + elif isinstance(msg, Request): + self._handle_request(msg) continue self.cmd.stdout.write(str(msg).rstrip()+'\n') @@ -146,19 +146,77 @@ class DoCommand (CommandMethod): % (len(arguments), self.name_fn(self.command.name), target_string)) - def _boolean_request(self, msg): + def _handle_request(self, msg): + """Repeatedly try to get a response to `msg`. + """ + prompt = getattr(self, '_%s_request_prompt' % msg.type, None) + if prompt == None: + raise NotImplementedError('_%s_request_prompt' % msg.type) + prompt_string = prompt(msg) + parser = getattr(self, '_%s_request_parser' % msg.type, None) + if parser == None: + raise NotImplementedError('_%s_request_parser' % msg.type) + error = None + while True: + if error != None: + self.cmd.stdout.write(''.join([ + error.__class__.__name__, ': ', str(error), '\n'])) + self.cmd.stdout.write(prompt_string) + value = parser(msg, self.cmd.stdin.readline()) + try: + response = msg.response(value) + break + except ValueError, error: + continue + self.cmd.inqueue.put(response) + + def _boolean_request_prompt(self, msg): if msg.default == True: yn = ' [Y/n] ' else: yn = ' [y/N] ' - self.cmd.stdout.write(msg.msg+yn) - response = self.cmd.stdin.readline().strip().lower() - if response.startswith('y'): - self.cmd.inqueue.put(BooleanResponse(True)) - elif response.startswith('n'): - self.cmd.inqueue.put(BooleanResponse(False)) + return msg.msg + yn + + def _boolean_request_parser(self, msg, response): + value = response.strip().lower() + if value.startswith('y'): + value = True + elif value.startswith('n'): + value = False + elif len(value) == 0: + value = msg.default + return value + + def _string_request_prompt(self, msg): + if msg.default == None: + d = ' ' else: - self.cmd.inqueue.put(BooleanResponse(msg.default)) + d = ' [%s] ' % msg.default + return msg.msg + d + + def _string_request_parser(self, msg, response): + return response.strip() + + def _float_request_prompt(self, msg): + return self._string_request_prompt(msg) + + def _float_request_parser(self, msg, resposne): + return float(response) + + def _selection_request_prompt(self, msg): + options = [] + for i,option in enumerate(msg.options): + options.append(' %d) %s' % (i,option)) + options = ''.join(options) + if msg.default == None: + prompt = '? ' + else: + prompt = '? [%d] ' % msg.default + return '\n'.join([msg,options,prompt]) + + def _selection_request_parser(self, msg, response): + return int(response) + class HelpCommand (CommandMethod): def __init__(self, *args, **kwargs): @@ -243,7 +301,7 @@ typing mistakes ;). default = False outqueue.put(BooleanRequest(msg, default)) result = inqueue.get() - assert isinstance(result, BooleanResponse) + assert result.type == 'boolean' _exit = result.value if _exit == True: raise Exit() -- 2.26.2