Skip to content

Commit a2e89c4

Browse files
committed
cli: Use metaprogramming to generate code during runtime when new apis sync
- Fixes metaprogramming to inject methods during shell class object's runtime - This enable new apis that are synced to come up with new verbs, docs etc. - Fixes printing issue - Fixes null bitfield parsing issue Signed-off-by: Rohit Yadav <bhaisaab@apache.org>
1 parent 6a601ba commit a2e89c4

3 files changed

Lines changed: 73 additions & 67 deletions

File tree

tools/cli/cloudmonkey/cachemaker.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ def savecache(apicache, json_file):
5656
"""
5757
Saves apicache dictionary as json_file, returns dictionary as indented str
5858
"""
59+
if isinstance(type(apicache), types.NoneType) or apicache is None or apicache is {}:
60+
return ""
5961
apicachestr = json.dumps(apicache, indent=2)
6062
with open(json_file, 'w') as cache_file:
6163
cache_file.write(apicachestr)
@@ -81,8 +83,9 @@ def monkeycache(apis):
8183
"""
8284
Feed this a dictionary of api bananas, it spits out processed cache
8385
"""
84-
if isinstance(type(apis), types.NoneType):
86+
if isinstance(type(apis), types.NoneType) or apis is None:
8587
return {}
88+
8689
responsekey = filter(lambda x: 'response' in x, apis.keys())
8790

8891
if len(responsekey) == 0:

tools/cli/cloudmonkey/cloudmonkey.py

Lines changed: 68 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,14 @@
2828
import sys
2929
import types
3030

31-
from urllib2 import HTTPError, URLError
32-
from httplib import BadStatusLine
33-
31+
from cachemaker import loadcache, savecache, monkeycache, splitverbsubject
3432
from config import __version__, cache_file
3533
from config import read_config, write_config
36-
34+
from prettytable import PrettyTable
3735
from printer import monkeyprint
3836
from requester import monkeyrequest
39-
from cachemaker import loadcache, savecache, monkeycache
40-
from cachemaker import splitverbsubject
41-
42-
from prettytable import PrettyTable
4337
except ImportError, e:
44-
print "Import error in %s : %s" % (__name__, e)
38+
print("Import error in %s : %s" % (__name__, e))
4539
import sys
4640
sys.exit()
4741

@@ -50,11 +44,10 @@
5044
except ImportError:
5145
apicache = {}
5246

53-
# Fix autocompletion issue, can be put in .pythonstartup
5447
try:
5548
import readline
5649
except ImportError, e:
57-
print "Module readline not found, autocompletions will fail", e
50+
print("Module readline not found, autocompletions will fail", e)
5851
else:
5952
import rlcompleter
6053
if 'libedit' in readline.__doc__:
@@ -71,8 +64,6 @@ class CloudMonkeyShell(cmd.Cmd, object):
7164
". Type help or ? to list commands.\n")
7265
ruler = "="
7366
cache_file = cache_file
74-
## datastructure {'verb': {cmd': ['api', [params], doc, required=[]]}}
75-
#cache_verbs = apicache
7667
config_options = []
7768

7869
def __init__(self, pname):
@@ -91,9 +82,9 @@ def __init__(self, pname):
9182
try:
9283
if os.path.exists(self.history_file):
9384
readline.read_history_file(self.history_file)
94-
atexit.register(readline.write_history_file, self.history_file)
95-
except IOError:
96-
monkeyprint("Error: history support")
85+
except IOError, e:
86+
logger.debug("Error: Unable to read history. " + str(e))
87+
atexit.register(readline.write_history_file, self.history_file)
9788

9889
def get_attr(self, field):
9990
return getattr(self, field)
@@ -105,7 +96,7 @@ def emptyline(self):
10596
pass
10697

10798
def cmdloop(self, intro=None):
108-
print self.intro
99+
print(self.intro)
109100
while True:
110101
try:
111102
super(CloudMonkeyShell, self).cmdloop(intro="")
@@ -118,7 +109,31 @@ def loadcache(self):
118109
self.apicache = loadcache(self.cache_file)
119110
else:
120111
self.apicache = apicache
121-
self.verbs = apicache['verbs']
112+
if 'verbs' in self.apicache:
113+
self.verbs = self.apicache['verbs']
114+
115+
for verb in self.verbs:
116+
def add_grammar(verb):
117+
def grammar_closure(self, args):
118+
if self.pipe_runner("%s %s" % (verb, args)):
119+
return
120+
if ' --help' in args or ' -h' in args:
121+
self.do_help("%s %s" % (verb, args))
122+
return
123+
try:
124+
args_partition = args.partition(" ")
125+
cmd = self.apicache[verb][args_partition[0]]['name']
126+
args = args_partition[2]
127+
except KeyError, e:
128+
self.monkeyprint("Error: invalid %s api arg" % verb, e)
129+
return
130+
self.default("%s %s" % (cmd, args))
131+
return grammar_closure
132+
133+
grammar_handler = add_grammar(verb)
134+
grammar_handler.__doc__ = "%ss resources" % verb.capitalize()
135+
grammar_handler.__name__ = 'do_' + str(verb)
136+
setattr(self.__class__, grammar_handler.__name__, grammar_handler)
122137

123138
def monkeyprint(self, *args):
124139
output = ""
@@ -128,12 +143,12 @@ def monkeyprint(self, *args):
128143
continue
129144
output += str(arg)
130145
except Exception, e:
131-
print e
146+
print(e)
132147

133148
if self.color == 'true':
134149
monkeyprint(output)
135150
else:
136-
print output
151+
print(output)
137152

