summaryrefslogtreecommitdiff
path: root/cli.py
blob: a01de85d45e0b6dbb2ac14efe68e3ae2bac72bb4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
""" Copyright (c) 2002-2003 LOGILAB S.A. (Paris, FRANCE).
 http://www.logilab.fr/ -- mailto:contact@logilab.fr

 
 Command line interface helper classes.
 
 It provides some default commands, a help system, a default readline
 configuration with completion and persistent history
"""

__revision__ = "$Id: cli.py,v 1.16 2005-05-10 12:31:04 adim Exp $"


import __builtin__
if not hasattr(__builtin__, '_'):
    __builtin__._ = str
    

def init_readline(complete_method, histfile=None):
    """init the readline library if available"""
    try:
        import readline
        readline.parse_and_bind("tab: complete")
        readline.set_completer(complete_method)
        string = readline.get_completer_delims().replace(':', '')
        readline.set_completer_delims(string)
        if histfile is not None:
            try:
                readline.read_history_file(histfile)
            except IOError:
                pass
            import atexit
            atexit.register(readline.write_history_file, histfile)
    except:
        print 'readline si not available :-('


class Completer :
    """readline completer"""
    
    def __init__(self, commands):
        self.list = commands
        
    def complete(self, text, state):
        """hook called by readline when <tab> is pressed"""
        n = len(text)
        matches = []
        for cmd in self.list :
            if cmd[:n] == text :
                matches.append(cmd)
        try:
            return matches[state]
        except IndexError:
            return None


class CLIHelper:
    """ an abstract command line interface client which recognize commands
    and provide an help system
    """
    
    CMD_MAP = {'help' : _("Others"),
               'quit' : _("Others"),
               }
    CMD_PREFIX = ''
    
    def __init__(self, histfile=None) :
        self._topics = {}
        self.commands = None
        self._completer = Completer(self._register_commands())
        init_readline(self._completer.complete, histfile)

    def run(self):
        """loop on user input, exit on EOF"""
        while 1:
            try:
                line = raw_input('>>> ')
            except EOFError:
                print 
                break
            s_line = line.strip()
            if not s_line:
                continue
            args = s_line.split()
            if self.commands.has_key(args[0]):
                try:
                    cmd = 'do_%s' % self.commands[args[0]]
                    getattr(self, cmd)(*args[1:])
                except EOFError:
                    break
                except:
                    import traceback
                    traceback.print_exc()
            else:
                try:
                    self.handle_line(s_line)
                except:
                    import traceback
                    traceback.print_exc()

    def handle_line(self, stripped_line):
        """method to overload in the concrete class
        
        should handle lines wich are not command
        """
        raise NotImplementedError()


    # private methods #########################################################
    
    def _register_commands(self):
        """ register available commands method and return the list of
        commands name
        """
        self.commands = {}
        self._command_help = {}
        commands = [attr[3:] for attr in dir(self) if attr[:3] == 'do_']
        for command in commands:
            topic = self.CMD_MAP[command]
            help_method = getattr(self, 'help_do_%s' % command)
            self._topics.setdefault(topic, []).append(help_method)
            self.commands[self.CMD_PREFIX + command] = command
            self._command_help[command] = help_method
        return self.commands.keys()

    def _print_help(self, cmd, syntax, explanation):
        print _('Command %s') % cmd
        print _('Syntax: %s') % syntax
        print '\t', explanation
        print


    # predefined commands #####################################################
    
    def do_help(self, command=None) :
        """base input of the help system"""
        if self._command_help.has_key(command):
            self._print_help(*self._command_help[command])
        elif command is None or not self._topics.has_key(command):
            print _("Use help <topic> or help <command>.")
            print _("Available topics are:")
            topics = self._topics.keys()
            topics.sort()
            for topic in topics:
                print '\t', topic
            print
            print _("Available commands are:")
            commands = self.commands.keys()
            commands.sort()
            for command in commands:
                print '\t', command[len(self.CMD_PREFIX):]
                
        else:
            print _('Available commands about %s:') % command
            print
            for command_help_method in self._topics[command]:
                try:
                    if callable(command_help_method):
                        self._print_help(*command_help_method())
                    else:
                        self._print_help(*command_help_method)
                except:
                    import traceback
                    traceback.print_exc()
                    print 'ERROR in help method %s'% (
                        command_help_method.func_name)
                
    help_do_help = ("help", "help [topic|command]",
                    _("print help message for the given topic/command or \
available topics when no argument"))

    def do_quit(self):
        """quit the CLI"""
        raise EOFError()
    
    def help_do_quit(self):
        return ("quit", "quit", _("quit the application"))