def create_console(self, script): usage_sections = parse_section('usage:', script.__doc__) if len(usage_sections) == 0: raise DocoptLanguageError('"usage:" (case-insensitive) not found.') if len(usage_sections) > 1: raise DocoptLanguageError('More than one "usage:" (case-insensitive).') DocoptExit.usage = usage_sections[0] pattern = parse_pattern(formal_usage(DocoptExit.usage), parse_defaults(script.__doc__)) paths = [] paths.append({ "flags": [opt for opt in parse_defaults(script.__doc__) if not opt.argcount and opt.name not in ["--help", "--version"]], "extras": [opt for opt in parse_defaults(script.__doc__) if not opt.argcount and opt.name in ["--help", "--version"]], "options": [opt for opt in parse_defaults(script.__doc__) if opt.argcount and not opt.value], "default_options": [opt for opt in parse_defaults(script.__doc__) if opt.value], "arguments": [arg.name for arg in pattern.flat(Argument)], }) return { "name": script.__name__, "file": script.__file__, "doc": script.__doc__, "paths": paths, }
def __init__(self, docopt_str, base_descriptor): with open(base_descriptor, "r") as base_desc: self.descriptor = collections.OrderedDict(json.load(base_desc)) del self.descriptor['groups'] del self.descriptor['inputs'] del self.descriptor['output-files'] self.docopt_str = docopt_str self.dependencies = collections.OrderedDict() self.all_desc_and_type = collections.OrderedDict() self.unique_ids = [] try: # docopt code snippet to extract args tree (pattern) # should run if docopt script is valid options = parse_defaults(docopt_str) self.pattern = parse_pattern( formal_usage(self._parse_section('usage:', docopt_str)[0]), options) argv = parse_argv(TokenStream(sys.argv[1:], DocoptLanguageError), list(options), False) pattern_options = set(self.pattern.flat(Option)) for options_shortcut in self.pattern.flat(AnyOptions): doc_options = parse_defaults(docopt_str) options_shortcut.children = list( set(doc_options) - pattern_options) matched, left, collected = self.pattern.fix().match(argv) except Exception: os.remove(base_descriptor) raise_error(ImportError, "Invalid docopt script")
def docopt(doc, argv=None, help=True, version=None, options_first=False): # @ReservedAssignment help """Re-implementation of docopt.docopt() function to parse ANYTHING at the end (for proxying django options).""" if argv is None: argv = sys.argv[1:] DocoptExit.usage = printable_usage(doc) options = parse_defaults(doc) pattern = parse_pattern(formal_usage(DocoptExit.usage), options) argv = parse_argv(TokenStream(argv, DocoptExit), list(options), options_first) pattern_options = set(pattern.flat(Option)) for ao in pattern.flat(AnyOptions): doc_options = parse_defaults(doc) ao.children = list(set(doc_options) - pattern_options) extras(help, version, argv, doc) __matched, __left, collected = pattern.fix().match(argv) # if matched and left == []: # better error message if left? if collected: # better error message if left? result = Dict((a.name, a.value) for a in (pattern.flat() + collected)) collected_django_options = len(result.get("DJANGO_OPTIONS", [])) result["DJANGO_OPTIONS"] = ( result.get("DJANGO_OPTIONS", []) + sys.argv[len(collected) + (collected_django_options or 1) :] ) # If any of the collected arguments are also in the DJANGO_OPTIONS, # then exit because we don't want users to have put options for kalite # at the end of the command if any(map(lambda x: x.name in map(lambda x: x.split("=")[0], result["DJANGO_OPTIONS"]), collected)): sys.stderr.write( "Cannot mix django manage command options with kalite options. " "Always put django management options last.\n\n" ) raise DocoptExit() return result raise DocoptExit()
def parse_params(cmd): # this function creates a parameter tree for the target docopt tool. # a parameter tree is a CommandParams instance, see the documentation of the class # this function also returns a second parameter, which is a dictionary of option->option help string from docopt import parse_defaults, parse_pattern, formal_usage, printable_usage usage = get_usage(cmd) options = parse_defaults(usage) pattern = parse_pattern(formal_usage(printable_usage(usage)), options) param_tree = CommandParams() build_command_tree(pattern, param_tree) return param_tree, dict(list(get_options_descriptions(usage)))
def parse_params(cmd, given_usage=None): # This creates a parameter tree (CommandParams object) for the target docopt tool. # Also returns a second parameter, a dict of: # option->option-help-string from docopt import parse_defaults, parse_pattern, formal_usage, printable_usage usage = get_usage(cmd) if given_usage is None else given_usage options = parse_defaults(usage) pattern = parse_pattern(formal_usage(printable_usage(usage)), options) param_tree = CommandParams() build_command_tree(pattern, param_tree) return param_tree, dict(list(get_options_descriptions(usage)))
def parse_params(cmd): # This creates a parameter tree (CommandParams object) for the target docopt tool. # Also returns a second parameter, a dict of: # option->option-help-string from docopt import parse_defaults, parse_pattern, formal_usage, printable_usage usage = get_usage(cmd) options = parse_defaults(usage) pattern = parse_pattern(formal_usage(printable_usage(usage)), options) param_tree = CommandParams() build_command_tree(pattern, param_tree) return param_tree, dict(list(get_options_descriptions(usage)))
def pattern(self): """Returns a docopt match pattern.""" try: from docopt import parse_defaults, formal_usage, parse_pattern except ImportError: LOGGER.warning('docopt not installed.') LOGGER.warning('Command help pattern generation unavailable.') return None usage = self.usage options = parse_defaults(usage) return parse_pattern(formal_usage(usage), options)
def parse_atom(tokens, options): if len(tokens) > 3 and (tokens[0], tokens[2]) == ('[', ']') and \ tokens[1].startswith('git-') and tokens[1].endswith('-options'): token = tokens[1][4:-8] tokens.move(); tokens.move(); tokens.move() ret = [] for opt in known_options[token]: opt = docopt.parse_pattern(opt, []).children[0] opt = GitOption(opt.short, opt.long, opt.argcount, opt.value) ret.append(opt) options.append(opt) return ret return docopt.orig_parse_atom(tokens, options)
def settable_options(doc, argv, ignore, options_first): """Determine which options we can set, which ones are boolean, and which ones are repeatable. All set items are option long names. :param str doc: Docstring from docoptcfg(). :param iter argv: CLI arguments from docoptcfg(). :param iter ignore: Options to ignore from docoptcfg(). :param bool options_first: docopt argument from docoptcfg(). :return: Settable options, boolean options, repeatable options, and short to long option name mapping. :rtype: tuple """ settable, booleans, repeatable, short_map = set(), set(), set(), dict() # Determine which options are settable by docoptcfg and which ones are flags/booleans. options = docopt.parse_defaults(doc) short_map.update((o.short, o.long) for o in options) parsed_argv = docopt.parse_argv( docopt.TokenStream(argv, docopt.DocoptExit), list(options), options_first) overridden = [o.long for o in parsed_argv if hasattr(o, 'long')] for option in options: if option.long in overridden or (option.long in ignore or option.short in ignore) or option.long is None: continue if option.argcount == 0: booleans.add(option.long) settable.add(option.long) # Determine which options are repeatable. if settable and '...' in doc: pattern = docopt.parse_pattern( docopt.formal_usage(docopt.DocoptExit.usage), options) for option in pattern.fix().flat(): if not hasattr(option, 'long'): continue # Positional argument or sub-command. if getattr(option, 'long') not in settable: continue # Don't care about this if we can't set it. if getattr(option, 'long') in booleans and getattr( option, 'value') == 0: repeatable.add(getattr(option, 'long')) elif hasattr(getattr(option, 'value'), '__iter__'): repeatable.add(getattr(option, 'long')) return settable, booleans, repeatable, short_map
def docopt(doc, argv=None, help=True, version=None, options_first=False): # @ReservedAssignment help """Re-implementation of docopt.docopt() function to parse ANYTHING at the end (for proxying django options).""" if argv is None: argv = sys.argv[1:] DocoptExit.usage = printable_usage(doc) options = parse_defaults(doc) pattern = parse_pattern(formal_usage(DocoptExit.usage), options) argv = parse_argv(TokenStream(argv, DocoptExit), list(options), options_first) pattern_options = set(pattern.flat(Option)) for ao in pattern.flat(AnyOptions): doc_options = parse_defaults(doc) ao.children = list(set(doc_options) - pattern_options) extras(help, version, argv, doc) __matched, __left, collected = pattern.fix().match(argv) # if matched and left == []: # better error message if left? if collected: # better error message if left? result = Dict((a.name, a.value) for a in (pattern.flat() + collected)) collected_django_options = len(result.get('DJANGO_OPTIONS', [])) result['DJANGO_OPTIONS'] = (result.get('DJANGO_OPTIONS', []) + sys.argv[len(collected) + (collected_django_options or 1):]) # If any of the collected arguments are also in the DJANGO_OPTIONS, # then exit because we don't want users to have put options for kalite # at the end of the command if any( map( lambda x: x.name in map(lambda x: x.split("=")[0], result[ 'DJANGO_OPTIONS']), collected)): sys.stderr.write( "Cannot mix django manage command options with kalite options. " "Always put django management options last.\n\n") raise DocoptExit() return result raise DocoptExit()
def settable_options(doc, argv, ignore, options_first): """Determine which options we can set, which ones are boolean, and which ones are repeatable. All set items are option long names. :param str doc: Docstring from docoptcfg(). :param iter argv: CLI arguments from docoptcfg(). :param iter ignore: Options to ignore from docoptcfg(). :param bool options_first: docopt argument from docoptcfg(). :return: Settable options, boolean options, repeatable options, and short to long option name mapping. :rtype: tuple """ settable, booleans, repeatable, short_map = set(), set(), set(), dict() # Determine which options are settable by docoptcfg and which ones are flags/booleans. options = docopt.parse_defaults(doc) short_map.update((o.short, o.long) for o in options) parsed_argv = docopt.parse_argv(docopt.TokenStream(argv, docopt.DocoptExit), list(options), options_first) overridden = [o.long for o in parsed_argv if hasattr(o, 'long')] for option in options: if option.long in overridden or (option.long in ignore or option.short in ignore): continue if option.argcount == 0: booleans.add(option.long) settable.add(option.long) # Determine which options are repeatable. if settable and '...' in doc: pattern = docopt.parse_pattern(docopt.formal_usage(docopt.DocoptExit.usage), options) for option in pattern.fix().flat(): if not hasattr(option, 'long'): continue # Positional argument or sub-command. if getattr(option, 'long') not in settable: continue # Don't care about this if we can't set it. if getattr(option, 'long') in booleans and getattr(option, 'value') == 0: repeatable.add(getattr(option, 'long')) elif hasattr(getattr(option, 'value'), '__iter__'): repeatable.add(getattr(option, 'long')) return settable, booleans, repeatable, short_map
def docopt_cmd_completion(func, **kwargs): options = parse_defaults(func.__doc__) pattern = parse_pattern(formal_usage(printable_usage(func.__doc__)), options).children[0] def get_state(it, pattern): try: value = next(it) except StopIteration: return pattern res = [] for x in pattern: if ((type(x[0]) == list and value in flatten(x[0])) or value == x[0]): res.append(x[1:]) if res: return get_state(it, res) return [] def wrapper(self, text, line, begidx, endidx): argv = shlex.split(line[:endidx])[1:] if not line[endidx - 1].isspace(): target = argv[-1] argv = argv[:-1] else: target = '' state = get_state(iter(argv), pattern.noflat()) res = [] for x in state: if type(x[0]) == list: res.extend(flatten(x[0])) else: res.append(x[0]) return list(set(x for x in res if x.startswith(target))) wrapper.__name__ = str('complete_' + func.__name__[3:]) wrapper.__module__ = func.__module__ wrapper.__doc__ = func.__doc__ return wrapper
def test_parse_pattern(): o = [Option("-h"), Option("-v", "--verbose"), Option("-f", "--file", 1)] assert parse_pattern("[ -h ]", options=o) == Required(NotRequired(Option("-h"))) assert parse_pattern("[ ARG ... ]", options=o) == Required( NotRequired(OneOrMore(Argument("ARG")))) assert parse_pattern("[ -h | -v ]", options=o) == Required( NotRequired(Either(Option("-h"), Option("-v", "--verbose")))) assert parse_pattern("( -h | -v [ --file <f> ] )", options=o) == Required( Required( Either( Option("-h"), Required(Option("-v", "--verbose"), NotRequired(Option("-f", "--file", 1, None)))))) assert parse_pattern("(-h|-v[--file=<f>]N...)", options=o) == Required( Required( Either( Option("-h"), Required(Option("-v", "--verbose"), NotRequired(Option("-f", "--file", 1, None)), OneOrMore(Argument("N")))))) assert parse_pattern("(N [M | (K | L)] | O P)", options=[]) == Required( Required( Either( Required( Argument("N"), NotRequired( Either(Argument("M"), Required(Either(Argument("K"), Argument("L")))))), Required(Argument("O"), Argument("P")), ))) assert parse_pattern("[ -h ] [N]", options=o) == Required(NotRequired(Option("-h")), NotRequired(Argument("N"))) assert parse_pattern("[options]", options=o) == Required(NotRequired(OptionsShortcut())) assert parse_pattern("[options] A", options=o) == Required(NotRequired(OptionsShortcut()), Argument("A")) assert parse_pattern("-v [options]", options=o) == Required(Option("-v", "--verbose"), NotRequired(OptionsShortcut())) assert parse_pattern("ADD", options=o) == Required(Argument("ADD")) assert parse_pattern("<add>", options=o) == Required(Argument("<add>")) assert parse_pattern("add", options=o) == Required(Command("add"))
args['--template'] = f.read() if args['--header']: with open(template_h_path, 'r') as f: template_h = f.read() except IOError as e: sys.exit(e) doc = args['<docopt>'] usage = docopt.parse_section('usage:', doc) s = ['More than one ', '"usage:" (case-insensitive)', ' not found.'] usage = {0: s[1:], 1: usage[0] if usage else None}.get(len(usage), s[:2]) if isinstance(usage, list): raise docopt.DocoptLanguageError(''.join(usage)) all_options = docopt.parse_defaults(doc) pattern = docopt.parse_pattern(docopt.formal_usage(usage), all_options) leafs, commands, arguments, flags, options = parse_leafs( pattern, all_options) t_commands = ';\n '.join('int %s' % c_name(cmd.name) for cmd in commands) t_commands = (('\n /* commands */\n ' + t_commands + ';') if t_commands != '' else '') t_arguments = ';\n '.join('char *%s' % c_name(arg.name) for arg in arguments) t_arguments = (('\n /* arguments */\n ' + t_arguments + ';') if t_arguments != '' else '') t_flags = ';\n '.join('int %s' % c_name(flag.long or flag.short) for flag in flags) t_flags = (('\n /* options without arguments */\n ' + t_flags + ';') if t_flags != '' else '')
def _parse_docopt(doc): options = docopt.parse_defaults(doc) pure_doc = docopt.formal_usage(docopt.printable_usage(doc)) pattern = docopt.parse_pattern(pure_doc, options) return pattern
return t % (('long' if o.long else 'short'), to_c( o.long or o.short), c_name(o.long or o.short)) def c_if_not_flag(o): t = """ else if (o->option.argument && strcmp(o->option.o%s, %s) == 0) { args.%s = o->option.argument;\n }""" return t % (('long' if o.long else 'short'), to_c( o.long or o.short), c_name(o.long or o.short)) if __name__ == '__main__': help_message = sys.stdin.read() usage_pattern = printable_usage(help_message) options = parse_doc_options(help_message) formal_pattern = parse_pattern(formal_usage(usage_pattern), options=options) formal_pattern.fix() out = __doc__ out = out.replace( '<<<flag_options>>>', ';\n '.join('int %s' % c_name(o.long or o.short) for o in options if o.argcount == 0)) out = out.replace( '<<<options_with_arguments>>>', ';\n '.join('char *%s' % c_name(o.long or o.short) for o in options if o.argcount == 1)) out = out.replace('<<<help_message>>>', to_c(help_message)) out = out.replace('<<<usage_pattern>>>', to_c(usage_pattern)) out = out.replace( '<<<defaults>>>', ', '.join(
if args["--template"] is None: args["--template"] = os.path.join(os.path.dirname(os.path.realpath(__file__)), "template.c") with open(args["--template"], "r") as f: args["--template"] = f.read() except IOError as e: sys.exit(e) doc = args["<docopt>"] usage = docopt.parse_section("usage:", doc) s = ["More than one ", '"usage:" (case-insensitive)', " not found."] usage = {0: s[1:], 1: usage[0] if usage else None}.get(len(usage), s[:2]) if isinstance(usage, list): raise docopt.DocoptLanguageError("".join(usage)) all_options = docopt.parse_defaults(doc) pattern = docopt.parse_pattern(docopt.formal_usage(usage), all_options) leafs, commands, arguments, flags, options = parse_leafs(pattern, all_options) t_commands = ";\n ".join("int %s" % c_name(cmd.name) for cmd in commands) t_commands = ("\n /* commands */\n " + t_commands + ";") if t_commands != "" else "" t_arguments = ";\n ".join("char *%s" % c_name(arg.name) for arg in arguments) t_arguments = ("\n /* arguments */\n " + t_arguments + ";") if t_arguments != "" else "" t_flags = ";\n ".join("int %s" % c_name(flag.long or flag.short) for flag in flags) t_flags = ("\n /* options without arguments */\n " + t_flags + ";") if t_flags != "" else "" t_options = ";\n ".join("char *%s" % c_name(opt.long or opt.short) for opt in options) t_options = ("\n /* options with arguments */\n " + t_options + ";") if t_options != "" else "" t_defaults = ", ".join(to_c(leaf.value) for leaf in leafs) t_defaults = re.sub(r'"(.*?)"', r'(char*) "\1"', t_defaults) t_defaults = "\n ".join(textwrap.wrap(t_defaults, 72)) t_defaults = ("\n " + t_defaults + ",") if t_defaults != "" else "" t_elems_cmds = ",\n ".join([c_command(cmd) for cmd in (commands)])
def test_parse_pattern(): o = [Option('-h'), Option('-v', '--verbose'), Option('-f', '--file', 1)] assert parse_pattern('[ -h ]', options=o) == \ Required(Optional(Option('-h'))) assert parse_pattern('[ ARG ... ]', options=o) == \ Required(Optional(OneOrMore(Argument('ARG')))) assert parse_pattern('[ -h | -v ]', options=o) == \ Required(Optional(Either(Option('-h'), Option('-v', '--verbose')))) assert parse_pattern('( -h | -v [ --file <f> ] )', options=o) == \ Required(Required( Either(Option('-h'), Required(Option('-v', '--verbose'), Optional(Option('-f', '--file', 1, None)))))) assert parse_pattern('(-h|-v[--file=<f>]N...)', options=o) == \ Required(Required(Either(Option('-h'), Required(Option('-v', '--verbose'), Optional(Option('-f', '--file', 1, None)), OneOrMore(Argument('N')))))) assert parse_pattern('(N [M | (K | L)] | O P)', options=[]) == \ Required(Required(Either( Required(Argument('N'), Optional(Either(Argument('M'), Required(Either(Argument('K'), Argument('L')))))), Required(Argument('O'), Argument('P'))))) assert parse_pattern('[ -h ] [N]', options=o) == \ Required(Optional(Option('-h')), Optional(Argument('N'))) assert parse_pattern('[options]', options=o) == \ Required(Optional(OptionsShortcut())) assert parse_pattern('[options] A', options=o) == \ Required(Optional(OptionsShortcut()), Argument('A')) assert parse_pattern('-v [options]', options=o) == \ Required(Option('-v', '--verbose'), Optional(OptionsShortcut())) assert parse_pattern('ADD', options=o) == Required(Argument('ADD')) assert parse_pattern('<add>', options=o) == Required(Argument('<add>')) assert parse_pattern('add', options=o) == Required(Command('add'))
def main(): args = docopt.docopt(__doc__) try: if args['<docopt>'] is not None: with open(args['<docopt>'], 'r') as f: args['<docopt>'] = f.read() elif args['<docopt>'] is None and sys.stdin.isatty(): print(__doc__.strip("\n")) sys.exit("") else: args['<docopt>'] = sys.stdin.read() except IOError as e: sys.exit(e) doc = args['<docopt>'] usage = parse_section('usage:', doc) s = ['More than one ', '"usage:" (case-insensitive)', ' not found.'] usage = {0: s[1:], 1: usage[0] if usage else None}.get(len(usage), s[:2]) if isinstance(usage, list): raise docopt.DocoptLanguageError(''.join(usage)) options = docopt.parse_defaults(doc) pattern = docopt.parse_pattern(docopt.formal_usage(usage), options) fsm = ragel_ast(pattern) leafs, commands, arguments, flags, options = parse_leafs(pattern) command_fields = '\n '.join( map(lambda c: 'int {0};'.format(clean_name(c)), commands)) flag_fields = '\n '.join( map(lambda c: 'int {0};'.format(clean_name(c)), flags)) option_fields = '\n '.join( map(lambda c: 'char* {0};'.format(clean_name(c)), options)) argument_fields = '\n '.join( map(lambda c: 'char* {0};'.format(clean_name(c)), arguments)) command_actions = '\n '.join( map( lambda c: 'action command_{0}{{ fsm->opt->{0} = 1; }}'.format( clean_name(c)), commands)) flag_actions = '\n '.join( map( lambda c: 'action option_{0}{{ fsm->opt->{0} = 1; }}'.format( clean_name(c)), flags)) option_actions = '\n '.join( map( lambda c: 'action option_{0}{{ fsm->opt->{0} = strdup(fsm->buffer); }}'. format(clean_name(c)), options)) argument_actions = '\n '.join( map( lambda c: 'action argument_{0}{{ fsm->opt->{0} = strdup(fsm->buffer); }}'. format(clean_name(c)), arguments)) options_with_defaults = filter(lambda x: x.value is not None, options) option_defaults = '\n '.join( map( lambda c: 'fsm->opt->{0} = strdup("{1}");'.format( clean_name(c), c.value), options_with_defaults)) usage = '\n '.join( map(lambda l: 'fprintf(stdout, "{0}\\n");'.format(l), doc.split('\n'))) file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "template.rl") print( Template(open(file).read()).safe_substitute( fsm=fsm, usage=usage, command_fields=command_fields, flag_fields=flag_fields, option_fields=option_fields, argument_fields=argument_fields, command_actions=command_actions, flag_actions=flag_actions, option_actions=option_actions, argument_actions=argument_actions, option_defaults=option_defaults, ))
def test_parse_pattern(): o = [Option('-h'), Option('-v', '--verbose'), Option('-f', '--file', 1)] assert parse_pattern('[ -h ]', options=o) == \ Required(Optional(Option('-h', None, 0, True))) assert parse_pattern('[ ARG ... ]', options=o) == \ Required(Optional(OneOrMore(Argument('ARG')))) assert parse_pattern('[ -h | -v ]', options=o) == \ Required(Optional(Either(Option('-h', None, 0, True), Option('-v', '--verbose', 0, True)))) assert parse_pattern('( -h | -v [ --file f.txt ] )', options=o) == \ Required(Required( Either(Option('-h', None, 0, True), Required(Option('-v', '--verbose', 0, True), Optional(Option('-f', '--file', 1, 'f.txt')))))) assert parse_pattern('(-h|-v[--file=f.txt]N...)', options=o) == \ Required(Required(Either(Option('-h', None, 0, True), Required(Option('-v', '--verbose', 0, True), Optional(Option('-f', '--file', 1, 'f.txt')), OneOrMore(Argument('N')))))) assert parse_pattern('(N [M | (K | L)] | O P)', options=[]) == \ Required(Required(Either( Required(Argument('N'), Optional(Either(Argument('M'), Required(Either(Argument('K'), Argument('L')))))), Required(Argument('O'), Argument('P'))))) assert parse_pattern('[ -h ] [N]', options=o) == \ Required(Optional(Option('-h', None, 0, True)), Optional(Argument('N'))) assert parse_pattern('[options]', options=o) == Required( Optional(AnyOptions())) assert parse_pattern('[options] A', options=o) == Required( Optional(AnyOptions()), Argument('A')) assert parse_pattern('-v [options]', options=o) == Required( Option('-v', '--verbose', 0, True), Optional(AnyOptions())) assert parse_pattern('ADD', options=o) == Required(Argument('ADD')) assert parse_pattern('<add>', options=o) == Required(Argument('<add>')) assert parse_pattern('add', options=o) == Required(Command('add'))
def main(): assert __doc__ is not None args = docopt.docopt(__doc__) try: if args['<docopt>'] is not None: with open(args['<docopt>'], 'r') as f: args['<docopt>'] = f.read() elif args['<docopt>'] is None and sys.stdin.isatty(): print(__doc__.strip("\n")) sys.exit("") else: args['<docopt>'] = sys.stdin.read() if args['--template'] is None: args['--template'] = template_c else: with open(args['--template'], 'rt') as f: args['--template'] = f.read() if args['--template-header'] is None: args['--template-header'] = template_h else: with open(args['--template-header'], 'rt') as f: args['--template-header'] = f.read() except IOError as e: sys.exit(e) doc = args['<docopt>'] usage = docopt.parse_section('usage:', doc) error_str_l = 'More than one ', '"usage:" (case-insensitive)', ' not found.' usage = {0: error_str_l[1:], 1: usage[0] if usage else None}.get(len(usage), error_str_l[:2]) if isinstance(usage, list): raise docopt.DocoptLanguageError(''.join(usage)) all_options = docopt.parse_defaults(doc) pattern = docopt.parse_pattern(docopt.formal_usage(usage), all_options) leafs, commands, arguments, flags, options = parse_leafs(pattern, all_options) _indent = ' ' * 4 t_commands = ';\n{indent}'.format(indent=_indent).join('size_t {!s}'.format(c_name(cmd.name)) for cmd in commands) t_commands = '\n{indent}/* commands */\n{indent}{t_commands};'.format(indent=_indent, t_commands=t_commands) \ if t_commands != '' else '' t_arguments = ';\n{indent}'.join('char *{!s}'.format(c_name(arg.name)) for arg in arguments) t_arguments = '\n{indent}/* arguments */\n{indent}{t_arguments};'.format(indent=_indent, t_arguments=t_arguments) \ if t_arguments != '' else '' t_flags = ';\n{indent}'.format(indent=_indent).join('size_t {!s}'.format(c_name(flag.long or flag.short)) for flag in flags) t_flags = '\n{indent}/* options without arguments */\n{indent}{t_flags};'.format(indent=_indent, t_flags=t_flags) \ if t_flags != '' else '' t_options = ';\n{indent}'.format(indent=_indent).join('char *{!s}'.format(c_name(opt.long or opt.short)) for opt in options) t_options = '\n{indent}/* options with arguments */\n{indent}{t_options};'.format(indent=_indent, t_options=t_options) \ if t_options != '' else '' t_defaults = ', '.join(to_c(leaf.value) for leaf in leafs) t_defaults = re.sub(r'"(.*?)"', r'(char *) "\1"', t_defaults) t_defaults = '\n{indent}'.format(indent=_indent * 2).join(textwrap.wrap(t_defaults, 72)) t_defaults = '\n{indent}{t_defaults},'.format(indent=_indent * 2, t_defaults=t_defaults) if t_defaults != '' else '' t_elems_cmds = ',\n{indent}'.format(indent=_indent * 2).join(c_command(cmd) for cmd in commands) t_elems_cmds = '\n{indent}{t_elems_cmds}'.format(indent=_indent * 2, t_elems_cmds=t_elems_cmds) if t_elems_cmds != '' else '' t_elems_args = ',\n{indent}'.format(indent=_indent * 2).join(c_argument(arg) for arg in arguments) t_elems_args = '\n{indent}{t_elems_args}'.format(indent=_indent * 2, t_elems_args=t_elems_args) if t_elems_args != '' else '' t_elems_opts = ',\n{indent}'.format(indent=_indent * 2).join(c_option(o) for o in (flags + options)) t_elems_opts = '\n{indent}{t_elems_opts}'.format(indent=_indent * 2, t_elems_opts=t_elems_opts) if t_elems_opts != '' else '' ''' t_elems_n_commands = str(len(commands)) t_elems_n_arguments = str(len(arguments)) t_elems_n_options = str(len(flags + options)) t_elems_n_cmds = str(len(commands)) t_elems_n = ', '.join(str(len(l)) for l in (commands, arguments, (flags + options))) print( 't_elems_n_commands:', t_elems_n_commands, ';\n', 't_elems_n_arguments:', t_elems_n_arguments, ';\n', 't_elems_n_options:', t_elems_n_options, ';\n', 't_elems_n_cmds:', t_elems_n_cmds, ';\n', 't_elems_n:', t_elems_n, ';' ) ''' t_if_command = ' else '.join(c_if_command(command) for command in commands) t_if_command = '\n{indent}{t_if_command}'.format(indent=_indent * 2, t_if_command=t_if_command) if t_if_command != '' else '' t_if_argument = ' else '.join(c_if_argument(arg) for arg in arguments) t_if_argument = '\n{indent}{t_if_argument}'.format( indent=_indent * 2, t_if_argument='\n{indent}'.format(indent=_indent * 2).join(t_if_argument.splitlines()) ) if t_if_argument != '' else '' t_if_flag = ''.join('\n{indent}'.format(indent=_indent * 2).join(c_if_flag(flag).splitlines()) for flag in flags) t_if_option = ''.join( '\n{indent}'.format(indent=_indent * 2).join(c_if_option(opt).splitlines()) for opt in options ) if not args['--output-name']: header_output_name = '<stdout>' else: base, ext = os.path.splitext(args['--output-name']) if ext not in frozenset(('.h', '.c')): base = args['--output-name'] args['--output-name'] = "{base}.c".format(base=base) header_output_name = "{base}.h".format(base=base) header_name = os.path.basename(header_output_name) doc = doc.splitlines() doc_n = len(doc) template_out = Template(args['--template']).safe_substitute( help_message='\n{indent}'.format(indent=_indent).join(to_initializer(doc).splitlines()), help_message_n=doc_n, usage_pattern='\n{indent}'.format(indent=_indent * 2).join(to_c(usage).splitlines()), if_flag=t_if_flag, if_option=t_if_option, if_command=t_if_command, if_argument=t_if_argument, defaults=t_defaults, elems_cmds=null_if_zero(t_elems_cmds), elems_args=null_if_zero(t_elems_args), elems_opts=null_if_zero(t_elems_opts), t_elems_n_commands=str(len(commands)), t_elems_n_arguments=str(len(arguments)), t_elems_n_options=str(len(flags + options)), header_name=header_name ) template_header_out = Template(args['--template-header']).safe_substitute( commands=t_commands, arguments=t_arguments, flags=t_flags, options=t_options, help_message_n=doc_n, # nargs=t_nargs ).replace('$header_no_ext', os.path.splitext(header_name)[0].upper()) if args['--output-name'] is None: print(template_out.strip(), '\n') else: try: with open(sys.stdout if args['--output-name'] in (None, "<stdout>") else args['--output-name'], 'w') as f: f.write(template_out.strip() + '\n') with open(sys.stdout if header_output_name == "<stdout>" else header_output_name, 'w') as f: f.write(template_header_out.strip() + '\n') except IOError as e: sys.exit(str(e))
def test_parse_pattern(): o = [Option("-h"), Option("-v", "--verbose"), Option("-f", "--file", 1)] assert parse_pattern("[ -h ]", options=o) == Required(Optional(Option("-h"))) assert parse_pattern("[ ARG ... ]", options=o) == Required(Optional(OneOrMore(Argument("ARG")))) assert parse_pattern("[ -h | -v ]", options=o) == Required( Optional(Either(Option("-h"), Option("-v", "--verbose"))) ) assert parse_pattern("( -h | -v [ --file <f> ] )", options=o) == Required( Required(Either(Option("-h"), Required(Option("-v", "--verbose"), Optional(Option("-f", "--file", 1, None))))) ) assert parse_pattern("(-h|-v[--file=<f>]N...)", options=o) == Required( Required( Either( Option("-h"), Required( Option("-v", "--verbose"), Optional(Option("-f", "--file", 1, None)), OneOrMore(Argument("N")) ), ) ) ) assert parse_pattern("(N [M | (K | L)] | O P)", options=[]) == Required( Required( Either( Required( Argument("N"), Optional(Either(Argument("M"), Required(Either(Argument("K"), Argument("L"))))) ), Required(Argument("O"), Argument("P")), ) ) ) assert parse_pattern("[ -h ] [N]", options=o) == Required(Optional(Option("-h")), Optional(Argument("N"))) assert parse_pattern("[options]", options=o) == Required(Optional(AnyOptions())) assert parse_pattern("[options] A", options=o) == Required(Optional(AnyOptions()), Argument("A")) assert parse_pattern("-v [options]", options=o) == Required(Option("-v", "--verbose"), Optional(AnyOptions())) assert parse_pattern("ADD", options=o) == Required(Argument("ADD")) assert parse_pattern("<add>", options=o) == Required(Argument("<add>")) assert parse_pattern("add", options=o) == Required(Command("add"))
def main(): args = docopt.docopt(__doc__, version='none') with open(args['<docopt_file>'], 'r') as f: args['<docopt_file>'] = f.read() template_h_name = args['--template_h'] template_c_name = args['--template_c'] args['template_h_obj'] = read_template_file_contents(args['--template_h']) args['template_c_obj'] = read_template_file_contents(args['--template_c']) doc = args['<docopt_file>'] usage = docopt.printable_usage(doc) all_options = docopt.parse_defaults(doc) pattern = docopt.parse_pattern(docopt.formal_usage(usage), all_options) prompt = usage.split()[1].strip() usage_lines = [x.replace(prompt, "") for x in usage.split('\n')[1:]] tokens = [] commands = [] # I'm not sure why we have to reach in here, but it "works" required_commands = pattern.children[0].children for idx, required in enumerate(required_commands): parts = [ o.name for o in required.children if isinstance(o, docopt.Command) ] if not parts: continue # "help" is a special case? So exclude? if "help" in parts: continue tokens.extend(parts) docopt_text = usage_lines[idx].strip( ) if idx < len(usage_lines) else None commands.append(Command(parts, docopt_text)) if args['--short'] is not None: doc = doc.replace(prompt + " ", args['--short'] + " ") rendering = Rendering(args['<module_name>'], commands, prompt, doc, args['--multithreaded']) if len(rendering.tokens) > 64: raise docopt.DocoptExit( 'Too many unique tokens ({}) for Docopt μC (max:64)'.format( len(rendering.tokens))) too_long_commands = [] for cmd in rendering.commands: if len(cmd.parts) > 6: too_long_commands.append(cmd) if too_long_commands: summaries = [ ' > {}'.format(" ".join(p for p in c.parts)) for c in too_long_commands ] raise docopt.DocoptExit('\n'.join([ 'The following commands are too long for Docopt μC (max: 6 long):' ] + summaries)) output_h_filename = args['--template_h'].replace(args['--template_prefix'], rendering.include_name) output_c_filename = args['--template_c'].replace(args['--template_prefix'], rendering.include_name) output_h_filename = os.path.join(args['--output_dir'], output_h_filename) output_c_filename = os.path.join(args['--output_dir'], output_c_filename) with open(output_h_filename, 'w') as f: f.write(args['template_h_obj'].render(rendering=rendering)) with open(output_c_filename, 'w') as f: f.write(args['template_c_obj'].render(rendering=rendering)) if args["--no-docopt-args-h"] is False: # copy the docopt header file to the output directory docopt_args = pkg_resources.resource_filename( __name__, 'templates/docopt_args.h') shutil.copy2(docopt_args, args['--output_dir'])
c_name(o.long or o.short)) def c_if_not_flag(o): t = """ else if (o->option.argument && strcmp(o->option.o%s, %s) == 0) { args.%s = o->option.argument;\n }""" return t % (('long' if o.long else 'short'), to_c(o.long or o.short), c_name(o.long or o.short)) if __name__ == '__main__': help_message=sys.stdin.read() usage_pattern = printable_usage(help_message) options = parse_doc_options(help_message) formal_pattern = parse_pattern(formal_usage(usage_pattern), options=options) formal_pattern.fix() out = __doc__ out = out.replace('<<<flag_options>>>', ';\n '.join('int %s' % c_name(o.long or o.short) for o in options if o.argcount == 0)) out = out.replace('<<<options_with_arguments>>>', ';\n '.join('char *%s' % c_name(o.long or o.short) for o in options if o.argcount == 1)) out = out.replace('<<<help_message>>>', to_c(help_message)) out = out.replace('<<<usage_pattern>>>', to_c(usage_pattern)) out = out.replace('<<<defaults>>>', ', '.join(to_c(o.value) for o in sorted(options, key=lambda o: o.argcount))) out = out.replace('<<<options>>>',