def testLazyFormatNestedExpansion(self): fmt = 'exp:{text} lit:{{text}} nest-exp:{nest} nest-lit:{{nest}}' expected = 'exp:TEXT lit:{text} nest-exp:TEXT+{text} nest-lit:{nest}' actual = console_io.LazyFormat(fmt, text='TEXT', nest='{text}+{{text}}') self.assertEqual(expected, actual)
def testLazyFormatExplicitDoubleQuote(self): fmt = '} {one} {{two}} {' expected = '} ONE {two} {' actual = console_io.LazyFormat(fmt, one='ONE', two='TWO', three='THREE') self.assertEqual(expected, actual)
def testLazyFormatListExample(self): fmt = "--dict-flag=^:^a=b,c:d=f,g # => {'a: 'b,c', 'd': 'f,g'}" expected = fmt actual = console_io.LazyFormat(fmt, one='ONE', two='TWO', three='THREE') self.assertEqual(expected, actual)
def _ExpandHelpText(self, text): """Expand command {...} references in text. Args: text: The text chunk to expand. Returns: The expanded help text. """ return console_io.LazyFormat(text or '', command=self._command_name, man_name=self._file_name, top_command=self._top, parent_command=' '.join( self._command_path[:-1]), index=self._capsule, **self._sections)
def ExpandHelpText(command, text): """Expand command {...} references in text. Args: command: calliope._CommandCommon, The command object that we're helping. text: str, The text chunk to expand. Returns: str, The expanded help text. """ if text == command.long_help: long_help = '' else: long_help = ExpandHelpText(command, command.long_help) path = command.GetPath() return console_io.LazyFormat(text or '', command=' '.join(path), man_name='_'.join(path), top_command=path[0], parent_command=' '.join(path[:-1]), index=command.short_help, description=long_help)
def ExpandHelpText(command, text, sections=True): """Expand command {...} references in text. Args: command: calliope._CommandCommon, The command object that we're helping. text: str, The text chunk to expand. sections: bool, Include #... markdown sections if True. Returns: str, The expanded help text. """ if text == command.long_help: long_help = '' else: long_help = ExpandHelpText(command, command.long_help, sections=False) path = command.GetPath() if not sections and text: section_markdown_index = text.find('\n\n#') if section_markdown_index >= 0: text = text[:section_markdown_index] # The lower case keys in the optional detailed_help dict are user specified # parameters to LazyFormat(). details = {} for key, value in getattr(command, 'detailed_help', {}).iteritems(): if key.islower(): details[key] = value return console_io.LazyFormat( text or '', command=' '.join(path), man_name='_'.join(path), top_command=path[0], parent_command=' '.join(path[:-1]), index=command.short_help, description=long_help, **details )
def _ExpandHelpText(self, text, sections=True): """Expand command {...} references in text. Args: text: The text chunk to expand. sections: Include #... markdown sections if True. Returns: The expanded help text. """ if text == self._docstring: description = '' else: description = self._ExpandHelpText(self._docstring, sections=False) if not sections and text: section_markdown_index = text.find('\n\n#') if section_markdown_index >= 0: text = text[:section_markdown_index] # The lower case keys in the optional detailed_help dict are user specified # parameters to LazyFormat(). details = {} for key, value in self._sections.iteritems(): if key.islower(): details[key] = value return console_io.LazyFormat( text or '', command=self._command_name, man_name=self._file_name, top_command=self._command_path[0], parent_command=' '.join(self._command_path[:-1]), index=self._capsule, # TODO(user): This should be capsule=... description=description, **details )
def testLazyFormatOneVarExplicitQuote(self): fmt = 'This is a {test} with one var and an {{explicit}} {{quote}}.' expected = 'This is a TEST with one var and an {explicit} {quote}.' actual = console_io.LazyFormat(fmt, test='TEST', explicit='EXPLICIT') self.assertEqual(expected, actual)
def testLazyFormatOneVarOneUndefined(self): fmt = 'This is a {test} with one var one {undefined}.' expected = 'This is a TEST with one var one {undefined}.' actual = console_io.LazyFormat(fmt, test='TEST') self.assertEqual(expected, actual)
def testLazyFormatOneCallable(self): fmt = 'This is a {test} with one var.' expected = 'This is a TEST with one var.' actual = console_io.LazyFormat(fmt, test=lambda: 'TEST') self.assertEqual(expected, actual)
def testLazyFormatNoVars(self): fmt = 'This is a test with no vars.' expected = fmt actual = console_io.LazyFormat(fmt, test='TEST') self.assertEqual(expected, actual)
def testLazyFormatLotsOfBraces(self): fmt = '{{}' expected = fmt actual = console_io.LazyFormat(fmt, test='TEST', one='ONE') self.assertEqual(expected, actual)
def testLazyFormatUnbalancedBrace(self): fmt = '{foo' expected = fmt actual = console_io.LazyFormat(fmt, test='TEST', one='ONE') self.assertEqual(expected, actual)
def __init__(self, command, parent, include_hidden_flags=True): self.release = command.ReleaseTrack().id self.path = command.GetPath() self.name = command.name.replace('_', '-') self.hidden = command.IsHidden() self.flags = {} self.positionals = [] self.sections = {} parent_command = parent.name.replace('_', '-') if parent else '' self.release, capsule = self.__Release( command, self.release, getattr(command, 'short_help', '')) self.capsule = console_io.LazyFormat(_NormalizeDescription(capsule), command=self.name, parent_command=parent_command) self.release, description = self.__Release( command, self.release, getattr(command, 'long_help', '')) self.description = console_io.LazyFormat( _NormalizeDescription(description), command=self.name, index=self.capsule, parent_command=parent_command) sections = getattr(command, 'detailed_help', None) if sections: for s in sections: if s == 'brief': self.release, self.capsule = self.__Release( command, self.release, sections[s]) else: self.sections[s] = console_io.LazyFormat( _NormalizeDescription(sections[s]), command=self.name, index=self.capsule, description=self.description, parent_command=parent_command) self.commands = {} # _parent is explicitly private so it won't appear in serialized output. self._parent = parent if parent: parent.commands[self.name] = self args = command.ai # Initialize the mutually exclusive flag groups. group_count = {} group_name = {} for arg in args.flag_args: for name in arg.option_strings: if name.startswith('--'): name = name.replace('_', '-') if not self.__Ancestor(name): g = args.mutex_groups.get(arg.dest, None) if g: group_name[name] = g if g in group_count: group_count[g] += 1 else: group_count[g] = 1 group_id_count = 0 group_id = {} # Sorted iteration preserves group_id[] indices across separate invocations # where the mutex groups do not change. for _, g in sorted(group_name.iteritems()): if group_count[g] > 1: group_count[g] = 0 # Don't check this group again! group_id_count += 1 group_id[g] = '{}.{}'.format(self.name, group_id_count) # Collect the flags. for arg in sorted(args.flag_args): for name in arg.option_strings: if name.startswith('--'): name = name.replace('_', '-') # Don't include ancestor flags. if not self.__Ancestor(name): flag = Flag(name, description=arg.help, default=arg.default) # ArgParse does not have an explicit Boolean flag type. By # convention a flag with arg.nargs=0 and action='store_true' or # action='store_false' is a Boolean flag. arg.type gives no hint # (arg.type=bool would have been so easy) and we don't have access # to args.action here. Even then the flag can take on non-Boolean # values. If arg.default is not specified then it will be None, but # it can be set to anything. So we do a conservative 'truthiness' # test here. if arg.nargs == 0: flag.type = 'bool' flag.default = True if arg.default else False else: if arg.type == int: flag.type = 'int' elif arg.type == float: flag.type = 'float' if arg.nargs == '*': pass elif arg.nargs == '?': flag.countmax = 1 elif arg.nargs == '+': flag.countmin = 1 elif isinstance(arg.nargs, (int, long)): flag.countmin = arg.nargs flag.countmax = arg.nargs elif arg.required: flag.countmin = 1 flag.countmax = 1 if arg.metavar: flag.value = arg.metavar else: flag.value = name[2:].upper() if arg.choices: choices = sorted(arg.choices) if choices == ['false', 'true']: flag.type = 'bool' else: flag.choices = choices flag.category = arg.category or '' if arg.required: flag.required = 1 flag.resource = getattr(arg, 'completion_resource', '') if name in group_name and group_name[name] in group_id: flag.group = group_id[group_name[name]] if include_hidden_flags or not flag.hidden: self.flags[flag.name] = flag # Collect the positionals. for arg in args.positional_args: name = arg.dest.replace('_', '-') positional = Positional(name, description=_NormalizeDescription( arg.help)) if arg.metavar: positional.value = arg.metavar if arg.nargs != 0: if arg.nargs == '*': pass elif arg.nargs == '?': positional.countmax = 1 elif arg.nargs == '+': positional.countmin = 1 elif isinstance(arg.nargs, (int, long)): positional.countmin = arg.nargs positional.countmax = arg.nargs elif arg.required: positional.countmin = 1 positional.countmax = 1 positional.resource = getattr(arg, 'completion_resource', '') self.positionals.append(positional)
def __init__(self, command, parent): self.commands = {} self.flags = {} self.is_global = not bool(parent) self.is_group = command.is_group self.is_hidden = command.IsHidden() self.name = command.name.replace('_', '-') self.path = command.GetPath() self.positionals = [] self.release = command.ReleaseTrack().id self.sections = {} command_path_string = ' '.join(self.path) parent_path_string = ' '.join(parent.path) if parent else '' self.release, capsule = self.__Release( command, self.release, getattr(command, 'short_help', '')) # This code block must be meticulous on when and where LazyFormat expansion # is applied to the markdown snippets. First, no expanded text should be # passed as a LazyFormat kwarg. Second, no unexpanded text should appear # in the CLI tree. The LazyFormat calls are ordered to make sure that # doesn't happen. capsule = _NormalizeDescription(capsule) sections = {} self.release, description = self.__Release( command, self.release, getattr(command, 'long_help', '')) detailed_help = getattr(command, 'detailed_help', {}) sections.update(detailed_help) description = _NormalizeDescription(description) if 'DESCRIPTION' not in sections: sections['DESCRIPTION'] = description notes = command.GetNotesHelpSection() if notes: sections['NOTES'] = notes if sections: for name, contents in sections.iteritems(): # islower() section names were used to convert markdown in command # docstrings into the static self.section[] entries seen here. if name.isupper(): self.sections[name] = console_io.LazyFormat( _NormalizeDescription(contents), command=command_path_string, index=capsule, description=description, parent_command=parent_path_string) self.capsule = console_io.LazyFormat( capsule, command=command_path_string, man_name='.'.join(self.path), top_command=self.path[0] if self.path else '', parent_command=parent_path_string, **sections) # _parent is explicitly private so it won't appear in serialized output. self._parent = parent if parent: parent.commands[self.name] = self args = command.ai # Collect the command specific flags. for arg in args.flag_args: for name in arg.option_strings: if name.startswith('--'): # Don't include ancestor flags, with the exception of --help. if name != '--help' and self.__Ancestor(name): continue name = name.replace('_', '-') flag = Flag(arg, name) self.flags[flag.name] = flag # Collect the ancestor flags. for arg in args.ancestor_flag_args: for name in arg.option_strings: if name.startswith('--'): name = name.replace('_', '-') flag = Flag(arg, name) self.flags[flag.name] = flag # Collect the positionals. for arg in args.positional_args: name = arg.dest.replace('_', '-') positional = Positional(arg, name) self.positionals.append(positional) # Collect the arg group constraints. self.constraints = Constraint(args)
def testLazyFormatOneVarAndImplicitBraces(self): fmt = 'This { : is a {test} ? } with { one } var.' expected = 'This { : is a TEST ? } with { one } var.' actual = console_io.LazyFormat(fmt, test='TEST', one='ONE') self.assertEqual(expected, actual)
def __init__(self, command, parent, include_hidden_flags=True): self.group = isinstance(command, backend.CommandGroup) self.commands = {} self.flags = {} self.groups = {} self.hidden = command.IsHidden() self.name = command.name.replace('_', '-') self.path = command.GetPath() self.positionals = [] self.release = command.ReleaseTrack().id self.sections = {} command_path_string = ' '.join(self.path) parent_path_string = ' '.join(parent.path) if parent else '' self.release, capsule = self.__Release( command, self.release, getattr(command, 'short_help', '')) # This code block must be meticulous on when and where LazyFormat expansion # is applied to the markdown snippets. First, no expanded text should be # passed as a LazyFormat kwarg. Second, no unexpanded text should appear # in the CLI tree. The LazyFormat calls are ordered to make sure that # doesn't happen. capsule = _NormalizeDescription(capsule) sections = {} self.release, description = self.__Release( command, self.release, getattr(command, 'long_help', '')) detailed_help = getattr(command, 'detailed_help', {}) sections.update(detailed_help) description = _NormalizeDescription(description) if 'DESCRIPTION' not in sections: sections['DESCRIPTION'] = description notes = command.GetNotesHelpSection() if notes: sections['NOTES'] = notes if sections: for name, contents in sections.iteritems(): # islower() section names were used to convert markdown in command # docstrings into the static self.section[] entries seen here. if name.isupper(): self.sections[name] = console_io.LazyFormat( _NormalizeDescription(contents), command=command_path_string, man_name='.'.join(self.path), top_command=self.path[0] if self.path else '', parent_command=parent_path_string, index=capsule, description=description, **sections) self.capsule = console_io.LazyFormat( capsule, command=command_path_string, man_name='.'.join(self.path), top_command=self.path[0] if self.path else '', parent_command=parent_path_string, **sections) # _parent is explicitly private so it won't appear in serialized output. self._parent = parent if parent: parent.commands[self.name] = self args = command.ai # Initialize the mutually exclusive flag groups. group_count = {} group_name = {} for arg in args.flag_args: for name in arg.option_strings: if name.startswith('--'): name = name.replace('_', '-') if not self.__Ancestor(name): g = args.mutex_groups.get(arg.dest, None) if g: group_name[name] = g if g in group_count: group_count[g] += 1 else: group_count[g] = 1 group_id_count = 0 group_id = {} # Sorted iteration preserves group_id[] indices across separate invocations # where the mutex groups do not change. for _, g in sorted(group_name.iteritems()): if group_count[g] > 1: group_count[g] = 0 # Don't check this group again! group_id_count += 1 group_id[g] = '{}.{}'.format(self.name, group_id_count) self.groups[group_id[g]] = command.ai.group_attr[g] # Collect the flags. for arg in sorted(args.flag_args): for name in arg.option_strings: if name.startswith('--'): name = name.replace('_', '-') # Don't include ancestor flags. if not self.__Ancestor(name): flag = Flag(arg, name) if flag.name in group_name and group_name[flag.name] in group_id: flag.group = group_id[group_name[flag.name]] if include_hidden_flags or not flag.hidden: self.flags[flag.name] = flag # Collect the positionals. for arg in args.positional_args: name = arg.dest.replace('_', '-') positional = Positional(arg, name) self.positionals.append(positional)
def __init__(self, command, parent, include_hidden_flags=True): self.group = isinstance(command, backend.CommandGroup) self.commands = {} self.flags = {} self.groups = {} self.hidden = command.IsHidden() self.name = command.name.replace('_', '-') self.path = command.GetPath() self.positionals = [] self.release = command.ReleaseTrack().id self.sections = {} parent_command = parent.name.replace('_', '-') if parent else '' self.release, capsule = self.__Release( command, self.release, getattr(command, 'short_help', '')) self.capsule = console_io.LazyFormat(_NormalizeDescription(capsule), command=self.name, parent_command=parent_command) self.release, description = self.__Release( command, self.release, getattr(command, 'long_help', '')) description = console_io.LazyFormat(_NormalizeDescription(description), command=self.name, index=self.capsule, parent_command=parent_command) sections = getattr(command, 'detailed_help', None) if sections: for s in sections: # islower() section names were used to convert markdown in command # docstrings into the static self.section[] entries seen here. if s.isupper(): self.sections[s] = console_io.LazyFormat( _NormalizeDescription(sections[s]), command=self.name, index=self.capsule, description=description, parent_command=parent_command) if 'DESCRIPTION' not in self.sections: self.sections['DESCRIPTION'] = description # _parent is explicitly private so it won't appear in serialized output. self._parent = parent if parent: parent.commands[self.name] = self args = command.ai # Initialize the mutually exclusive flag groups. group_count = {} group_name = {} for arg in args.flag_args: for name in arg.option_strings: if name.startswith('--'): name = name.replace('_', '-') if not self.__Ancestor(name): g = args.mutex_groups.get(arg.dest, None) if g: group_name[name] = g if g in group_count: group_count[g] += 1 else: group_count[g] = 1 group_id_count = 0 group_id = {} # Sorted iteration preserves group_id[] indices across separate invocations # where the mutex groups do not change. for _, g in sorted(group_name.iteritems()): if group_count[g] > 1: group_count[g] = 0 # Don't check this group again! group_id_count += 1 group_id[g] = '{}.{}'.format(self.name, group_id_count) self.groups[group_id[g]] = command.ai.group_attr[g] # Collect the flags. for arg in sorted(args.flag_args): for name in arg.option_strings: if name.startswith('--'): name = name.replace('_', '-') # Don't include ancestor flags. if not self.__Ancestor(name): flag = Flag(arg, name) if flag.name in group_name and group_name[ flag.name] in group_id: flag.group = group_id[group_name[flag.name]] if include_hidden_flags or not flag.hidden: self.flags[flag.name] = flag # Collect the positionals. for arg in args.positional_args: name = arg.dest.replace('_', '-') positional = Positional(arg, name) self.positionals.append(positional)