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
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?
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
31 # 5. Instatiate, parse_out and run object like in One-command
32 # 6. Run object.main() . Again, catch ClientErrors and, probably, syntax errors
34 #Monkey-patch everything for gevent early on
35 gevent.monkey.patch_all()
39 from .config import Config
40 from .errors import CLISyntaxError
42 class Argument(object):
43 """An argument that can be parsed from command line or otherwise"""
45 def __init__(self, arity, help=None, parsed_name=None, default=None):
46 self.arity = int(arity)
50 if parsed_name is not None:
51 self.parsed_name = parsed_name
52 if default is not None:
53 self.default = default
56 def parsed_name(self):
57 return getattr(self, '_parsed_name', None)
59 def parsed_name(self, newname):
60 self._parsed_name = getattr(self, '_parsed_name', [])
61 if isinstance(newname, list) or isinstance(newname, tuple):
62 self._parsed_name += list(newname)
64 self._parsed_name.append(unicode(newname))
68 return getattr(self, '_help', None)
70 def help(self, newhelp):
71 self._help = unicode(newhelp)
75 return getattr(self, '_arity', None)
77 def arity(self, newarity):
78 newarity = int(newarity)
80 self._arity = newarity
84 if not hasattr(self, '_default'):
85 self._default = False if self.arity == 0 else None
88 def default(self, newdefault):
89 self._default = newdefault
93 return getattr(self, '_value', self.default)
95 def value(self, newvalue):
96 self._value = newvalue
98 def update_parser(self, parser, name):
99 """Update an argument parser with this argument info"""
100 action = 'store_true' if self.arity==0 else 'store'
101 parser.add_argument(*self.parsed_name, dest=name, action=action,
102 default=self.default, help=self.help)
105 """Overide this method to give functionality to ur args"""
106 raise NotImplementedError
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')
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')
120 args, argv = parser.parse_known_args()
121 print('args: %s\nargv: %s'%(args, argv))
123 class VersionArgument(Argument):
126 return super(self.__class__, self).value
128 def value(self, newvalue):
129 self._value = newvalue
135 print('kamaki %s'%kamaki.__version__)
137 class ConfigArgument(Argument):
140 return super(self.__class__, self).value
142 def value(self, config_file):
143 self._value = Config(config_file) if config_file is not None else Config()
145 def get_groups(self):
146 return self.value.apis()
149 _config_arg = ConfigArgument(1, 'Path to configuration file', '--config')
151 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
158 return super(self.__class__, self).value
160 def value(self, options):
161 if options == self.default:
163 options = [unicode(options)] if not isinstance(options, list) else options
164 for option in options:
165 keypath, sep, val = option.partition('=')
167 raise CLISyntaxError(details='Missing = between key and value: -o section.key=val')
168 section, sep, key = keypath.partition('.')
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())
173 _arguments = dict(config = _config_arg,
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'))
182 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