Changeset 554

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

Implemented reentrant input drivers. This allows multiple grammars to be used
at the same time. Fixes #67.

Files:

Legend:

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

    r553 r554  
    1717Press ``?`` at any time to view contextual help. 
    1818""" 
     19 
     20from __future__ import with_statement 
    1921 
    2022import os 
     
    5254        self.history_file = history_file 
    5355        self.history_length = history_length 
    54         self._cli_inject_text = '' 
    55         self._completion_candidates = [] 
     56 
     57    def enter(self): 
     58        """Enter the input context.""" 
    5659 
    5760    def input(self): 
     
    5962        raise NotImplementedError 
    6063 
    61     def shutdown(self): 
    62         """Shutdown input driver.""" 
     64    def leave(self): 
     65        """Exit the input context.""" 
    6366 
    6467    @staticmethod 
    65     def choose(): 
     68    def usable(): 
    6669        """Called to determine whether this driver is usable.""" 
    6770        raise NotImplementedError 
    6871 
    6972 
    70 class DummyInputDriver(InputDriver): 
     73class DumbInput(InputDriver): 
    7174    """The horror.""" 
     75 
     76    def input(self): 
     77        return raw_input(self.prompt) 
     78 
    7279    @staticmethod 
    73     def choose(): 
     80    def usable(): 
    7481        print >> sys.stderr, \ 
    7582            'WARNING: Most line editing features are unavailable.' 
    7683        return True 
    7784 
    78     def input(self): 
    79         return raw_input(self.prompt) 
    80  
    81  
    82 class BaseReadlineDriver(InputDriver): 
     85 
     86class ReadlineDriver(InputDriver): 
    8387    """Base class for readline variants.""" 
     88 
    8489    def __init__(self, *args, **kwargs): 
    85         super(BaseReadlineDriver, self).__init__(*args, **kwargs) 
     90        super(ReadlineDriver, self).__init__(*args, **kwargs) 
     91        self._cli_inject_text = '' 
     92        self._completion_candidates = [] 
     93 
     94    def enter(self): 
    8695        try: 
    8796            readline.set_history_length(self.history_length) 
     
    95104        readline.set_startup_hook(self._redraw_input) 
    96105 
    97         self.rl_bind_help() 
     106        self._bind_help() 
     107 
     108    def input(self): 
     109        return raw_input(self.prompt) 
     110 
     111    def leave(self): 
     112        try: 
     113            readline.write_history_file(self.history_file) 
     114        except: 
     115            pass 
    98116 
    99117    @staticmethod 
    100     def choose(): 
     118    def usable(): 
    101119        if readline: 
    102120            print >> sys.stderr, \ 
     
    106124        return readline 
    107125 
    108     def shutdown(self): 
    109         try: 
    110             readline.write_history_file(self.history_file) 
    111         except: 
    112             pass 
    113         _interact = None 
    114  
    115     def input(self): 
    116         return raw_input(self.prompt) 
    117  
    118     def rl_bind_help(self): 
     126    def _bind_help(self): 
    119127        pass 
    120128 
    121     def rl_force_redisplay(self): 
     129    def _force_redisplay(self): 
    122130        raise NotImplementedError 
    123131 
    124     def rl_get_cursor(self): 
     132    def _get_cursor(self): 
    125133        raise NotImplementedError 
    126134 
    127     def rl_set_cursor(self, cursor): 
     135    def _set_cursor(self, cursor): 
    128136        raise NotImplementedError 
    129137 
    130     cursor = property(lambda s: s.rl_get_cursor(), lambda s, c: s.rl_set_cursor(c)) 
     138    cursor = property(lambda s: s._get_cursor(), lambda s, c: s._set_cursor(c)) 
    131139 
    132140    # Internal methods 
     
    153161        except Exception, e: 
    154162            self._dump_traceback(e) 
    155             self.rl_force_redisplay() 
     163            self._force_redisplay() 
    156164            raise 
    157165 
     
    171179                       ', '.join(candidates)) 
    172180                console.cerror(text) 
    173                 self.rl_force_redisplay() 
     181                self._force_redisplay() 
    174182                return 
    175183            help = context.help() 
    176184            print 
    177185            console.cprint('\n'.join(help.format())) 
    178             self.rl_force_redisplay() 
     186            self._force_redisplay() 
    179187            return 0 
    180188        except Exception, e: 
    181189            self._dump_traceback(e) 
    182             self.rl_force_redisplay() 
     190            self._force_redisplay() 
    183191            return 0 
    184192 
    185193 
    186 class PyReadlineDriver(BaseReadlineDriver): 
     194class PyReadlineDriver(ReadlineDriver): 
    187195    """The IPython pure-Python pyreadline implementation.""" 
     196 
    188197    @staticmethod 
    189     def choose(): 
     198    def usable(): 
    190199        return pyreadline 
    191200 
    192     def rl_bind_help(self): 
     201    def _bind_help(self): 
    193202        def _show_help_proxy(_, __): 
    194203            self._show_help(None, None) 
     
    201210        pyreadline.parse_and_bind('F1: cly-help') 
    202211 
    203     def rl_force_redisplay(self): 
     212    def _force_redisplay(self): 
    204213        pyreadline.rl._print_prompt() 
    205214 
    206     def rl_get_cursor(self): 
     215    def _get_cursor(self): 
    207216        return pyreadline.rl.l_buffer.point 
    208217 
    209     def rl_set_cursor(self, cursor): 
     218    def _set_cursor(self, cursor): 
    210219        pyreadline.rl.l_buffer.point = cursor 
    211220 
    212221 
    213 class ExtendedReadlineDriver(BaseReadlineDriver): 
     222class ExtendedReadlineDriver(ReadlineDriver): 
    214223    """Use CLY's built-in readline extensions.""" 
    215224 
    216225    @staticmethod 
    217     def choose(): 
     226    def usable(): 
    218227        return _rlext 
    219228 
    220     def rl_bind_help(self): 
     229    def _bind_help(self): 
    221230        _rlext.bind_key(ord('?'), self._show_help) 
    222231 
    223     def rl_force_redisplay(self): 
     232    def _force_redisplay(self): 
    224233        _rlext.force_redisplay() 
    225234 
    226     def rl_get_cursor(self): 
     235    def _get_cursor(self): 
    227236        return _rlext.cursor() 
    228237 
    229     def rl_set_cursor(self, cursor): 
     238    def _set_cursor(self, cursor): 
    230239        _rlext.cursor(cursor) 
    231240 
     
    268277        As for :meth:`loop`. 
    269278    """ 
     279 
     280    # Available input drivers 
     281    INPUT_DRIVERS = [ExtendedReadlineDriver, PyReadlineDriver, ReadlineDriver, 
     282                     DumbInput] 
     283 
    270284 
    271285    def __init__(self, grammar_or_parser, application='cly', prompt=None, 
     
    284298        self.data = data 
    285299 
    286         self.input_driver = input_driver( 
     300        self.input_driver = self.best_input_driver( 
    287301            parser, prompt, history_file, history_length 
    288302            ) 
    289303 
    290     def once(self, default_text=''): 
     304    def once(self): 
    291305        """Input one command from the user and return the result of the 
    292306        executed command. 
    293  
    294         :param default_text: 
    295             Text to insert into the input buffer prior to editing. 
    296307        """ 
    297         self._cli_inject_text = default_text 
    298  
    299308        while True: 
    300309            command = '' 
    301310            try: 
    302                 command = self.input_driver.input() 
    303             except KeyboardInterrupt: 
    304                 print 
    305                 continue 
    306             except EOFError: 
    307                 print 
    308                 return None 
     311                self.input_driver.enter() 
     312                try: 
     313                    command = self.input_driver.input() 
     314                except KeyboardInterrupt: 
     315                    print 
     316                    continue 
     317                except EOFError: 
     318                    print 
     319                    return None 
     320            finally: 
     321                self.input_driver.leave() 
    309322 
    310323            try: 
     
    329342            Called with the Interact object before each line is displayed. 
    330343        """ 
    331         try: 
    332             while True: 
    333                 try: 
    334                     if callback: 
    335                         callback(self) 
    336                     if not self.once(): 
    337                         break 
    338                 except Exception, e: 
    339                     if inhibit_exceptions: 
    340                         if with_backtrace: 
    341                             import traceback 
    342                             console.cerror(traceback.format_exc()) 
    343                         else: 
    344                             console.cerror('error: %s' % e) 
     344        while True: 
     345            try: 
     346                if callback: 
     347                    callback(self) 
     348                if not self.once(): 
     349                    break 
     350            except Exception, e: 
     351                if inhibit_exceptions: 
     352                    if with_backtrace: 
     353                        import traceback 
     354                        console.cerror(traceback.format_exc()) 
    345355                    else: 
    346                         raise 
    347         finally
    348             self.input_driver.shutdown() 
     356                        console.cerror('error: %s' % e) 
     357                else
     358                    raise 
    349359 
    350360    def print_error(self, context, e): 
     
    388398 
    389399 
    390 # Available input drivers 
    391 DRIVERS = [ExtendedReadlineDriver, PyReadlineDriver, BaseReadlineDriver, 
    392            DummyInputDriver] 
    393  
    394  
    395 def 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') 
     400    @classmethod 
     401    def best_input_driver(cls, *args, **kwargs): 
     402        """Select the "best" available input driver.""" 
     403        for driver in cls.INPUT_DRIVERS: 
     404            if driver.usable(): 
     405                return driver(*args, **kwargs) 
     406        raise Error('No usable input driver found') 
    401407 
    402408