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>
This commit is contained in:
Rohit Yadav 2013-02-04 17:55:11 +05:30
parent 6a601ba068
commit a2e89c4200
3 changed files with 73 additions and 67 deletions

View File

@ -56,6 +56,8 @@ def savecache(apicache, json_file):
""" """
Saves apicache dictionary as json_file, returns dictionary as indented str Saves apicache dictionary as json_file, returns dictionary as indented str
""" """
if isinstance(type(apicache), types.NoneType) or apicache is None or apicache is {}:
return ""
apicachestr = json.dumps(apicache, indent=2) apicachestr = json.dumps(apicache, indent=2)
with open(json_file, 'w') as cache_file: with open(json_file, 'w') as cache_file:
cache_file.write(apicachestr) cache_file.write(apicachestr)
@ -81,8 +83,9 @@ def monkeycache(apis):
""" """
Feed this a dictionary of api bananas, it spits out processed cache Feed this a dictionary of api bananas, it spits out processed cache
""" """
if isinstance(type(apis), types.NoneType): if isinstance(type(apis), types.NoneType) or apis is None:
return {} return {}
responsekey = filter(lambda x: 'response' in x, apis.keys()) responsekey = filter(lambda x: 'response' in x, apis.keys())
if len(responsekey) == 0: if len(responsekey) == 0:

View File

