Changeset 553

Show
Ignore:
Timestamp:
06/15/08 05:17:19 (6 months ago)
Author:
athomas
Message:

Refactored InputDriver so implementations are cleanly separated.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • cly/trunk/cly/interactive.py

    r552 r553  
    1919 
    2020import os 
    21 import readline 
    2221import sys 
    2322import types 
     
    2726from cly.parser import Parser 
    2827 
     28try: 
     29    import readline 
     30except ImportError: 
     31    readline = None 
    2932try: 
    3033    from cly import _rlext 
     
    4245 
    4346class InputDriver(object): 
     47    """Abstraction for line input."""  
     48 
    4449    def __init__(self, parser, prompt, history_file, history_length): 
    4550        self.parser = parser 
    46         self._prompt = prompt 
     51        self.prompt = prompt 
    4752        self.history_file = history_file 
    4853        self.history_length = history_length 
    4954        self._cli_inject_text = '' 
    5055        self._completion_candidates = [] 
     56 
     57    def input(self): 
     58        """Input one line from user and return it.""" 
     59        raise NotImplementedError 
     60 
     61    def shutdown(self): 
     62        """Shutdown input driver.""" 
     63 
     64    @staticmethod 
     65    def choose(): 
     66        """Called to determine whether this driver is usable.""" 
     67        raise NotImplementedError 
     68 
     69 
     70class DummyInputDriver(InputDriver): 
     71    """The horror.""" 
     72    @staticmethod 
     73    def choose(): 
     74        print >> sys.stderr, \ 
     75            'WARNING: Most line editing features are unavailable.' 
     76        return True 
     77 
     78    def input(self): 
     79        return raw_input(self.prompt) 
     80 
     81 
     82class BaseReadlineDriver(InputDriver): 
     83    """Base class for readline variants.""" 
     84    def __init__(self, *args, **kwargs): 
     85        super(BaseReadlineDriver, self).__init__(*args, **kwargs) 
    5186        try: 
    5287            readline.set_history_length(self.history_length) 
     
    6095        readline.set_startup_hook(self._redraw_input) 
    6196 
    62         if _rlext: 
    63             self._bind_help = self._rlext_bind_help 
    64             self._force_redisplay = _rlext.force_redisplay 
    65             self._cursor = _rlext.cursor 
    66         elif pyreadline: 
    67             self._bind_help = self._pyrl_bind_help 
    68             self._force_redisplay = self._pyrl_force_redisplay 
    69             self._cursor = self._pyrl_cursor 
    70         else: 
     97        self.rl_bind_help() 
     98 
     99    @staticmethod 
     100    def choose(): 
     101        if readline: 
    71102            print >> sys.stderr, \ 
    72                 'WARNING: neither cly._rlext nor pyreadline found, ' \ 
    73                 'contextual help will\n         not be available.' 
    74             self._bind_help = lambda: None 
    75             self._force_redisplay = lambda: None 
    76             self._cursor = lambda _=None: None 
    77  
    78         self._bind_help() 
    79  
     103                'WARNING: neither pyreadline nor CLY\'s built-in readline ' \ 
     104                'extensions found,\n         contextual help are not ' \ 
     105                'available.' 
     106        return readline 
    80107 
    81108    def shutdown(self): 
     
    89116        return raw_input(self.prompt) 
    90117 
    91     def set_prompt(self, prompt): 
    92         self._prompt = prompt 
    93  
    94     prompt = property(lambda s: s._prompt, lambda s, p: s.set_prompt(p)) 
     118    def rl_bind_help(self): 
     119        pass 
     120 
     121    def rl_force_redisplay(self): 
     122        raise NotImplementedError 
     123 
     124    def rl_get_cursor(self): 
     125        raise NotImplementedError 
     126 
     127    def rl_set_cursor(self, cursor): 
     128        raise NotImplementedError 
     129 
     130    cursor = property(lambda s: s.rl_get_cursor(), lambda s, c: s.rl_set_cursor(c)) 
    95131 
    96132    # Internal methods 
     
    117153        except Exception, e: 
    118154            self._dump_traceback(e) 
    119             self._force_redisplay() 
     155            self.rl_force_redisplay() 
    120156            raise 
    121157 
     
    126162    def _show_help(self, key, count): 
    127163        try: 
    128             command = readline.get_line_buffer()[:self._cursor()
     164            command = readline.get_line_buffer()[:self.cursor
    129165            context = self.parser.parse(command) 
    130166            if context.remaining.strip(): 
     
    133169                text = '%s^ invalid token (candidates are %s)' % \ 
    134170                       (' ' * (context.cursor + len(self.prompt)), 
    135                         ', '.join(candidates)) 
     171                       ', '.join(candidates)) 
    136172                console.cerror(text) 
    137                 self._force_redisplay() 
     173                self.rl_force_redisplay() 
    138174                return 
    139175            help = context.help() 
    140176            print 
    141177            console.cprint('\n'.join(help.format())) 
    142             self._force_redisplay() 
     178            self.rl_force_redisplay() 
    143179            return 0 
    144180        except Exception, e: 
    145181            self._dump_traceback(e) 
    146             self._force_redisplay() 
     182            self.rl_force_redisplay() 
    147183            return 0 
    148184 
    149     # cly._rlext specific methods 
    150     def _rlext_bind_help(self): 
    151         _rlext.bind_key(ord('?'), self._show_help) 
    152  
    153     # pyreadline specific methods 
    154     def _pyrl_bind_help(self): 
     185 
     186class PyReadlineDriver(BaseReadlineDriver): 
     187    """The IPython pure-Python pyreadline implementation.""" 
     188    @staticmethod 
     189    def choose(): 
     190        return pyreadline 
     191 
     192    def rl_bind_help(self): 
    155193        def _show_help_proxy(_, __): 
    156194            self._show_help(None, None) 
     
    163201        pyreadline.parse_and_bind('F1: cly-help') 
    164202 
    165     def _pyrl_force_redisplay(self): 
     203    def rl_force_redisplay(self): 
    166204        pyreadline.rl._print_prompt() 
    167205 
    168     def _pyrl_cursor(self, cursor=None): 
    169         if cursor is None: 
    170             return pyreadline.rl.l_buffer.point 
    171         else: 
    172             pyreadline.rl.l_buffer.point = cursor 
    173         return 0 
     206    def rl_get_cursor(self): 
     207        return pyreadline.rl.l_buffer.point 
     208 
     209    def rl_set_cursor(self, cursor): 
     210        pyreadline.rl.l_buffer.point = cursor 
     211 
     212 
     213class ExtendedReadlineDriver(BaseReadlineDriver): 
     214    """Use CLY's built-in readline extensions.""" 
     215 
     216    @staticmethod 
     217    def choose(): 
     218        return _rlext 
     219 
     220    def rl_bind_help(self): 
     221        _rlext.bind_key(ord('?'), self._show_help) 
     222 
     223    def rl_force_redisplay(self): 
     224        _rlext.force_redisplay() 
     225 
     226    def rl_get_cursor(self): 
     227        return _rlext.cursor() 
     228 
     229    def rl_set_cursor(self, cursor): 
     230        _rlext.cursor(cursor) 
    174231 
    175232 
     
    227284        self.data = data 
    228285 
    229         self.input_driver = InputDriver( 
     286        self.input_driver = input_driver( 
    230287            parser, prompt, history_file, history_length 
    231288            ) 
     
    331388 
    332389 
     390# Available input drivers 
     391DRIVERS = [ExtendedReadlineDriver, PyReadlineDriver, BaseReadlineDriver, 
     392           DummyInputDriver] 
     393 
     394 
     395def input_driver(*args, **kwargs): 
     396    """Select the "best" available input driver.""" 
     397    for driver in DRIVERS: 
     398        if driver.choose(): 
     399            return driver(*args, **kwargs) 
     400    raise Error('No choose input driver found') 
     401 
     402 
    333403def interact(grammar_or_parser, inhibit_exceptions=False, with_backtrace=False, 
    334404             *args, **kwargs):