1# Licensed under the Apache License, Version 2.0 (the "License"); you may 2# not use this file except in compliance with the License. You may obtain 3# a copy of the License at 4# 5# http://www.apache.org/licenses/LICENSE-2.0 6# 7# Unless required by applicable law or agreed to in writing, software 8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10# License for the specific language governing permissions and limitations 11# under the License. 12 13import argparse 14import inspect 15import sys 16import traceback 17 18from . import command 19from . import utils 20 21 22class HelpAction(argparse.Action): 23 """Provide a custom action so the -h and --help options 24 to the main app will print a list of the commands. 25 26 The commands are determined by checking the CommandManager 27 instance, passed in as the "default" value for the action. 28 """ 29 def __call__(self, parser, namespace, values, option_string=None): 30 app = self.default 31 parser.print_help(app.stdout) 32 app.stdout.write('\nCommands:\n') 33 dists_by_module = command._get_distributions_by_modules() 34 35 def dist_for_obj(obj): 36 name = inspect.getmodule(obj).__name__.partition('.')[0] 37 return dists_by_module.get(name) 38 39 app_dist = dist_for_obj(app) 40 command_manager = app.command_manager 41 for name, ep in sorted(command_manager): 42 try: 43 factory = ep.load() 44 except Exception: 45 app.stdout.write('Could not load %r\n' % ep) 46 if namespace.debug: 47 traceback.print_exc(file=app.stdout) 48 continue 49 try: 50 kwargs = {} 51 if 'cmd_name' in utils.getargspec(factory.__init__).args: 52 kwargs['cmd_name'] = name 53 cmd = factory(app, None, **kwargs) 54 if cmd.deprecated: 55 continue 56 except Exception as err: 57 app.stdout.write('Could not instantiate %r: %s\n' % (ep, err)) 58 if namespace.debug: 59 traceback.print_exc(file=app.stdout) 60 continue 61 one_liner = cmd.get_description().split('\n')[0] 62 dist_name = dist_for_obj(factory) 63 if dist_name and dist_name != app_dist: 64 dist_info = ' (' + dist_name + ')' 65 else: 66 dist_info = '' 67 app.stdout.write(' %-13s %s%s\n' % (name, one_liner, dist_info)) 68 sys.exit(0) 69 70 71class HelpCommand(command.Command): 72 """print detailed help for another command 73 """ 74 75 def get_parser(self, prog_name): 76 parser = super(HelpCommand, self).get_parser(prog_name) 77 parser.add_argument('cmd', 78 nargs='*', 79 help='name of the command', 80 ) 81 return parser 82 83 def take_action(self, parsed_args): 84 if parsed_args.cmd: 85 try: 86 the_cmd = self.app.command_manager.find_command( 87 parsed_args.cmd, 88 ) 89 cmd_factory, cmd_name, search_args = the_cmd 90 except ValueError: 91 # Did not find an exact match 92 cmd = parsed_args.cmd[0] 93 fuzzy_matches = [k[0] for k in self.app.command_manager 94 if k[0].startswith(cmd) 95 ] 96 if not fuzzy_matches: 97 raise 98 self.app.stdout.write('Command "%s" matches:\n' % cmd) 99 for fm in sorted(fuzzy_matches): 100 self.app.stdout.write(' %s\n' % fm) 101 return 102 self.app_args.cmd = search_args 103 kwargs = {} 104 if 'cmd_name' in utils.getargspec(cmd_factory.__init__).args: 105 kwargs['cmd_name'] = cmd_name 106 cmd = cmd_factory(self.app, self.app_args, **kwargs) 107 full_name = (cmd_name 108 if self.app.interactive_mode 109 else ' '.join([self.app.NAME, cmd_name]) 110 ) 111 cmd_parser = cmd.get_parser(full_name) 112 cmd_parser.print_help(self.app.stdout) 113 else: 114 action = HelpAction(None, None, default=self.app) 115 action(self.app.parser, self.app.options, None, None) 116 return 0 117