@ -28,20 +28,14 @@ try:
import sys import sys
import types import types
from urllib2 import HTTPError, URLError from cachemaker import loadcache, savecache, monkeycache, splitverbsubject
from httplib import BadStatusLine
from config import __version__, cache_file from config import __version__, cache_file
from config import read_config, write_config from config import read_config, write_config
from prettytable import PrettyTable
from printer import monkeyprint from printer import monkeyprint
from requester import monkeyrequest from requester import monkeyrequest
from cachemaker import loadcache, savecache, monkeycache
from cachemaker import splitverbsubject
from prettytable import PrettyTable
except ImportError, e: except ImportError, e:
print "Import error in %s : %s" % (__name__, e) print("Import error in %s : %s" % (__name__, e))
import sys import sys
sys.exit() sys.exit()
@ -50,11 +44,10 @@ try:
except ImportError: except ImportError:
apicache = {} apicache = {}
# Fix autocompletion issue, can be put in .pythonstartup
try: try:
import readline import readline
except ImportError, e: except ImportError, e:
print "Module readline not found, autocompletions will fail", e print("Module readline not found, autocompletions will fail", e)
else: else:
import rlcompleter import rlcompleter
if 'libedit' in readline.__doc__: if 'libedit' in readline.__doc__:
@ -71,8 +64,6 @@ class CloudMonkeyShell(cmd.Cmd, object):
". Type help or ? to list commands.\n") ". Type help or ? to list commands.\n")
ruler = "=" ruler = "="
cache_file = cache_file cache_file = cache_file
## datastructure {'verb': {cmd': ['api', [params], doc, required=[]]}}
#cache_verbs = apicache
config_options = [] config_options = []
def __init__(self, pname): def __init__(self, pname):
@ -91,9 +82,9 @@ class CloudMonkeyShell(cmd.Cmd, object):
try: try:
if os.path.exists(self.history_file): if os.path.exists(self.history_file):
readline.read_history_file(self.history_file) readline.read_history_file(self.history_file)
atexit.register(readline.write_history_file, self.history_file) except IOError, e:
except IOError: logger.debug("Error: Unable to read history. " + str(e))
monkeyprint("Error: history support") atexit.register(readline.write_history_file, self.history_file)
def get_attr(self, field): def get_attr(self, field):
return getattr(self, field) return getattr(self, field)
@ -105,7 +96,7 @@ class CloudMonkeyShell(cmd.Cmd, object):
pass pass
def cmdloop(self, intro=None): def cmdloop(self, intro=None):
print self.intro print(self.intro)
while True: while True:
try: try:
super(CloudMonkeyShell, self).cmdloop(intro="") super(CloudMonkeyShell, self).cmdloop(intro="")
@ -118,7 +109,31 @@ class CloudMonkeyShell(cmd.Cmd, object):
self.apicache = loadcache(self.cache_file) self.apicache = loadcache(self.cache_file)
else: else:
self.apicache = apicache self.apicache = apicache
self.verbs = apicache['verbs'] if 'verbs' in self.apicache:
self.verbs = self.apicache['verbs']
for verb in self.verbs:
def add_grammar(verb):
def grammar_closure(self, args):
if self.pipe_runner("%s %s" % (verb, args)):
return
if ' --help' in args or ' -h' in args:
self.do_help("%s %s" % (verb, args))
return
try:
args_partition = args.partition(" ")
cmd = self.apicache[verb][args_partition[0]]['name']
args = args_partition[2]
except KeyError, e:
self.monkeyprint("Error: invalid %s api arg" % verb, e)
return
self.default("%s %s" % (cmd, args))
return grammar_closure
grammar_handler = add_grammar(verb)
grammar_handler.__doc__ = "%ss resources" % verb.capitalize()
grammar_handler.__name__ = 'do_' + str(verb)
setattr(self.__class__, grammar_handler.__name__, grammar_handler)
def monkeyprint(self, *args): def monkeyprint(self, *args):
output = "" output = ""
@ -128,12 +143,12 @@ class CloudMonkeyShell(cmd.Cmd, object):
continue continue
output += str(arg) output += str(arg)
except Exception, e: except Exception, e:
print e print(e)
if self.color == 'true': if self.color == 'true':
monkeyprint(output) monkeyprint(output)
else: else:
print output print(output)
def print_result(self, result, result_filter=None): def print_result(self, result, result_filter=None):
if result is None or len(result) == 0: if result is None or len(result) == 0:
@ -219,7 +234,7 @@ class CloudMonkeyShell(cmd.Cmd, object):
next_val = lexp.next() next_val = lexp.next()
if next_val is None: if next_val is None:
break break
args.append(next_val) args.append(next_val.replace('\x00', ''))
args_dict = dict(map(lambda x: [x.partition("=")[0], args_dict = dict(map(lambda x: [x.partition("=")[0],
x.partition("=")[2]], x.partition("=")[2]],
@ -230,15 +245,21 @@ class CloudMonkeyShell(cmd.Cmd, object):
map(lambda x: x.strip(), map(lambda x: x.strip(),
args_dict.pop('filter').split(','))) args_dict.pop('filter').split(',')))
missing_args = filter(lambda x: x not in args_dict.keys(), missing_args = []
self.apicache[verb][subject]['requiredparams']) if verb in self.apicache:
missing_args = filter(lambda x: x not in args_dict.keys(),
self.apicache[verb][subject]['requiredparams'])
if len(missing_args) > 0: if len(missing_args) > 0:
self.monkeyprint("Missing arguments: ", ' '.join(missing_args)) self.monkeyprint("Missing arguments: ", ' '.join(missing_args))
return return
result = self.make_request(apiname, args_dict, isasync = False
apiname in self.apicache['asyncapis']) if 'asyncapis' in self.apicache:
isasync = apiname in self.apicache['asyncapis']
result = self.make_request(apiname, args_dict, isasync)
if result is None: if result is None:
return return
try: try:
@ -285,6 +306,9 @@ class CloudMonkeyShell(cmd.Cmd, object):
""" """
response = self.make_request("listApis") response = self.make_request("listApis")
self.apicache = monkeycache(response) self.apicache = monkeycache(response)
if response is None:
monkeyprint("Failed to sync apis, check your config")
return
savecache(self.apicache, self.cache_file) savecache(self.apicache, self.cache_file)
self.loadcache() self.loadcache()
@ -361,9 +385,19 @@ class CloudMonkeyShell(cmd.Cmd, object):
else: else:
verb = fields[0] verb = fields[0]
subject = fields[2].partition(" ")[0] subject = fields[2].partition(" ")[0]
if subject in self.apicache[verb]:
if subject in self.cache_verbs[verb]: api = self.apicache[verb][subject]
self.monkeyprint(self.cache_verbs[verb][subject][2]) helpdoc = "(%s) %s" % (api['name'], api['description'])
helpdoc = api['description']
if api['isasync']:
helpdoc += "\nThis API is asynchronous."
required = api['requiredparams']
if len(required) > 0:
helpdoc += "\nRequired params are %s" % ' '.join(required)
helpdoc += "\nParameters\n" + "=" * 10
for param in api['params']:
helpdoc += "\n%s = (%s) %s" % (param['name'], param['type'], param['description'])
self.monkeyprint(helpdoc)
else: else:
self.monkeyprint("Error: no such api (%s) on %s" % self.monkeyprint("Error: no such api (%s) on %s" %
(subject, verb)) (subject, verb))
@ -379,6 +413,12 @@ class CloudMonkeyShell(cmd.Cmd, object):
text = subfields[2] text = subfields[2]
return self.completedefault(text, line, begidx, endidx) return self.completedefault(text, line, begidx, endidx)
def do_EOF(self, args):
"""
Quit on Ctrl+d or EOF
"""
sys.exit()
def do_exit(self, args): def do_exit(self, args):
""" """
Quit CloudMonkey CLI Quit CloudMonkey CLI
@ -392,45 +432,8 @@ class CloudMonkeyShell(cmd.Cmd, object):
self.monkeyprint("Bye!") self.monkeyprint("Bye!")
return self.do_EOF(args) return self.do_EOF(args)
def do_EOF(self, args):
"""
Quit on Ctrl+d or EOF
"""
sys.exit()
def main(): def main():
verbs = []
if os.path.exists(cache_file):
verbs = loadcache(cache_file)['verbs']
elif 'verbs' in apicache:
verbs = apicache['verbs']
for verb in verbs:
def add_grammar(verb):
def grammar_closure(self, args):
if self.pipe_runner("%s %s" % (verb, args)):
return
try:
args_partition = args.partition(" ")
api = self.apicache[verb][args_partition[0]]
cmd = api['name']
helpdoc = api['description']
args = args_partition[2]
except KeyError, e:
self.monkeyprint("Error: invalid %s api arg" % verb, e)
return
if ' --help' in args or ' -h' in args:
self.monkeyprint(helpdoc)
return
self.default("%s %s" % (cmd, args))
return grammar_closure
grammar_handler = add_grammar(verb)
grammar_handler.__doc__ = "%ss resources" % verb.capitalize()
grammar_handler.__name__ = 'do_' + str(verb)
setattr(CloudMonkeyShell, grammar_handler.__name__, grammar_handler)
shell = CloudMonkeyShell(sys.argv[0]) shell = CloudMonkeyShell(sys.argv[0])
if len(sys.argv) > 1: if len(sys.argv) > 1:
shell.onecmd(' '.join(sys.argv[1:])) shell.onecmd(' '.join(sys.argv[1:]))

View File

@ -69,10 +69,10 @@ class MonkeyLexer(RegexLexer):
(r'(?:\b\d+\b(?:-\b\d+|%)?)', Number), (r'(?:\b\d+\b(?:-\b\d+|%)?)', Number),
(r'^[-=]*\n', Operator.Word), (r'^[-=]*\n', Operator.Word),
(r'Error', Error), (r'Error', Error),
(makelistre(keywords), Keyword),
(makelistre(attributes), Literal), (makelistre(attributes), Literal),
(makelistre(params) + r'( = )(.*)', bygroups(Name, Operator, (makelistre(params) + r'( = )(.*)', bygroups(Name, Operator,
String)), String)),
(makelistre(keywords), Keyword),
(makelistre(params), Name), (makelistre(params), Name),
(r'(^[a-zA-Z]* )(=)', bygroups(Name, Operator)), (r'(^[a-zA-Z]* )(=)', bygroups(Name, Operator)),
(r'\S+', Text), (r'\S+', Text),