Revision c41a86b2 kamaki/cli/argument.py
b/kamaki/cli/argument.py | ||
---|---|---|
1 | 1 |
#A. One-command CLI |
2 |
# 1. Get a command string DONE
|
|
3 |
# 2. Parse out some Arguments DONE
|
|
4 |
# a. We need an Argument "library" for each command-level DONE
|
|
5 |
# b. Handle arg errors
|
|
6 |
# 3. Retrieve and validate command_sequence
|
|
7 |
# a. For faster responses, first command can be chosen from
|
|
8 |
# a prefixed list of names, loaded from the config file
|
|
9 |
# b. Normally, each 1st level command has a file to read
|
|
10 |
# command specs from. Load command_specs in this file
|
|
11 |
# i. A dict with command specs is created
|
|
12 |
# e.g. {'store':{'list':{'all', None}, 'info'}, 'server':{'list', 'info'}}
|
|
13 |
# but in this case there will be only 'store', or 'server', etc.
|
|
14 |
# c. Now, loop over the other parsed terms and check them against the commands
|
|
15 |
# i. That will produce a path of the form ['store', 'list' 'all']
|
|
16 |
# d. Catch syntax errors
|
|
17 |
# 4. Instaciate object to exec
|
|
18 |
# a. For path ['store', 'list', 'all'] instatiate store_list_all()
|
|
19 |
# 5. Parse out some more Arguments
|
|
20 |
# a. Each command path has an "Argument library" to check your args against
|
|
21 |
# 6. Call object.main() and catch ClientErrors
|
|
22 |
# a. Now, there are some command-level syntax errors that we should catch
|
|
23 |
# as syntax errors? Maybe! Why not?
|
|
2 |
# 1. Get a command string DONE
|
|
3 |
# 2. Parse out some Arguments DONE
|
|
4 |
# a. We need an Argument "library" for each command-level DONE
|
|
5 |
# b. Handle arg errors
|
|
6 |
# 3. Retrieve and validate command_sequence
|
|
7 |
# a. For faster responses, first command can be chosen from
|
|
8 |
# a prefixed list of names, loaded from the config file
|
|
9 |
# b. Normally, each 1st level command has a file to read
|
|
10 |
# command specs from. Load command_specs in this file
|
|
11 |
# i. A dict with command specs is created
|
|
12 |
# e.g. {'store':{'list':{'all', None}, 'info'}, 'server':{'list', 'info'}}
|
|
13 |
# but in this case there will be only 'store', or 'server', etc.
|
|
14 |
# c. Now, loop over the other parsed terms and check them against the commands
|
|
15 |
# i. That will produce a path of the form ['store', 'list' 'all']
|
|
16 |
# d. Catch syntax errors
|
|
17 |
# 4. Instaciate object to exec
|
|
18 |
# a. For path ['store', 'list', 'all'] instatiate store_list_all()
|
|
19 |
# 5. Parse out some more Arguments
|
|
20 |
# a. Each command path has an "Argument library" to check your args against
|
|
21 |
# 6. Call object.main() and catch ClientErrors
|
|
22 |
# a. Now, there are some command-level syntax errors that we should catch
|
|
23 |
# as syntax errors? Maybe! Why not?
|
|
24 | 24 |
|
25 | 25 |
#Shell |
26 |
# 1. Load ALL available command specs in advance
|
|
27 |
# 2. Iimport cmd (and run it ?)
|
|
28 |
# 3. There is probably a way to tell cmd of the command paths you support.
|
|
29 |
# 4. If cmd does not support it, for the sellected path call parse out stuff
|
|
30 |
# as in One-command
|
|
31 |
# 5. Instatiate, parse_out and run object like in One-command
|
|
32 |
# 6. Run object.main() . Again, catch ClientErrors and, probably, syntax errors
|
|
26 |
# 1. Load ALL available command specs in advance
|
|
27 |
# 2. Iimport cmd (and run it ?)
|
|
28 |
# 3. There is probably a way to tell cmd of the command paths you support.
|
|
29 |
# 4. If cmd does not support it, for the sellected path call parse out stuff
|
|
30 |
# as in One-command
|
|
31 |
# 5. Instatiate, parse_out and run object like in One-command
|
|
32 |
# 6. Run object.main() . Again, catch ClientErrors and, probably, syntax errors
|
|
33 | 33 |
import gevent.monkey |
34 | 34 |
#Monkey-patch everything for gevent early on |
35 | 35 |
gevent.monkey.patch_all() |
... | ... | |
105 | 105 |
"""Overide this method to give functionality to ur args""" |
106 | 106 |
raise NotImplementedError |
107 | 107 |
|
108 |
@classmethod |
|
109 |
def test(self): |
|
110 |
h = Argument(arity=0, help='Display a help massage', parsed_name=('--help', '-h')) |
|
111 |
b = Argument(arity=1, help='This is a bbb', parsed_name='--bbb') |
|
112 |
c = Argument(arity=2, help='This is a ccc', parsed_name='--ccc') |
|
113 |
|
|
114 |
from argparse import ArgumentParser |
|
115 |
parser = ArgumentParser(add_help=False) |
|
116 |
h.update_parser(parser, 'hee') |
|
117 |
b.update_parser(parser, 'bee') |
|
118 |
c.update_parser(parser, 'cee') |
|
119 |
|
|
120 |
args, argv = parser.parse_known_args() |
|
121 |
print('args: %s\nargv: %s'%(args, argv)) |
|
122 |
|
|
123 |
class VersionArgument(Argument): |
|
124 |
@property |
|
125 |
def value(self): |
|
126 |
return super(self.__class__, self).value |
|
127 |
@value.setter |
|
128 |
def value(self, newvalue): |
|
129 |
self._value = newvalue |
|
130 |
self.main() |
|
131 |
|
|
132 |
def main(self): |
|
133 |
if self.value: |
|
134 |
import kamaki |
|
135 |
print('kamaki %s'%kamaki.__version__) |
|
136 |
|
|
137 | 108 |
class ConfigArgument(Argument): |
138 |
@property
|
|
139 |
def value(self):
|
|
140 |
return super(self.__class__, self).value
|
|
141 |
@value.setter
|
|
142 |
def value(self, config_file):
|
|
143 |
self._value = Config(config_file) if config_file is not None else Config()
|
|
109 |
@property
|
|
110 |
def value(self):
|
|
111 |
return super(self.__class__, self).value
|
|
112 |
@value.setter
|
|
113 |
def value(self, config_file):
|
|
114 |
self._value = Config(config_file) if config_file is not None else Config()
|
|
144 | 115 |
|
145 |
def get_groups(self): |
|
146 |
return self.value.apis() |
|
116 |
def get(self, group, term): |
|
117 |
return self.value.get(group, term) |
|
118 |
|
|
119 |
def get_groups(self): |
|
120 |
return self.value.apis() |
|
147 | 121 |
|
148 | 122 |
|
149 | 123 |
_config_arg = ConfigArgument(1, 'Path to configuration file', '--config') |
150 | 124 |
|
151 | 125 |
class CmdLineConfigArgument(Argument): |
152 |
def __init__(self, config_arg, help='', parsed_name=None, default=None): |
|
153 |
super(self.__class__, self).__init__(1, help, parsed_name, default) |
|
154 |
self._config_arg = config_arg |
|
155 |
|
|
156 |
@property |
|
157 |
def value(self): |
|
158 |
return super(self.__class__, self).value |
|
159 |
@value.setter |
|
160 |
def value(self, options): |
|
161 |
if options == self.default: |
|
162 |
return |
|
163 |
options = [unicode(options)] if not isinstance(options, list) else options |
|
164 |
for option in options: |
|
165 |
keypath, sep, val = option.partition('=') |
|
166 |
if not sep: |
|
167 |
raise CLISyntaxError(details='Missing = between key and value: -o section.key=val') |
|
168 |
section, sep, key = keypath.partition('.') |
|
169 |
if not sep: |
|
170 |
raise CLISyntaxError(details='Missing . between section and key: -o section.key=val') |
|
171 |
self._config_arg.value.override(section.strip(), key.strip(), val.strip()) |
|
126 |
def __init__(self, config_arg, help='', parsed_name=None, default=None): |
|
127 |
super(self.__class__, self).__init__(1, help, parsed_name, default) |
|
128 |
self._config_arg = config_arg |
|
129 |
|
|
130 |
@property |
|
131 |
def value(self): |
|
132 |
return super(self.__class__, self).value |
|
133 |
@value.setter |
|
134 |
def value(self, options): |
|
135 |
if options == self.default: |
|
136 |
return |
|
137 |
options = [unicode(options)] if not isinstance(options, list) else options |
|
138 |
for option in options: |
|
139 |
keypath, sep, val = option.partition('=') |
|
140 |
if not sep: |
|
141 |
raise CLISyntaxError(details='Missing = between key and value: -o section.key=val') |
|
142 |
section, sep, key = keypath.partition('.') |
|
143 |
if not sep: |
|
144 |
raise CLISyntaxError(details='Missing . between section and key: -o section.key=val') |
|
145 |
self._config_arg.value.override(section.strip(), key.strip(), val.strip()) |
|
146 |
|
|
147 |
class FlagArgument(Argument): |
|
148 |
def __init__(self, help='', parsed_name=None, default=None): |
|
149 |
super(FlagArgument, self).__init__(0, help, parsed_name, default) |
|
150 |
|
|
151 |
class ValueArgument(Argument): |
|
152 |
def __init__(self, help='', parsed_name=None, default=None): |
|
153 |
super(ValueArgument, self).__init__(1, help, parsed_name, default) |
|
154 |
|
|
155 |
class VersionArgument(FlagArgument): |
|
156 |
@property |
|
157 |
def value(self): |
|
158 |
return super(self.__class__, self).value |
|
159 |
@value.setter |
|
160 |
def value(self, newvalue): |
|
161 |
self._value = newvalue |
|
162 |
self.main() |
|
163 |
|
|
164 |
def main(self): |
|
165 |
if self.value: |
|
166 |
import kamaki |
|
167 |
print('kamaki %s'%kamaki.__version__) |
|
172 | 168 |
|
173 | 169 |
_arguments = dict(config = _config_arg, help = Argument(0, 'Show help message', ('-h', '--help')), |
174 |
debug = Argument(0, 'Include debug output', ('-d', '--debug')),
|
|
175 |
include = Argument(0, 'Include protocol headers in the output', ('-i', '--include')),
|
|
176 |
silent = Argument(0, 'Do not output anything', ('-s', '--silent')),
|
|
177 |
verbose = Argument(0, 'More info at response', ('-v', '--verbose')),
|
|
178 |
version = VersionArgument(0, 'Print current version', ('-V', '--version')),
|
|
179 |
options = CmdLineConfigArgument(_config_arg, 'Override a config value', ('-o', '--options'))
|
|
170 |
debug = FlagArgument('Include debug output', ('-d', '--debug')),
|
|
171 |
include = FlagArgument('Include protocol headers in the output', ('-i', '--include')),
|
|
172 |
silent = FlagArgument('Do not output anything', ('-s', '--silent')),
|
|
173 |
verbose = FlagArgument('More info at response', ('-v', '--verbose')),
|
|
174 |
version = VersionArgument('Print current version', ('-V', '--version')),
|
|
175 |
options = CmdLineConfigArgument(_config_arg, 'Override a config value', ('-o', '--options'))
|
|
180 | 176 |
) |
181 | 177 |
|
182 | 178 |
def parse_known_args(parser): |
183 |
parsed, unparsed = parser.parse_known_args()
|
|
184 |
for name, arg in _arguments.items():
|
|
185 |
arg.value = getattr(parsed, name, arg.value)
|
|
186 |
return parsed, unparsed
|
|
179 |
parsed, unparsed = parser.parse_known_args()
|
|
180 |
for name, arg in _arguments.items():
|
|
181 |
arg.value = getattr(parsed, name, arg.value)
|
|
182 |
return parsed, unparsed
|
|
187 | 183 |
|
188 | 184 |
|
Also available in: Unified diff