138153
def print_result(self, result, result_filter=None):
139154
if result is None or len(result) == 0:
@@ -219,7 +234,7 @@ def default(self, args):
219234
next_val = lexp.next()
220235
if next_val is None:
221236
break
222-
args.append(next_val)
237+
args.append(next_val.replace('\x00', ''))
223238

224239
args_dict = dict(map(lambda x: [x.partition("=")[0],
225240
x.partition("=")[2]],
@@ -230,15 +245,21 @@ def default(self, args):
230245
map(lambda x: x.strip(),
231246
args_dict.pop('filter').split(',')))
232247

233-
missing_args = filter(lambda x: x not in args_dict.keys(),
234-
self.apicache[verb][subject]['requiredparams'])
248+
missing_args = []
249+
if verb in self.apicache:
250+
missing_args = filter(lambda x: x not in args_dict.keys(),
251+
self.apicache[verb][subject]['requiredparams'])
235252

236253
if len(missing_args) > 0:
237254
self.monkeyprint("Missing arguments: ", ' '.join(missing_args))
238255
return
239256

240-
result = self.make_request(apiname, args_dict,
241-
apiname in self.apicache['asyncapis'])
257+
isasync = False
258+
if 'asyncapis' in self.apicache:
259+
isasync = apiname in self.apicache['asyncapis']
260+
261+
result = self.make_request(apiname, args_dict, isasync)
262+
242263
if result is None:
243264
return
244265
try:
@@ -285,6 +306,9 @@ def do_sync(self, args):
285306
"""
286307
response = self.make_request("listApis")
287308
self.apicache = monkeycache(response)
309+
if response is None:
310+
monkeyprint("Failed to sync apis, check your config")
311+
return
288312
savecache(self.apicache, self.cache_file)
289313
self.loadcache()
290314

@@ -361,9 +385,19 @@ def do_help(self, args):
361385
else:
362386
verb = fields[0]
363387
subject = fields[2].partition(" ")[0]
364-
365-
if subject in self.cache_verbs[verb]:
366-
self.monkeyprint(self.cache_verbs[verb][subject][2])
388+
if subject in self.apicache[verb]:
389+
api = self.apicache[verb][subject]
390+
helpdoc = "(%s) %s" % (api['name'], api['description'])
391+
helpdoc = api['description']
392+
if api['isasync']:
393+
helpdoc += "\nThis API is asynchronous."
394+
required = api['requiredparams']
395+
if len(required) > 0:
396+
helpdoc += "\nRequired params are %s" % ' '.join(required)
397+
helpdoc += "\nParameters\n" + "=" * 10
398+
for param in api['params']:
399+
helpdoc += "\n%s = (%s) %s" % (param['name'], param['type'], param['description'])
400+
self.monkeyprint(helpdoc)
367401
else:
368402
self.monkeyprint("Error: no such api (%s) on %s" %
369403
(subject, verb))
@@ -379,6 +413,12 @@ def complete_help(self, text, line, begidx, endidx):
379413
text = subfields[2]
380414
return self.completedefault(text, line, begidx, endidx)
381415

416+
def do_EOF(self, args):
417+
"""
418+
Quit on Ctrl+d or EOF
419+
"""
420+
sys.exit()
421+
382422
def do_exit(self, args):
383423
"""
384424
Quit CloudMonkey CLI
@@ -392,45 +432,8 @@ def do_quit(self, args):
392432
self.monkeyprint("Bye!")
393433
return self.do_EOF(args)
394434

395-
def do_EOF(self, args):
396-
"""
397-
Quit on Ctrl+d or EOF
398-
"""
399-
sys.exit()
400-
401435

402436
def main():
403-
verbs = []
404-
if os.path.exists(cache_file):
405-
verbs = loadcache(cache_file)['verbs']
406-
elif 'verbs' in apicache:
407-
verbs = apicache['verbs']
408-
409-
for verb in verbs:
410-
def add_grammar(verb):
411-
def grammar_closure(self, args):
412-
if self.pipe_runner("%s %s" % (verb, args)):
413-
return
414-
try:
415-
args_partition = args.partition(" ")
416-
api = self.apicache[verb][args_partition[0]]
417-
cmd = api['name']
418-
helpdoc = api['description']
419-
args = args_partition[2]
420-
except KeyError, e:
421-
self.monkeyprint("Error: invalid %s api arg" % verb, e)
422-
return
423-
if ' --help' in args or ' -h' in args:
424-
self.monkeyprint(helpdoc)
425-
return
426-
self.default("%s %s" % (cmd, args))
427-
return grammar_closure
428-
429-
grammar_handler = add_grammar(verb)
430-
grammar_handler.__doc__ = "%ss resources" % verb.capitalize()
431-
grammar_handler.__name__ = 'do_' + str(verb)
432-
setattr(CloudMonkeyShell, grammar_handler.__name__, grammar_handler)
433-
434437
shell = CloudMonkeyShell(sys.argv[0])
435438
if len(sys.argv) > 1:
436439
shell.onecmd(' '.join(sys.argv[1:]))

tools/cli/cloudmonkey/printer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ def makelistre(lis):
6969
(r'(?:\b\d+\b(?:-\b\d+|%)?)', Number),
7070
(r'^[-=]*\n', Operator.Word),
7171
(r'Error', Error),
72-
(makelistre(keywords), Keyword),
7372
(makelistre(attributes), Literal),
7473
(makelistre(params) + r'( = )(.*)', bygroups(Name, Operator,
7574
String)),
75+
(makelistre(keywords), Keyword),
7676
(makelistre(params), Name),
7777
(r'(^[a-zA-Z]* )(=)', bygroups(Name, Operator)),
7878
(r'\S+', Text),

0 commit comments

Comments
 (0)