summaryrefslogtreecommitdiff
path: root/plac/plac_ext.py
blob: 6c904f2f71f47a8245fee9ba60978f4f115110ac (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
import os, sys, cmd, shlex
import plac

def cmd_interface(obj):
    "Returns a cmd.Cmd wrapper over the command container"
    dic = {}
    for command in obj.commands:
        method = getattr(obj, command)
        def do_func(self, line, command=command):
            args = [command] + shlex.split(line)
            try:
                for output in plac.call(obj, args):
                    print(output)
            except SystemExit:
                print(e)
            except Exception:
                print('%s: %s' % (e.__class__.__name__, e))
        do_func.__doc__ = method.__doc__
        if sys.version >= '2.4':
            do_func.__name__ = method.__name__
        dic['do_' + command] = do_func
    clsname = '_%s_' % obj.__class__.__name__
    cls = type(clsname, (cmd.Cmd, object), dic)
    return cls()

# requires Python 2.5+
class Interpreter(object):
    """
    The safety_net is a function taking a parsing function and a list of
    arguments and applying the first to the second by managing some class
    of exceptions.
    """
    def __init__(self, obj, safety_net=lambda parse, arglist: parse(arglist),
                 commentchar='#'):
        self.obj = obj
        self.safety_net = safety_net
        self.commentchar = commentchar
        self.interpreter = None
        self.p = plac.parser_from(obj)
        self.p.error = lambda msg: sys.exit(msg) # patch the parser

    def __enter__(self):
        self.interpreter = self._make_interpreter()
        self.interpreter.send(None)
        return self

    def send(self, line):
        """
        Send a line to the underlying interpreter. 
        Return a string or None for comment lines.
        The line should end with a newline.
        """
        return self.interpreter.send(line)

    def close(self):
        self.interpreter.close()

    def __exit__(self, *exc):
        self.close()

    def _make_interpreter(self):
        enter = getattr(self.obj, '__enter__', lambda : None)
        exit = getattr(self.obj, '__exit__', lambda a1, a2, a3: None)
        enter()
        result = None
        prefix = self.p.short_prefix
        try:
            while True:
                line = yield result
                if not line:
                    break
                elif line.startswith(self.commentchar):
                    yield; continue
                arglist = shlex.split(line)
                for i, long_opt in enumerate(arglist):
                    # avoid double prefix in long options
                    if len(long_opt) > 2 and long_opt[0] == prefix:
                        arglist[i] = prefix + long_opt
                try:
                    output = self.safety_net(self.p.parselist, arglist)
                except SystemExit, e:
                    output = [str(e)]
                result = os.linesep.join(output)
        except:
            exit(*sys.exc_info())
            raise
        else:
            exit(None, None, None)

## from cmd.py
# def complete(self, text, state):
#     """Return the next possible completion for 'text'.

#     If a command has not been entered, then complete against command list.
#     Otherwise try to call complete_<command> to get list of completions.
#     """
#     if state == 0:
#         import readline
#         origline = readline.get_line_buffer()
#         line = origline.lstrip()
#         stripped = len(origline) - len(line)
#         begidx = readline.get_begidx() - stripped
#         endidx = readline.get_endidx() - stripped
#         if begidx>0:
#             cmd, args, foo = self.parseline(line)
#             if cmd == '':
#                 compfunc = self.completedefault
#             else:
#                 try:
#                     compfunc = getattr(self, 'complete_' + cmd)
#                 except AttributeError:
#                     compfunc = self.completedefault
#         else:
#             compfunc = self.completenames
#         self.completion_matches = compfunc(text, line, begidx, endidx)
#     try:
#         return self.completion_matches[state]
#     except IndexError:
#         return None

# def readlines(completekey='tab'):
#     if readline:
#         old_completer = readline.get_completer()
#         readline.set_completer(self.complete)
#         readline.parse_and_bind(self.completekey + ": complete")
#     try:
#         while True:
#             try:
#                 yield raw_input('cli> ')
#             except EOFError:
#                 break
#     finally:
#         if readline:
#             readline.set_completer(old_completer)

class Cmds(object):
    commands = 'checkout', 'commit', 'status', 'help'
    quit = False

    @plac.annotations(
        name=('a recognized command', 'positional', None, str, commands))
    def help(self, name):
        return self.p.subp[name].format_help()

    def checkout(self, url):
        return ('checkout', url)

    def commit(self):
        return ('commit')

    @plac.annotations(quiet=('summary information', 'flag'))
    def status(self, quiet):
        return ('status', quiet)

if __name__ == '__main__':
    cmdloop(Cmds())