Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
52868 views
1
"""
2
Digress's CLI interface.
3
"""
4
5
import inspect
6
import sys
7
from optparse import OptionParser
8
9
import textwrap
10
11
from types import MethodType
12
13
from digress import __version__ as version
14
15
def dispatchable(func):
16
"""
17
Mark a method as dispatchable.
18
"""
19
func.digress_dispatchable = True
20
return func
21
22
class Dispatcher(object):
23
"""
24
Dispatcher for CLI commands.
25
"""
26
def __init__(self, fixture):
27
self.fixture = fixture
28
fixture.dispatcher = self
29
30
def _monkey_print_help(self, optparse, *args, **kwargs):
31
# monkey patches OptionParser._print_help
32
OptionParser.print_help(optparse, *args, **kwargs)
33
34
print >>sys.stderr, "\nAvailable commands:"
35
36
maxlen = max([ len(command_name) for command_name in self.commands ])
37
38
descwidth = 80 - maxlen - 4
39
40
for command_name, command_meth in self.commands.iteritems():
41
print >>sys.stderr, " %s %s\n" % (
42
command_name.ljust(maxlen + 1),
43
("\n" + (maxlen + 4) * " ").join(
44
textwrap.wrap(" ".join(filter(
45
None,
46
command_meth.__doc__.strip().replace("\n", " ").split(" ")
47
)),
48
descwidth
49
)
50
)
51
)
52
53
def _enable_flush(self):
54
self.fixture.flush_before = True
55
56
def _populate_parser(self):
57
self.commands = self._get_commands()
58
59
self.optparse = OptionParser(
60
usage = "usage: %prog [options] command [args]",
61
description = "Digress CLI frontend for %s." % self.fixture.__class__.__name__,
62
version = "Digress %s" % version
63
)
64
65
self.optparse.print_help = MethodType(self._monkey_print_help, self.optparse, OptionParser)
66
67
self.optparse.add_option(
68
"-f",
69
"--flush",
70
action="callback",
71
callback=lambda option, opt, value, parser: self._enable_flush(),
72
help="flush existing data for a revision before testing"
73
)
74
75
self.optparse.add_option(
76
"-c",
77
"--cases",
78
metavar="FOO,BAR",
79
action="callback",
80
dest="cases",
81
type=str,
82
callback=lambda option, opt, value, parser: self._select_cases(*value.split(",")),
83
help="test cases to run, run with command list to see full list"
84
)
85
86
def _select_cases(self, *cases):
87
self.fixture.cases = filter(lambda case: case.__name__ in cases, self.fixture.cases)
88
89
def _get_commands(self):
90
commands = {}
91
92
for name, member in inspect.getmembers(self.fixture):
93
if hasattr(member, "digress_dispatchable"):
94
commands[name] = member
95
96
return commands
97
98
def _run_command(self, name, *args):
99
if name not in self.commands:
100
print >>sys.stderr, "error: %s is not a valid command\n" % name
101
self.optparse.print_help()
102
return
103
104
command = self.commands[name]
105
106
argspec = inspect.getargspec(command)
107
108
max_arg_len = len(argspec.args) - 1
109
min_arg_len = max_arg_len - ((argspec.defaults is not None) and len(argspec.defaults) or 0)
110
111
if len(args) < min_arg_len:
112
print >>sys.stderr, "error: %s takes at least %d arguments\n" % (
113
name,
114
min_arg_len
115
)
116
print >>sys.stderr, "%s\n" % command.__doc__
117
self.optparse.print_help()
118
return
119
120
if len(args) > max_arg_len:
121
print >>sys.stderr, "error: %s takes at most %d arguments\n" % (
122
name,
123
max_arg_len
124
)
125
print >>sys.stderr, "%s\n" % command.__doc__
126
self.optparse.print_help()
127
return
128
129
command(*args)
130
131
def pre_dispatch(self):
132
pass
133
134
def dispatch(self):
135
self._populate_parser()
136
137
self.optparse.parse_args()
138
self.pre_dispatch()
139
args = self.optparse.parse_args()[1] # arguments may require reparsing after pre_dispatch; see test_x264.py
140
141
if len(args) == 0:
142
print >>sys.stderr, "error: no comamnd specified\n"
143
self.optparse.print_help()
144
return
145
146
command = args[0]
147
addenda = args[1:]
148
149
self._run_command(command, *addenda)
150
151