def rst_all_options(cls, nspace=0, prefix=None): """Recursively generate rST for a provided Configurable class. :arg cls: The Configurable class. :arg nspace: Indentation level. :arg prefix: Prefix to use for new traits.""" lines = [] classname = cls.__name__ # Slaved options don't appear directly, but underneath their controlling enum. slaved_options = set() for trait in cls.class_own_traits().values(): if isinstance(trait, PairedEnum): slaved_options.add(trait.paired_name) for k, trait in sorted(cls.class_own_traits(config=True).items()): typ = trait.__class__.__name__ if trait.name in slaved_options: continue _prefix = prefix if prefix is not None else classname termline = "{prefix:}.\ **{suffix:}**".format(prefix=_prefix, suffix=trait.name) if 'Enum' in typ: termline += ' : ' + '|'.join(repr(x) for x in trait.values) else: termline += ' : ' + typ lines.append(indent(termline, nspace)) if isinstance(trait, PairedEnum): # Find the slaved option and recurse to fill in the subtree. dvr = trait.default_value_repr() extra = [ "", "Setting value implies configuration of sub-tree %s.%s:" % (classname, trait.paired_name), "" ] for opt, val in trait.paired_defaults.items(): extra.append("'%s':" % opt) extra.append("") extra.append( rst_all_options(val, 4 + nspace, prefix=classname + "." + trait.paired_name)) extra = "\n".join(extra) else: extra = None try: dvr = trait.default_value_repr() except Exception: dvr = None help = trait.help or 'No description' lines.append(indent(dedent(help), 4 + nspace)) lines.append('') lines.append(indent("Default:\n", 4 + nspace)) lines.append(indent(dvr.replace("\\n", "\\\\n"), 4 + nspace)) if extra is not None: lines.append(indent(extra, 4 + nspace)) lines.append('') return "\n".join(lines)
def print_flag_help(self): """Print the flag part of the help.""" if not self.flags: return lines = [] for flags, (cfg, fhelp) in self.flags.items(): try: if not isinstance(flags, tuple): flags = (flags, ) flags = sorted(flags, key=len) flags = ', '.join( ('--%s' if len(m) > 1 else '-%s') % m for m in flags) lines.append(flags) lines.append(indent(dedent(fhelp.strip()))) cfg_list = ' '.join('--%s.%s=%s' % (clname, prop, val) for clname, props_dict in cfg.items() for prop, val in props_dict.items()) cfg_txt = "Equivalent to: [%s]" % cfg_list lines.append(indent(dedent(cfg_txt))) except Exception as ex: self.log.error( 'Failed collecting help-message for flag %r, due to: %s', flags, ex) raise # lines.append('') print(os.linesep.join(lines))
def class_get_trait_help(cls, trait, inst=None): """Get the help string for a single trait. If `inst` is given, it's current trait values will be used in place of the class default. """ assert inst is None or isinstance(inst, cls) lines = [] header = "--%s.%s=<%s>" % (cls.__name__, trait.name, trait.__class__.__name__) lines.append(header) if inst is not None: lines.append(indent('Current: %r' % getattr(inst, trait.name), 4)) else: try: dvr = trait.default_value_repr() except Exception: dvr = None # ignore defaults we can't construct if dvr is not None: if len(dvr) > 64: dvr = dvr[:61] + '...' lines.append(indent('Default: %s' % dvr, 4)) if 'Enum' in trait.__class__.__name__: # include Enum choices lines.append(indent('Choices: %r' % (trait.values, ))) help = trait.help if help != '': help = '\n'.join(wrap_paragraphs(help, 76)) lines.append(indent(help, 4)) return '\n'.join(lines)
def print_flag_help(self): """Print the flag part of the help.""" if not self.flags: return lines = [] for flags, (cfg, fhelp) in self.flags.items(): try: if not isinstance(flags, tuple): flags = (flags, ) flags = sorted(flags, key=len) flags = ', '.join(('--%s' if len(m) > 1 else '-%s') % m for m in flags) lines.append(flags) lines.append(indent(dedent(fhelp.strip()))) cfg_list = ' '.join('--%s.%s=%s' %(clname, prop, val) for clname, props_dict in cfg.items() for prop, val in props_dict.items()) cfg_txt = "Equivalent to: [%s]" % cfg_list lines.append(indent(dedent(cfg_txt))) except Exception as ex: self.log.error('Failed collecting help-message for flag %r, due to: %s', flags, ex) raise # lines.append('') print(os.linesep.join(lines))
def class_get_trait_help(cls, trait, inst=None): """Get the help string for a single trait. If `inst` is given, it's current trait values will be used in place of the class default. """ assert inst is None or isinstance(inst, cls) lines = [] header = "--%s.%s=<%s>" % (cls.__name__, trait.name, trait.__class__.__name__) lines.append(header) if inst is not None: lines.append(indent('Current: %r' % getattr(inst, trait.name), 4)) else: try: dvr = trait.default_value_repr() except Exception: dvr = None # ignore defaults we can't construct if dvr is not None: if len(dvr) > 64: dvr = dvr[:61]+'...' lines.append(indent('Default: %s' % dvr, 4)) if 'Enum' in trait.__class__.__name__: # include Enum choices lines.append(indent('Choices: %r' % (trait.values,))) help = trait.help if help != '': help = '\n'.join(wrap_paragraphs(help, 76)) lines.append(indent(help, 4)) return '\n'.join(lines)
def class_get_trait_help(cls, trait, inst=None, helptext=None): """Get the helptext string for a single trait. :param inst: If given, it's current trait values will be used in place of the class default. :param helptext: If not given, uses the `help` attribute of the current trait. """ assert inst is None or isinstance(inst, cls) lines = [] header = "--%s.%s" % (cls.__name__, trait.name) if isinstance(trait, (Container, Dict)): multiplicity = trait.metadata.get('multiplicity', 'append') if isinstance(trait, Dict): sample_value = '<key-1>=<value-1>' else: sample_value = '<%s-item-1>' % trait.__class__.__name__.lower() if multiplicity == 'append': header = "%s=%s..." % (header, sample_value) else: header = "%s %s..." % (header, sample_value) else: header = '%s=<%s>' % (header, trait.__class__.__name__) #header = "--%s.%s=<%s>" % (cls.__name__, trait.name, trait.__class__.__name__) lines.append(header) if helptext is None: helptext = trait.help if helptext != '': helptext = '\n'.join(wrap_paragraphs(helptext, 76)) lines.append(indent(helptext, 4)) if 'Enum' in trait.__class__.__name__: # include Enum choices lines.append(indent('Choices: %s' % trait.info())) env_var = trait.metadata.get('envvar') if env_var: only = '' if trait.metadata.get('config') else '(only)' env_info = 'Environment variable%s: %s' % (only, env_var) lines.append(indent(env_info, 4)) if inst is not None: lines.append(indent('Current: %r' % getattr(inst, trait.name), 4)) else: try: dvr = trait.default_value_repr() except Exception: dvr = None # ignore defaults we can't construct if dvr is not None: if len(dvr) > 64: dvr = dvr[:61] + '...' lines.append(indent('Default: %s' % dvr, 4)) return '\n'.join(lines)
def document_flag_help(self): """ Return a string containing descriptions of all the flags. """ flags = "The following flags are defined:\n\n" for flag, (cfg, fhelp) in self.flags.items(): flags += "{}\n".format(flag) flags += indent(fill(fhelp, 80)) + '\n\n' flags += indent(fill("Long Form: " + str(cfg), 80)) + '\n\n' return flags
def class_get_trait_help(cls, trait, inst=None, helptext=None): """Get the helptext string for a single trait. :param inst: If given, it's current trait values will be used in place of the class default. :param helptext: If not given, uses the `help` attribute of the current trait. """ assert inst is None or isinstance(inst, cls) lines = [] header = "--%s.%s" % (cls.__name__, trait.name) if isinstance(trait, (Container, Dict)): multiplicity = trait.metadata.get('multiplicity', 'append') if isinstance(trait, Dict): sample_value = '<key-1>=<value-1>' else: sample_value = '<%s-item-1>' % trait.__class__.__name__.lower() if multiplicity == 'append': header = "%s=%s..." % (header, sample_value) else: header = "%s %s..." % (header, sample_value) else: header = '%s=<%s>' % (header, trait.__class__.__name__) #header = "--%s.%s=<%s>" % (cls.__name__, trait.name, trait.__class__.__name__) lines.append(header) if helptext is None: helptext = trait.help if helptext != '': helptext = '\n'.join(wrap_paragraphs(helptext, 76)) lines.append(indent(helptext, 4)) if 'Enum' in trait.__class__.__name__: # include Enum choices lines.append(indent('Choices: %s' % trait.info())) if inst is not None: lines.append(indent('Current: %r' % getattr(inst, trait.name), 4)) else: try: dvr = trait.default_value_repr() except Exception: dvr = None # ignore defaults we can't construct if dvr is not None: if len(dvr) > 64: dvr = dvr[:61]+'...' lines.append(indent('Default: %s' % dvr, 4)) return '\n'.join(lines)
def class_config_rst_doc(cls, trait_aliases): """Generate rST documentation for this class' config options. Excludes traits defined on parent classes. """ lines = [] classname = cls.__name__ for k, trait in sorted(cls.class_traits(config=True).items()): ttype = trait.__class__.__name__ fullname = classname + '.' + trait.name lines += ['.. configtrait:: ' + fullname, '' ] help = trait.help.rstrip() or 'No description' lines.append(indent(dedent(help), 4) + '\n') # Choices or type if 'Enum' in ttype: # include Enum choices lines.append(indent( ':options: ' + ', '.join('``%r``' % x for x in trait.values), 4)) else: lines.append(indent(':trait type: ' + ttype, 4)) # Default value # Ignore boring default values like None, [] or '' if interesting_default_value(trait.default_value): try: dvr = trait.default_value_repr() except Exception: dvr = None # ignore defaults we can't construct if dvr is not None: if len(dvr) > 64: dvr = dvr[:61] + '...' # Double up backslashes, so they get to the rendered docs dvr = dvr.replace('\\n', '\\\\n') lines.append(indent(':default: ``%s``' % dvr, 4)) # Command line aliases if trait_aliases[fullname]: fmt_aliases = format_aliases(trait_aliases[fullname]) lines.append(indent(':CLI option: ' + fmt_aliases, 4)) # Blank line lines.append('') return '\n'.join(lines)
def print_subcommands(self): """Print the subcommand part of the help.""" ## Overridden, to print "default" sub-cmd. if not self.subcommands: return lines = ["Subcommands"] lines.append('-' * len(lines[0])) lines.append('') for p in wrap_paragraphs( self.subcommand_description.format(app=self.name)): lines.append(p) lines.append('') for subc, (cls, hlp) in self.subcommands.items(): if self.default_subcmd == subc: subc = '%s[*]' % subc lines.append(subc) if hlp: lines.append(indent(dedent(hlp.strip()))) if self.default_subcmd: lines.append('') lines.append( """Note: The asterisk '[*]' marks the "default" sub-command to run when none specified.""" ) lines.append('') print(os.linesep.join(lines))
def class_config_rst_doc(cls): """Generate rST documentation for this class' config options. Excludes traits defined on parent classes. """ lines = [] classname = cls.__name__ for k, trait in sorted(cls.class_traits(config=True).items()): ttype = trait.__class__.__name__ termline = classname + '.' + trait.name # Choices or type if 'Enum' in ttype: # include Enum choices termline += ' : ' + '|'.join(repr(x) for x in trait.values) else: termline += ' : ' + ttype lines.append(termline) env_var = trait.metadata.get('envvar') if env_var: only = '' if trait.metadata.get('config') else '(only)' env_info = 'Environment variable%s: ``%s``' % (only, env_var) lines.append(indent(env_info, 4)) # Default value try: dvr = trait.default_value_repr() except Exception: dvr = None # ignore defaults we can't construct if dvr is not None: if len(dvr) > 64: dvr = dvr[:61] + '...' # Double up backslashes, so they get to the rendered docs dvr = dvr.replace('\\n', '\\\\n') lines.append(indent('Default: ``%s``' % dvr, 4)) lines.append('') help = trait.help or 'No description' lines.append(indent(dedent(help), 4)) # Blank line lines.append('') return '\n'.join(lines)
def print_examples(self): """Print usage and examples. This usage string goes at the end of the command line help string and should contain examples of the application's usage. """ if self.examples: print("Examples") print("--------") print() print(indent(dedent(self.examples.strip()))) print()
def emit_examples(self): """Yield lines with the usage and examples. This usage string goes at the end of the command line help string and should contain examples of the application's usage. """ if self.examples: yield "Examples" yield "--------" yield '' yield indent(dedent(self.examples.strip())) yield ''
def print_flag_help(self): """Print the flag part of the help.""" if not self.flags: return lines = [] for m, (cfg, help) in self.flags.items(): prefix = '--' if len(m) > 1 else '-' lines.append(prefix + m) lines.append(indent(dedent(help.strip()))) # lines.append('') print(os.linesep.join(lines))
def print_flag_help(self): """Print the flag part of the help.""" if not self.flags: return lines = [] for m, (cfg,help) in iteritems(self.flags): prefix = '--' if len(m) > 1 else '-' lines.append(prefix+m) lines.append(indent(dedent(help.strip()))) # lines.append('') print(os.linesep.join(lines))
def class_config_rst_doc(cls): """Generate rST documentation for this class' config options. Excludes traits defined on parent classes. """ lines = [] classname = cls.__name__ for k, trait in sorted(cls.class_traits(config=True).items()): ttype = trait.__class__.__name__ termline = classname + '.' + trait.name # Choices or type if 'Enum' in ttype: # include Enum choices termline += ' : ' + trait.info_rst() else: termline += ' : ' + ttype lines.append(termline) # Default value try: dvr = trait.default_value_repr() except Exception: dvr = None # ignore defaults we can't construct if dvr is not None: if len(dvr) > 64: dvr = dvr[:61]+'...' # Double up backslashes, so they get to the rendered docs dvr = dvr.replace('\\n', '\\\\n') lines.append(indent('Default: ``%s``' % dvr, 4)) lines.append('') help = trait.help or 'No description' lines.append(indent(dedent(help), 4)) # Blank line lines.append('') return '\n'.join(lines)
def print_subcommands(self): """Print the subcommand part of the help.""" if not self.subcommands: return lines = ["Subcommands"] lines.append('=' * len(lines[0])) for p in wrap_paragraphs( self.subcommand_description.format(app=self.name)): lines.append(p) lines.append('') for subc, (cls, help) in self.subcommands.items(): lines.append(subc) if help: lines.append(indent(dedent(help.strip()))) lines.append('') print(os.linesep.join(lines))
def print_subcommands(self): """Print the subcommand part of the help.""" if not self.subcommands: return lines = ["Subcommands"] lines.append('=' * len(lines[0])) for p in wrap_paragraphs(self.subcommand_description.format( app=self.name)): lines.append(p) lines.append('') for subc, (cls, help) in self.subcommands.items(): lines.append(subc) if help: lines.append(indent(dedent(help.strip()))) lines.append('') print(os.linesep.join(lines))
def emit_subcommands_help(self): """Yield the lines for the subcommand part of the help.""" if not self.subcommands: return header = "Subcommands" yield header yield '=' * len(header) for p in wrap_paragraphs( self.subcommand_description.format(app=self.name)): yield p yield '' for subc, (cls, help) in self.subcommands.items(): yield subc if help: yield indent(dedent(help.strip())) yield ''
def print_subcommands(self): """Print the subcommand part of the help.""" lines = ["Call"] lines.append('-'*len(lines[-1])) lines.append('') lines.append("> jhubctl <subcommand> <resource-type> <resource-name>") lines.append('') lines.append("Subcommands") lines.append('-'*len(lines[-1])) lines.append('') for name, subcommand in self.subcommands.items(): lines.append(name) lines.append(indent(subcommand[1])) lines.append('') print(os.linesep.join(lines))
def emit_subcommands_help(self): """Yield the lines for the subcommand part of the help.""" if not self.subcommands: return header = "Subcommands" yield header yield '=' * len(header) for p in wrap_paragraphs(self.subcommand_description.format( app=self.name)): yield p yield '' for subc, (cls, help) in self.subcommands.items(): yield subc if help: yield indent(dedent(help.strip())) yield ''
def class_config_rst_doc(cls): """Generate rST documentation for this class' config options. Excludes traits defined on parent classes. """ lines = [] classname = cls.__name__ for k, trait in sorted(cls.class_own_traits(config=True).items()): ttype = trait.__class__.__name__ termline = classname + '.' + trait.name # Choices or type if 'Enum' in ttype: # include Enum choices termline += ' : ' + '|'.join(repr(x) for x in trait.values) else: termline += ' : ' + ttype lines.append(termline) # Default value try: dv = trait.default_value dvr = repr(dv) except Exception: dvr = dv = None # ignore defaults we can't construct if (dv is not None) and (dvr is not None): if len(dvr) > 64: dvr = dvr[:61] + '...' # Double up backslashes, so they get to the rendered docs dvr = dvr.replace('\\n', '\\\\n') lines.append(' Default: ``%s``' % dvr) lines.append('') help = trait.get_metadata('help') if help is not None: lines.append(indent(dedent(help), 4)) else: lines.append(' No description') # Blank line lines.append('') return '\n'.join(lines)
def print_alias_help(self): """Print the alias part of the help.""" if not self.aliases: return lines = [] classdict = {} for cls in self.classes: # include all parents (up to, but excluding Configurable) in available names for c in cls.mro()[:-3]: classdict[c.__name__] = c for alias, longname in self.aliases.items(): try: if isinstance(longname, tuple): longname, fhelp = longname else: fhelp = None classname, traitname = longname.split('.', 1) cls = classdict[classname] trait = cls.class_traits(config=True)[traitname] fhelp = cls.class_get_trait_help(trait, helptext=fhelp).splitlines() if not isinstance(alias, tuple): alias = (alias, ) alias = sorted(alias, key=len) alias = ', '.join( ('--%s' if len(m) > 1 else '-%s') % m for m in alias) # reformat first line fhelp[0] = fhelp[0].replace('--' + longname, alias) lines.extend(fhelp) lines.append(indent("Equivalent to: [--%s]" % longname)) except Exception as ex: self.log.error( 'Failed collecting help-message for alias %r, due to: %s', alias, ex) raise # lines.append('') print(os.linesep.join(lines))
def _make_commit_message(self, *projects: pvproject.Project, is_release=False): from ipython_genutils.text import indent, wrap_paragraphs sub_summary = ', '.join( prj.interp(prj.summary_interped(is_release)) # type: ignore # none for prj in projects) summary = self.interp(self.message_summary, sub_summary=sub_summary) text_lines: List[str] = [] for prj in projects: if prj.message_body: text_lines.append('- %s' % prj.pname) text_lines.append(indent(wrap_paragraphs(prj.message_body), 2)) sub_body = '\n'.join(text_lines).strip() body = self.interp(self.message_body, sub_body=sub_body) return '%s\n\n%s' % (summary, body)
def print_alias_help(self): """Print the alias part of the help.""" if not self.aliases: return lines = [] classdict = {} for cls in self.classes: # include all parents (up to, but excluding Configurable) in available names for c in cls.mro()[:-3]: classdict[c.__name__] = c for alias, longname in self.aliases.items(): try: if isinstance(longname, tuple): longname, fhelp = longname else: fhelp = None classname, traitname = longname.split('.', 1) cls = classdict[classname] trait = cls.class_traits(config=True)[traitname] fhelp = cls.class_get_trait_help(trait, helptext=fhelp).splitlines() if not isinstance(alias, tuple): alias = (alias, ) alias = sorted(alias, key=len) alias = ', '.join(('--%s' if len(m) > 1 else '-%s') % m for m in alias) # reformat first line fhelp[0] = fhelp[0].replace('--' + longname, alias) lines.extend(fhelp) lines.append(indent("Equivalent to: [--%s]" % longname)) except Exception as ex: self.log.error('Failed collecting help-message for alias %r, due to: %s', alias, ex) raise # lines.append('') print(os.linesep.join(lines))
def class_config_rst_doc(cls): """Generate rST documentation for this class' config options. Excludes traits defined on parent classes. """ lines = [] classname = cls.__name__ for k, trait in sorted(cls.class_own_traits(config=True).items()): ttype = trait.__class__.__name__ termline = classname + "." + trait.name # Choices or type if "Enum" in ttype: # include Enum choices termline += " : " + "|".join(repr(x) for x in trait.values) else: termline += " : " + ttype lines.append(termline) # Default value try: dvr = trait.default_value_repr() except Exception: dvr = None # ignore defaults we can't construct if dvr is not None: if len(dvr) > 64: dvr = dvr[:61] + "..." # Double up backslashes, so they get to the rendered docs dvr = dvr.replace("\\n", "\\\\n") lines.append(" Default: ``%s``" % dvr) lines.append("") help = trait.help or "No description" lines.append(indent(dedent(help), 4)) # Blank line lines.append("") return "\n".join(lines)
def class_config_yaml( cls, outer_cfg, # noqa: C901 # too complex: TODO: FIX Yaml+contextvars classes=None, config: trc.Config = None): """Get the config section for this Configurable. :param list classes: (optional) The list of other classes in the config file, used to reduce redundant help descriptions. If given, only params from these classes reported. :param config: If given, only what is contained there is included in generated yaml, with help-descriptions from classes, only where class default-values differ from the values contained in this dictionary. """ from ..utils import yamlutil as yu from ruamel.yaml.comments import CommentedMap import textwrap as tw cfg = CommentedMap() for name, trait in sorted(cls.class_traits(config=True).items()): if config is None: default_value = trait.default() cfg[name] = yu.preserve_yaml_literals(default_value) else: dumpables = _dumpable_trait_value(cls, trait, config) if dumpables: value, default_value = dumpables cfg[name] = yu.preserve_yaml_literals(value) else: continue trait_lines = [] default_repr = yu.ydumps(default_value) if default_repr and default_repr.count( '\n') > 1 and default_repr[0] != '\n': default_repr = tw.indent('\n' + default_repr, ' ' * 9) if yu._dump_trait_help.get(): if classes: defining_class = cls._defining_class(trait, classes) else: defining_class = cls if defining_class is cls: # cls owns the trait, show full help if trait.help: trait_lines.append('') trait_lines.append(_make_comment(trait.help).strip()) if 'Enum' in type(trait).__name__: # include Enum choices trait_lines.append('Choices: %s' % trait.info()) trait_lines.append('Default: %s' % default_repr) else: # Trait appears multiple times and isn't defined here. # Truncate help to first line + "See also Original.trait" if trait.help: trait_lines.append( _make_comment(trait.help.split('\n', 1)[0])) trait_lines.append('See also: %s.%s' % (defining_class.__name__, name)) cfg.yaml_set_comment_before_after_key( name, before='\n'.join(trait_lines), indent=2) if not cfg: return outer_cfg[cls.__name__] = cfg if yu._dump_trait_help.get(): # section header breaker = '#' * 76 parent_classes = ', '.join(p.__name__ for p in cls.__bases__ if issubclass(p, trc.Configurable)) s = "%s(%s) configuration" % (cls.__name__, parent_classes) head_lines = ['', '', breaker, s, breaker] # get the description trait desc = class_help_description_lines(cls) if desc: head_lines.append(_make_comment('\n'.join(desc))) outer_cfg.yaml_set_comment_before_after_key(cls.__name__, '\n'.join(head_lines))