def run(self, key): name = from_cli(key) mod_name = '%s.%s' % (self._PLUGIN_BASE_MODULE, name) if key is None or name == "topics": self.print_topics() return if name in self._topics: self.print_commands(name) elif name in self.Command: cmd = self.Command[name] if cmd.NO_CLI: raise HelpError(topic=name) print unicode(_('Purpose: %s')) % unicode(_(cmd.doc)).strip() self.Backend.cli.build_parser(cmd).print_help() elif mod_name in sys.modules: self.print_commands(name) elif name == "commands": mcl = max(len(s) for s in (self.Command)) for cname in self.Command: cmd = self.Command[cname] if cmd.NO_CLI: continue print '%s %s' % (to_cli(cmd.name).ljust(mcl), cmd.summary) else: raise HelpError(topic=name)
def _on_finalize(self): # {topic: ["description", mcl, {"subtopic": ["description", mcl, [commands]]}]} # {topic: ["description", mcl, [commands]]} self._topics = {} # [builtin_commands] self._builtins = [] # build help topics for c in self.Command(): if c.NO_CLI: continue topic = self._get_module_topic(c.module) topic_name = topic[0] if topic_name: if topic[1] is None: # a module without grouping if topic_name in self._topics: self._topics[topic_name][2].append(c) else: m = '%s.%s' % (self._PLUGIN_BASE_MODULE, topic_name) doc = (unicode(_(sys.modules[m].__doc__)) or '').strip().split('\n', 1)[0] self._topics[topic_name] = [doc, 0, [c]] mcl = max((self._topics[topic_name][1], len(c.name))) self._topics[topic_name][1] = mcl else: # a module grouped in a topic doc = (unicode(_(sys.modules[c.module].__doc__)) or '').strip().split('\n', 1)[0] mod_name = c.module.rsplit('.', 1)[1] if topic_name in self._topics: if mod_name in self._topics[topic_name][2]: self._topics[topic_name][2][mod_name][2].append(c) else: self._topics[topic_name][2][mod_name] = [ doc, 0, [c] ] self._count_topic_mcl(topic_name, mod_name) # count mcl for for the subtopic mcl = max((self._topics[topic_name][2][mod_name][1], len(c.name))) self._topics[topic_name][2][mod_name][1] = mcl else: self._topics[topic_name] = [ unicode(_(topic[1])), 0, { mod_name: [doc, 0, [c]] } ] self._count_topic_mcl(topic_name, mod_name) else: self._builtins.append(c) # compute maximum topic length self._mtl = max( len(s) for s in (self._topics.keys() + [c.name for c in self._builtins])) super(help, self)._on_finalize()
def _on_finalize(self): # {topic: ["description", mcl, {"subtopic": ["description", mcl, [commands]]}]} # {topic: ["description", mcl, [commands]]} self._topics = {} # [builtin_commands] self._builtins = [] # build help topics for c in self.Command(): if c.NO_CLI: continue topic = self._get_module_topic(c.module) topic_name = topic[0] if topic_name: if topic[1] is None: # a module without grouping if topic_name in self._topics: self._topics[topic_name][2].append(c) else: m = '%s.%s' % (self._PLUGIN_BASE_MODULE, topic_name) try: module = sys.modules[m] except KeyError: doc = '' else: doc = ( unicode(_(module.__doc__)) or '' ).strip().split('\n', 1)[0] self._topics[topic_name] = [doc, 0, [c]] mcl = max((self._topics[topic_name][1], len(c.name))) self._topics[topic_name][1] = mcl else: # a module grouped in a topic doc = ( unicode(_(sys.modules[c.module].__doc__)) or '' ).strip().split('\n', 1)[0] mod_name = c.module.rsplit('.',1)[1] if topic_name in self._topics: if mod_name in self._topics[topic_name][2]: self._topics[topic_name][2][mod_name][2].append(c) else: self._topics[topic_name][2][mod_name] = [doc, 0, [c]] self._count_topic_mcl(topic_name, mod_name) # count mcl for for the subtopic mcl = max((self._topics[topic_name][2][mod_name][1], len(c.name))) self._topics[topic_name][2][mod_name][1] = mcl else: self._topics[topic_name] = [unicode(_(topic[1])), 0, {mod_name: [doc, 0, [c]]}] self._count_topic_mcl(topic_name, mod_name) else: self._builtins.append(c) # compute maximum topic length self._mtl = max( len(s) for s in (self._topics.keys() + [c.name for c in self._builtins]) ) super(help, self)._on_finalize()
def print_topics(self): topics = sorted(self._topics.keys()) print unicode(_('Usage: ipa [global-options] COMMAND ...')) print '' print unicode(_('Built-in commands:')) for c in self._builtins: print unicode(_('Help subtopics:')) print ' %s %s' % (to_cli(c.name).ljust(self._mtl), c.summary) print '' print unicode(_('Help topics:')) for t in topics: topic = self._topics[t] print ' %s %s' % (to_cli(t).ljust(self._mtl), topic[0]) print '' print unicode(_('Try `ipa --help` for a list of global options.'))
class show_mappings(frontend.Command): """ Show mapping of LDAP attributes to command-line option. """ takes_args = (Str( 'command_name', label=_('Command name'), ), ) has_output = tuple() def run(self, command_name, **options): command_name = from_cli(command_name) if command_name not in self.Command: raise CommandError(name=command_name) params = self.Command[command_name].options out = [('Parameter', 'LDAP attribute'), ('=========', '==============')] mcl = len(out[0][0]) for param in params(): if param.exclude and 'webui' in param.exclude: continue out.append((param.cli_name, param.param_spec)) mcl = max(mcl, len(param.cli_name)) for item in out: print to_cli(item[0]).ljust(mcl) + ' : ' + item[1]
def new_session_id(self, max_retries=5): ''' Returns a new *unique* session id. See `generate_session_id()` for how the session id's are formulated. The scope of the uniqueness of the id is limited to id's generated by this instance of the `SessionManager` and session id's currently stored in the memcache instance. :parameters: max_retries Maximum number of attempts to produce a unique id. :returns: Unique session id as a string. ''' n_retries = 0 while n_retries < max_retries: session_id = super(MemcacheSessionManager, self).new_session_id(max_retries) session_data = self.get_session_data(session_id) if session_data is None: break n_retries += 1 if n_retries >= max_retries: self.error( 'could not allocate unique new session_id, %d retries exhausted', n_retries) raise errors.ExecutionError( message=_('could not allocate unique new session_id')) return session_id
def new_session_id(self, max_retries=5): ''' Returns a new *unique* session id. See `generate_session_id()` for how the session id's are formulated. The scope of the uniqueness of the id is limited to id's generated by this instance of the `SessionManager`. :parameters: max_retries Maximum number of attempts to produce a unique id. :returns: Unique session id as a string. ''' n_retries = 0 while n_retries < max_retries: session_id = self.generate_session_id() if not session_id in self.generated_session_ids: break n_retries += 1 if n_retries >= max_retries: self.error( 'could not allocate unique new session_id, %d retries exhausted', n_retries) raise errors.ExecutionError( message=_('could not allocate unique new session_id')) self.generated_session_ids.add(session_id) return session_id
def new_session_id(self, max_retries=5): ''' Returns a new *unique* session id. See `generate_session_id()` for how the session id's are formulated. The scope of the uniqueness of the id is limited to id's generated by this instance of the `SessionManager`. :parameters: max_retries Maximum number of attempts to produce a unique id. :returns: Unique session id as a string. ''' n_retries = 0 while n_retries < max_retries: session_id = self.generate_session_id() if not session_id in self.generated_session_ids: break n_retries += 1 if n_retries >= max_retries: self.error('could not allocate unique new session_id, %d retries exhausted', n_retries) raise errors.ExecutionError(message=_('could not allocate unique new session_id')) self.generated_session_ids.add(session_id) return session_id
def new_session_id(self, max_retries=5): ''' Returns a new *unique* session id. See `generate_session_id()` for how the session id's are formulated. The scope of the uniqueness of the id is limited to id's generated by this instance of the `SessionManager` and session id's currently stored in the memcache instance. :parameters: max_retries Maximum number of attempts to produce a unique id. :returns: Unique session id as a string. ''' n_retries = 0 while n_retries < max_retries: session_id = super(MemcacheSessionManager, self).new_session_id(max_retries) session_data = self.get_session_data(session_id) if session_data is None: break n_retries += 1 if n_retries >= max_retries: self.error('could not allocate unique new session_id, %d retries exhausted', n_retries) raise errors.ExecutionError(message=_('could not allocate unique new session_id')) return session_id
def __init__(self): self.__api = None self.__finalize_called = False self.__finalized = False self.__finalize_lock = threading.RLock() cls = self.__class__ self.name = cls.__name__ self.module = cls.__module__ self.fullname = '%s.%s' % (self.module, self.name) self.bases = tuple( '%s.%s' % (b.__module__, b.__name__) for b in cls.__bases__ ) self.doc = _(cls.__doc__) if not self.doc.msg: self.summary = '<%s>' % self.fullname else: self.summary = unicode(self.doc).split('\n\n', 1)[0].strip() log_mgr.get_logger(self, True) if self.label is None: self.label = text.FixMe(self.name + '.label') if not isinstance(self.label, text.LazyText): raise TypeError( TYPE_ERROR % ( self.fullname + '.label', text.LazyText, type(self.label), self.label ) )
def select_entry(self, entries, format, attrs, display_count=True): """ Display a list of lines in with formatting defined in ``format``. ``attrs`` is a list of attributes in the format. Prompt user for a selection and return the value (index of ``entries`` -1). If only one entry is provided then always return 0. Return: 0..n for the index of the selected entry -1 if all entries should be displayed -2 to quit, no entries to be displayed """ if not self.env.interactive or not sys.stdout.isatty(): return -1 counter = len(entries) if counter == 0: raise NotFound(reason=_("No matching entries found")) i = 1 for e in entries: # There is no guarantee that all attrs are in any given # entry d = {} for a in attrs: d[a] = e.get(a, '') self.print_line("%d: %s" % (i, format % d)) i = i + 1 if display_count: self.print_count(entries, 'Found %d match', 'Found %d matches') while True: try: resp = self.prompt( "Choose one: (1 - %s), a for all, q to quit" % counter) except EOFError: return -2 if resp.lower() == "q": #pylint: disable=E1103 return -2 if resp.lower() == "a": #pylint: disable=E1103 return -1 try: selection = int(resp) - 1 if (selection >= 0 and selection < counter): break except: # fall through to the error msg pass self.print_line("Please enter a number between 1 and %s" % counter) self.print_line('') return selection
def prompt_password(self, label, confirm=True): """ Prompt user for a password or read it in via stdin depending on whether there is a tty or not. """ if sys.stdin.isatty(): prompt = u'%s: ' % unicode(label) repeat_prompt = unicode(_('Enter %(label)s again to verify: ') % dict(label=label)) while True: pw1 = self.prompt_helper(prompt, label, prompt_func=getpass.getpass) if not confirm: return pw1 pw2 = self.prompt_helper(repeat_prompt, label, prompt_func=getpass.getpass) if pw1 == pw2: return pw1 self.print_error( _('Passwords do not match!')) else: return self.decode(sys.stdin.readline().strip())
def select_entry(self, entries, format, attrs, display_count=True): """ Display a list of lines in with formatting defined in ``format``. ``attrs`` is a list of attributes in the format. Prompt user for a selection and return the value (index of ``entries`` -1). If only one entry is provided then always return 0. Return: 0..n for the index of the selected entry -1 if all entries should be displayed -2 to quit, no entries to be displayed """ if not self.env.interactive or not sys.stdout.isatty(): return -1 counter = len(entries) if counter == 0: raise NotFound(reason=_("No matching entries found")) i = 1 for e in entries: # There is no guarantee that all attrs are in any given # entry d = {} for a in attrs: d[a] = e.get(a, '') self.print_line("%d: %s" % (i, format % d)) i = i + 1 if display_count: self.print_count(entries, 'Found %d match', 'Found %d matches') while True: try: resp = self.prompt("Choose one: (1 - %s), a for all, q to quit" % counter) except EOFError: return -2 if resp.lower() == "q": #pylint: disable=E1103 return -2 if resp.lower() == "a": #pylint: disable=E1103 return -1 try: selection = int(resp) - 1 if (selection >= 0 and selection < counter): break except: # fall through to the error msg pass self.print_line("Please enter a number between 1 and %s" % counter) self.print_line('') return selection
class ListOfEntries(Output): type = (list, tuple) doc = _('A list of LDAP entries') def validate(self, cmd, entries): assert isinstance(entries, self.type) for (i, entry) in enumerate(entries): if not isinstance(entry, dict): raise TypeError( emsg % (cmd.name, self.__class__.__name__, self.name, i, dict, type(entry), entry))
def print_commands(self, topic, outfile): writer = self._writer(outfile) if topic in self._topics and type(self._topics[topic][2]) is dict: # we want to display topic which has subtopics for subtopic in self._topics[topic][2]: doc = self._topics[topic][2][subtopic][0] mcl = self._topics[topic][1] writer(' %s %s' % (to_cli(subtopic).ljust(mcl), doc)) else: # we want to display subtopic or a topic which has no subtopics if topic in self._topics: mcl = self._topics[topic][1] commands = self._topics[topic][2] else: commands = [] for t in self._topics: if type(self._topics[t][2]) is not dict: continue if topic not in self._topics[t][2]: continue mcl = self._topics[t][2][topic][1] commands = self._topics[t][2][topic][2] break m = '%s.%s' % (self._PLUGIN_BASE_MODULE, topic) doc = (unicode(_(sys.modules[m].__doc__)) or '').strip() if topic not in self.Command and len(commands) == 0: raise HelpError(topic=topic) writer(doc) if commands: writer() writer(_('Topic commands:')) for c in commands: writer( ' %s %s' % (to_cli(c.name).ljust(mcl), c.summary)) writer() writer(_('To get command help, use:')) writer(_(' ipa <command> --help')) writer()
def print_commands(self, topic, outfile): writer = self._writer(outfile) if topic in self._topics and type(self._topics[topic][2]) is dict: # we want to display topic which has subtopics for subtopic in self._topics[topic][2]: doc = self._topics[topic][2][subtopic][0] mcl = self._topics[topic][1] writer(' %s %s' % (to_cli(subtopic).ljust(mcl), doc)) else: # we want to display subtopic or a topic which has no subtopics if topic in self._topics: mcl = self._topics[topic][1] commands = self._topics[topic][2] else: commands = [] for t in self._topics: if type(self._topics[t][2]) is not dict: continue if topic not in self._topics[t][2]: continue mcl = self._topics[t][2][topic][1] commands = self._topics[t][2][topic][2] break m = '%s.%s' % (self._PLUGIN_BASE_MODULE, topic) doc = (unicode(_(sys.modules[m].__doc__)) or '').strip() if topic not in self.Command and len(commands) == 0: raise HelpError(topic=topic) writer(doc) if commands: writer() writer(_('Topic commands:')) for c in commands: writer(' %s %s' % (to_cli(c.name).ljust(mcl), c.summary)) writer() writer(_('To get command help, use:')) writer(_(' ipa <command> --help')) writer()
def prompt_password(self, label, confirm=True): """ Prompt user for a password or read it in via stdin depending on whether there is a tty or not. """ if sys.stdin.isatty(): prompt = u'%s: ' % unicode(label) repeat_prompt = unicode( _('Enter %(label)s again to verify: ') % dict(label=label)) while True: pw1 = self.prompt_helper(prompt, label, prompt_func=getpass.getpass) if not confirm: return pw1 pw2 = self.prompt_helper(repeat_prompt, label, prompt_func=getpass.getpass) if pw1 == pw2: return pw1 self.print_error(_('Passwords do not match!')) else: return self.decode(sys.stdin.readline().strip())
def print_commands(self, topic): if topic in self._topics and type(self._topics[topic][2]) is dict: # we want to display topic which has subtopics for subtopic in self._topics[topic][2]: doc = self._topics[topic][2][subtopic][0] mcl = self._topics[topic][1] print ' %s %s' % (to_cli(subtopic).ljust(mcl), doc) else: # we want to display subtopic or a topic which has no subtopics if topic in self._topics: mcl = self._topics[topic][1] commands = self._topics[topic][2] else: commands = [] for t in self._topics: if type(self._topics[t][2]) is not dict: continue if topic not in self._topics[t][2]: continue mcl = self._topics[t][2][topic][1] commands = self._topics[t][2][topic][2] break m = '%s.%s' % (self._PLUGIN_BASE_MODULE, topic) doc = (unicode(_(sys.modules[m].__doc__)) or '').strip() if topic not in self.Command and len(commands) == 0: raise HelpError(topic=topic) print doc if commands: print '' print unicode(_('Topic commands:')) for c in commands: print ' %s %s' % (to_cli(c.name).ljust(mcl), c.summary) print "\n"
def load_files(self, cmd, kw): """ Load files from File parameters. This has to be done after all required parameters have been read (i.e. after prompt_interactively has or would have been called) AND before they are passed to the command. This is because: 1) we need to be sure no more files are going to be added 2) we load files from the machine where the command was executed 3) the webUI will use a different way of loading files """ for p in cmd.params(): if isinstance(p, File): # FIXME: this only reads the first file raw = None if p.name in kw: if type(kw[p.name]) in (tuple, list): fname = kw[p.name][0] else: fname = kw[p.name] try: f = open(fname, 'r') raw = f.read() f.close() except IOError, e: raise ValidationError( name=to_cli(p.cli_name), error='%s: %s:' % (fname, e[1]) ) elif p.stdin_if_missing: try: raw = sys.stdin.read() except IOError, e: raise ValidationError( name=to_cli(p.cli_name), error=e[1] ) if raw: kw[p.name] = self.Backend.textui.decode(raw) elif p.required: raise ValidationError( name=to_cli(p.cli_name), error=_('No file to read') )
def format_option_help(self, formatter=None): """ Prepend argument help to standard OptionParser's option help """ option_help = optparse.OptionParser.format_option_help(self, formatter) if isinstance(formatter, CLIOptionParserFormatter): heading = unicode(_("Positional arguments")) arguments = [formatter.format_heading(heading)] formatter.indent() for (name, help_string) in self._arguments: arguments.append(formatter.format_argument(name, help_string)) formatter.dedent() if len(arguments) > 1: # there is more than just the heading arguments.append(u"\n") else: arguments = [] option_help = "".join(arguments) + option_help return option_help
def load_files(self, cmd, kw): """ Load files from File parameters. This has to be done after all required parameters have been read (i.e. after prompt_interactively has or would have been called) AND before they are passed to the command. This is because: 1) we need to be sure no more files are going to be added 2) we load files from the machine where the command was executed 3) the webUI will use a different way of loading files """ for p in cmd.params(): if isinstance(p, File): # FIXME: this only reads the first file raw = None if p.name in kw: if type(kw[p.name]) in (tuple, list): fname = kw[p.name][0] else: fname = kw[p.name] try: f = open(fname, 'r') raw = f.read() f.close() except IOError, e: raise ValidationError(name=to_cli(p.cli_name), error='%s: %s:' % (fname, e[1])) elif p.stdin_if_missing: try: raw = sys.stdin.read() except IOError, e: raise ValidationError(name=to_cli(p.cli_name), error=e[1]) if raw: kw[p.name] = self.Backend.textui.decode(raw) elif p.required: raise ValidationError(name=to_cli(p.cli_name), error=_('No file to read'))
def __init__(self): self.__api = None self.__finalize_called = False self.__finalized = False self.__finalize_lock = threading.RLock() cls = self.__class__ self.name = cls.__name__ self.module = cls.__module__ self.fullname = '%s.%s' % (self.module, self.name) self.bases = tuple('%s.%s' % (b.__module__, b.__name__) for b in cls.__bases__) self.doc = _(cls.__doc__) if not self.doc.msg: self.summary = '<%s>' % self.fullname else: self.summary = unicode(self.doc).split('\n\n', 1)[0].strip() log_mgr.get_logger(self, True) if self.label is None: self.label = text.FixMe(self.name + '.label') if not isinstance(self.label, text.LazyText): raise TypeError(TYPE_ERROR % (self.fullname + '.label', text.LazyText, type(self.label), self.label))
class ListOfEntries(Output): type = (list, tuple) doc = _('A list of LDAP entries') def validate(self, cmd, entries): assert isinstance(entries, self.type) for (i, entry) in enumerate(entries): if not isinstance(entry, dict): raise TypeError( emsg % (cmd.name, self.__class__.__name__, self.name, i, dict, type(entry), entry)) result = Output('result', doc=_('All commands should at least have a result')) summary = Output('summary', (unicode, NoneType), _('User-friendly description of action performed')) value = Output( 'value', unicode, _("The primary_key value of the entry, e.g. 'jdoe' for a user"), flags=['no_display'], ) standard = (summary, result) standard_entry = ( summary,
class Entry(Output): type = dict doc = _('A dictionary representing an LDAP entry')
cmd.name, self.__class__.__name__, self.name, types[0], type(values), values)) if client_has_capability(version, 'primary_key_types'): if hasattr(cmd, 'obj') and cmd.obj and cmd.obj.primary_key: types = cmd.obj.primary_key.allowed_types else: types = (unicode,) for (i, value) in enumerate(values): if not isinstance(value, types): raise TypeError(emsg % ( cmd.name, self.__class__.__name__, i, self.name, types[0], type(value), value)) result = Output('result', doc=_('All commands should at least have a result')) summary = Output('summary', (unicode, NoneType), _('User-friendly description of action performed') ) value = PrimaryKey('value', None, _("The primary_key value of the entry, e.g. 'jdoe' for a user"), flags=['no_display'], ) standard = (summary, result) standard_entry = ( summary, Entry('result'),
def build_parser(self, cmd): parser = CLIOptionParser( usage=' '.join(self.usage_iter(cmd)), description=unicode(cmd.doc), formatter=IPAHelpFormatter(), ) option_groups = {} def _get_option_group(group_name): """Get or create an option group for the given name""" option_group = option_groups.get(group_name) if option_group is None: option_group = optparse.OptionGroup(parser, group_name) parser.add_option_group(option_group) option_groups[group_name] = option_group return option_group for option in cmd.options(): kw = dict( dest=option.name, help=unicode(option.doc), ) if 'no_option' in option.flags: continue if option.password and self.env.interactive: kw['action'] = 'store_true' elif option.type is bool and option.autofill: if option.default is True: kw['action'] = 'store_false' else: kw['action'] = 'store_true' elif isinstance(option, Enum): kw['metavar'] = list(str(x) for x in option.values) else: kw['metavar'] = option.__class__.__name__.upper() cli_name = to_cli(option.cli_name) option_names = ['--%s' % cli_name] if option.cli_short_name: option_names.append('-%s' % option.cli_short_name) opt = optparse.make_option(*option_names, **kw) if option.option_group is None: parser.add_option(opt) else: _get_option_group(option.option_group).add_option(opt) if option.deprecated_cli_aliases: new_kw = dict(kw) new_kw['help'] = _('Same as --%s') % cli_name if isinstance(option, Enum): new_kw['metavar'] = 'VAL' group = _get_option_group(unicode(_('Deprecated options'))) for alias in option.deprecated_cli_aliases: name = '--%s' % alias group.add_option(optparse.make_option(name, **new_kw)) for arg in cmd.args(): name = self.__get_arg_name(arg, format_name=False) if 'no_option' in arg.flags or name is None: continue doc = unicode(arg.doc) parser.add_argument(name, doc) return parser
class help(frontend.Local): """ Display help for a command or topic. """ takes_args = (Str('command?', cli_name='topic', label=_('Topic or Command'), doc=_('The topic or command name.')), ) takes_options = (Any('outfile?', flags=['no_option']), ) has_output = tuple() _PLUGIN_BASE_MODULE = 'ipalib.plugins' def _get_command_module(self, module): """ Return last part of ``module`` name, or ``None`` if module is this file. For example: """ if module == __name__: return return module.split('.')[-1] def _get_module_topic(self, module_name): if not sys.modules[module_name]: __import__(module_name) module = sys.modules[module_name] topic = getattr(module, 'topic', None) if topic is None: topic = (self._get_command_module(module_name), None) return topic def _count_topic_mcl(self, topic_name, mod_name): mcl = max((self._topics[topic_name][1], len(mod_name))) self._topics[topic_name][1] = mcl def _on_finalize(self): # {topic: ["description", mcl, {"subtopic": ["description", mcl, [commands]]}]} # {topic: ["description", mcl, [commands]]} self._topics = {} # [builtin_commands] self._builtins = [] # build help topics for c in self.Command(): if c.NO_CLI: continue topic = self._get_module_topic(c.module) topic_name = topic[0] if topic_name: if topic[1] is None: # a module without grouping if topic_name in self._topics: self._topics[topic_name][2].append(c) else: m = '%s.%s' % (self._PLUGIN_BASE_MODULE, topic_name) try: module = sys.modules[m] except KeyError: doc = '' else: doc = (unicode(_(module.__doc__)) or '').strip().split('\n', 1)[0] self._topics[topic_name] = [doc, 0, [c]] mcl = max((self._topics[topic_name][1], len(c.name))) self._topics[topic_name][1] = mcl else: # a module grouped in a topic doc = (unicode(_(sys.modules[c.module].__doc__)) or '').strip().split('\n', 1)[0] mod_name = c.module.rsplit('.', 1)[1] if topic_name in self._topics: if mod_name in self._topics[topic_name][2]: self._topics[topic_name][2][mod_name][2].append(c) else: self._topics[topic_name][2][mod_name] = [ doc, 0, [c] ] self._count_topic_mcl(topic_name, mod_name) # count mcl for for the subtopic mcl = max((self._topics[topic_name][2][mod_name][1], len(c.name))) self._topics[topic_name][2][mod_name][1] = mcl else: self._topics[topic_name] = [ unicode(_(topic[1])), 0, { mod_name: [doc, 0, [c]] } ] self._count_topic_mcl(topic_name, mod_name) else: self._builtins.append(c) # compute maximum topic length self._mtl = max( len(s) for s in (self._topics.keys() + [c.name for c in self._builtins])) super(help, self)._on_finalize() def run(self, key, outfile=None, **options): if outfile is None: outfile = sys.stdout writer = self._writer(outfile) name = from_cli(key) mod_name = '%s.%s' % (self._PLUGIN_BASE_MODULE, name) if key is None: self.api.parser.print_help(outfile) return if name == "topics": self.print_topics(outfile) return if name in self._topics: self.print_commands(name, outfile) elif name in self.Command: cmd = self.Command[name] if cmd.NO_CLI: raise HelpError(topic=name) self.Backend.cli.build_parser(cmd).print_help(outfile) elif mod_name in sys.modules: self.print_commands(name, outfile) elif name == "commands": mcl = max(len(s) for s in (self.Command)) for cname in self.Command: cmd = self.Command[cname] if cmd.NO_CLI: continue writer('%s %s' % (to_cli(cmd.name).ljust(mcl), cmd.summary)) else: raise HelpError(topic=name) def _writer(self, outfile): def writer(string=''): print >> outfile, unicode(string) return writer def print_topics(self, outfile): writer = self._writer(outfile) for t, topic in sorted(self._topics.items()): writer('%s %s' % (to_cli(t).ljust(self._mtl), topic[0])) def print_commands(self, topic, outfile): writer = self._writer(outfile) if topic in self._topics and type(self._topics[topic][2]) is dict: # we want to display topic which has subtopics for subtopic in self._topics[topic][2]: doc = self._topics[topic][2][subtopic][0] mcl = self._topics[topic][1] writer(' %s %s' % (to_cli(subtopic).ljust(mcl), doc)) else: # we want to display subtopic or a topic which has no subtopics if topic in self._topics: mcl = self._topics[topic][1] commands = self._topics[topic][2] else: commands = [] for t in self._topics: if type(self._topics[t][2]) is not dict: continue if topic not in self._topics[t][2]: continue mcl = self._topics[t][2][topic][1] commands = self._topics[t][2][topic][2] break m = '%s.%s' % (self._PLUGIN_BASE_MODULE, topic) doc = (unicode(_(sys.modules[m].__doc__)) or '').strip() if topic not in self.Command and len(commands) == 0: raise HelpError(topic=topic) writer(doc) if commands: writer() writer(_('Topic commands:')) for c in commands: writer(' %s %s' % (to_cli(c.name).ljust(mcl), c.summary)) writer() writer(_('To get command help, use:')) writer(_(' ipa <command> --help')) writer()
def build_parser(self, cmd): parser = CLIOptionParser( usage=' '.join(self.usage_iter(cmd)), description=unicode(cmd.doc), formatter=IPAHelpFormatter(), ) option_groups = {} def _get_option_group(group_name): """Get or create an option group for the given name""" option_group = option_groups.get(group_name) if option_group is None: option_group = optparse.OptionGroup(parser, group_name) parser.add_option_group(option_group) option_groups[group_name] = option_group return option_group for option in cmd.options(): kw = dict( dest=option.name, help=unicode(option.doc), ) if 'no_option' in option.flags: continue if option.password and self.env.interactive: kw['action'] = 'store_true' elif isinstance(option, Flag): if option.default is True: kw['action'] = 'store_false' else: kw['action'] = 'store_true' elif isinstance(option, Enum): kw['metavar'] = list(str(x) for x in option.values) else: kw['metavar'] = option.__class__.__name__.upper() cli_name = to_cli(option.cli_name) option_names = ['--%s' % cli_name] if option.cli_short_name: option_names.append('-%s' % option.cli_short_name) opt = optparse.make_option(*option_names, **kw) if option.option_group is None: parser.add_option(opt) else: _get_option_group(option.option_group).add_option(opt) if option.deprecated_cli_aliases: new_kw = dict(kw) new_kw['help'] = _('Same as --%s') % cli_name if isinstance(option, Enum): new_kw['metavar'] = 'VAL' group = _get_option_group(unicode(_('Deprecated options'))) for alias in option.deprecated_cli_aliases: name = '--%s' % alias group.add_option(optparse.make_option(name, **new_kw)) for arg in cmd.args(): name = self.__get_arg_name(arg, format_name=False) if 'no_option' in arg.flags or name is None: continue doc = unicode(arg.doc) parser.add_argument(name, doc) return parser