| # Licensed to the Apache Software Foundation (ASF) under one | 
 | # or more contributor license agreements.  See the NOTICE file | 
 | # distributed with this work for additional information | 
 | # regarding copyright ownership.  The ASF licenses this file | 
 | # to you under the Apache License, Version 2.0 (the | 
 | # "License"); you may not use this file except in compliance | 
 | # with the License.  You may obtain a copy of the License at | 
 | #  | 
 | #   http://www.apache.org/licenses/LICENSE-2.0 | 
 | #  | 
 | # Unless required by applicable law or agreed to in writing, | 
 | # software distributed under the License is distributed on an | 
 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
 | # KIND, either express or implied.  See the License for the | 
 | # specific language governing permissions and limitations | 
 | # under the License. | 
 |  | 
 |   | 
 |  | 
 | ''' | 
 | Created on Aug 2, 2010 | 
 |  | 
 | ''' | 
 |  | 
 |  | 
 | import sys | 
 | import os | 
 | import inspect | 
 | from optparse import OptionParser, OptParseError, BadOptionError, OptionError, OptionConflictError, OptionValueError | 
 | import cloudapis as apis | 
 |  | 
 |  | 
 | def describe(name,desc): | 
 |     def inner(decoratee): | 
 |         if not hasattr(decoratee,"descriptions"): decoratee.descriptions = {} | 
 |         decoratee.descriptions[name] = desc | 
 |         return decoratee | 
 |     return inner | 
 |  | 
 |  | 
 | def error(msg): | 
 |     sys.stderr.write(msg) | 
 |     sys.stderr.write("\n") | 
 |  | 
 |  | 
 | class MyOptionParser(OptionParser): | 
 |     def error(self, msg): | 
 |         error("%s: %s\n" % (self.get_prog_name(),msg)) | 
 |         self.print_usage(sys.stderr) | 
 |         self.exit(os.EX_USAGE) | 
 |          | 
 |     def parse_args(self,*args,**kwargs): | 
 |         options,arguments = OptionParser.parse_args(self,*args,**kwargs) | 
 |          | 
 |         def prune_options(options,alist): | 
 |             """Given 'options' -- a list of arguments to OptionParser.add_option, | 
 |             and a set of optparse Values, return a dictionary of only those values | 
 |             that apply exclusively to 'options'""" | 
 |             return dict( [ (k,getattr(options,k)) for k in dir(options) if k in alist ] ) | 
 |          | 
 |         api_options = prune_options(options,self.api_dests) | 
 |         cmd_options = prune_options(options,self.cmd_dests) | 
 |          | 
 |         return options,arguments,api_options,cmd_options | 
 |  | 
 |  | 
 | def get_parser(api_callable=None,cmd_callable=None): # this should probably be the __init__ method of myoptionparser | 
 |      | 
 |     def getdefaulttag(default): | 
 |         if default is not None: return " [Default: %default]" | 
 |         return '' | 
 |      | 
 |     def get_arguments_and_options(callable): | 
 |         """Infers and returns arguments and options based on a callable's signature. | 
 |         Cooperates with decorator @describe""" | 
 | 	try: | 
 |         	funcargs = inspect.getargspec(callable).args | 
 |         	defaults = inspect.getargspec(callable).defaults | 
 | 	except: | 
 | 		funcargs = inspect.getargspec(callable)[0] | 
 |         	defaults = inspect.getargspec(callable)[3] | 
 |         if not defaults: defaults = [] | 
 |         args = funcargs[1:len(funcargs)-len(defaults)] # this assumes self, so assumes methods | 
 |         opts = funcargs[len(funcargs)-len(defaults):] | 
 |         try: descriptions = callable.descriptions | 
 |         except AttributeError: descriptions = {} | 
 |         arguments = [ (argname, descriptions.get(argname,'') ) for argname in args ] | 
 |         options = [ [ | 
 |                           ("--%s"%argname.replace("_","-"),), | 
 |                           { | 
 |                            "dest":argname, | 
 |                            "help":descriptions.get(argname,'') + getdefaulttag(default), | 
 |                            "default":default, | 
 |                           } | 
 |                         ] for argname,default in zip(opts,defaults) ] | 
 |         return arguments,options | 
 |  | 
 |     basic_usage = "usage: %prog [options...] " | 
 |      | 
 |     api_name = "<api>" | 
 |     cmd_name = "<command>" | 
 |     description = "%prog is a command-line tool to access several cloud APIs." | 
 |     arguments = '' | 
 |     argexp = "" | 
 |      | 
 |     if api_callable: | 
 |         api_name = api_callable.__module__.split(".")[-1].replace("_","-") | 
 |         api_arguments,api_options = get_arguments_and_options(api_callable) | 
 |         assert len(api_arguments) is 0 # no mandatory arguments for class initializers | 
 |          | 
 |     if cmd_callable: | 
 |         cmd_name = cmd_callable.func_name.replace("_","-") | 
 |         cmd_arguments,cmd_options = get_arguments_and_options(cmd_callable) | 
 |         if cmd_arguments: | 
 |             arguments   = " " + " ".join( [ s[0].upper() for s in cmd_arguments ] ) | 
 |             argexp = "\n\nArguments:\n" + "\n".join ( "  %s\n                        %s"%(s.upper(),u) for s,u in cmd_arguments ) | 
 |         description = cmd_callable.__doc__ | 
 |         | 
 |     api_command = "%s %s"%(api_name,cmd_name) | 
 |  | 
 |     if description: description = "\n\n" + description | 
 |     else: description = '' | 
 |          | 
 |     usage = basic_usage + api_command + arguments + description + argexp | 
 |  | 
 |     parser = MyOptionParser(usage=usage, add_help_option=False) | 
 |      | 
 |     parser.add_option('--help', action="help") | 
 |   | 
 |     group = parser.add_option_group("General options") | 
 |     group.add_option('-v', '--verbose', dest="verbose", help="Print extra output") | 
 |  | 
 |     parser.api_dests = [] | 
 |     if api_callable and api_options: | 
 |         group = parser.add_option_group("Options for the %s API"%api_name) | 
 |         for a in api_options: | 
 |             group.add_option(a[0][0],**a[1]) | 
 |             parser.api_dests.append(a[1]["dest"]) | 
 |   | 
 |     parser.cmd_dests = [] | 
 |     if cmd_callable and cmd_options: | 
 |         group = parser.add_option_group("Options for the %s command"%cmd_name) | 
 |         for a in cmd_options: | 
 |             group.add_option(a[0][0],**a[1]) | 
 |             parser.cmd_dests.append(a[1]["dest"]) | 
 |   | 
 |     return parser | 
 |  | 
 | def lookup_command_in_api(api,command_name): | 
 |     command = getattr(api,command_name.replace("-","_"),None) | 
 |     return command | 
 |  | 
 | def get_api_list(api): | 
 | 	apilist = [] | 
 | 	for cmd_name in dir(api): | 
 | 		cmd = getattr(api,cmd_name) | 
 |             	if callable(cmd) and not cmd_name.startswith("_"): | 
 | 			apilist.append(cmd_name)	 | 
 | 	return apilist | 
 |  | 
 | def get_command_list(api): | 
 |         cmds = [] | 
 |         for cmd_name in dir(api): | 
 |             cmd = getattr(api,cmd_name) | 
 |             if callable(cmd) and not cmd_name.startswith("_"): | 
 | 		if cmd.__doc__:docstring = cmd.__doc__ | 
 | 		else:docstring = '' | 
 | 		cmds.append( "    %s" % (cmd_name.replace('_','-')) ) | 
 |         return cmds |