예제 #1
0
    def get_short_option_arg(self, current, token, arg_token, rest):
        prepended = False

        opt_2_ins = {}
        # TODO: Better solution, maybe cache one
        for options in self.titled_opt_to_ins.values():
            opt_2_ins.update(options)

        if current in opt_2_ins:
            ins = opt_2_ins[current][0]
            # In Options it requires no argument
            if ins.ref is None:
                # sth stacked with it
                if rest:
                    if Atom.get_class(rest)[0] is Argument:
                        raise DocpieError(
                            ('%s announced difference in '
                             'Options(%s) and Usage(%s)') %
                            (current, ins, current))
                    if not (self.stdopt and self.attachopt):
                        raise DocpieError(
                            ("You can't write %s while it requires "
                             "argument and attachopt=False") % current)

                    token.insert(0, '-' + rest)
                    prepended = True
            # In Options it requires argument
            else:
                if rest:
                    arg_token = Token([rest])
                elif arg_token:
                    pass
                else:
                    _current = token.current()
                    if _current in '([':
                        tk = [token.next()]
                        tk.extend(token.till_end_bracket(tk[0]))
                        tk.append(')' if tk[0] == '(' else ']')
                        arg_token = Token(tk)
                    elif _current in ('...', '|'):
                        raise DocpieError(
                            ('%s requires argument in Options(%s) '
                             'but hit "%s" in Usage') %
                            current, ins, _current)
                    else:
                        arg_token = Token([token.next()])

                if token.current() == '...':
                    arg_token.append(token.next())
        elif rest:
            if not (self.stdopt and self.attachopt):
                raise DocpieError(
                    "You can't write %s while it requires argument "
                    "and attachopt=False" % current)
            # -asth -> -a -sth
            token.insert(0, '-' + rest)
            prepended = True

        return arg_token, prepended
예제 #2
0
    def parse_other_element(self, current, token):
        atom_class, title = Atom.get_class(current)
        if atom_class is OptionsShortcut:
            return self.parse_options_shortcut(title, token)

        args = set([current])

        if atom_class is Option:
            for options in self.titled_opt_to_ins.values():
                if current in options:
                    ins_in_opt = options[current][0]

                    args.update(ins_in_opt.names)
                    if ins_in_opt.ref is not None:
                        ref_current = token.next()
                        ref_token = Token()
                        if ref_current in '([':
                            ref_token.append(ref_current)
                            ref_token.extend(
                                token.till_end_bracket(ref_current)
                            )
                            ref_token.append(
                                ')' if ref_current == '(' else ']'
                            )
                        else:
                            ref_token.extend(('(', ref_current, ')'))

                        ref_ins = self.parse_pattern(ref_token)

                        logger.debug(ins_in_opt.ref)
                        logger.debug(ref_ins[0])

                        if len(ref_ins) != 1:
                            raise DocpieError(
                                ('%s announced difference in '
                                 'Options(%s) and Usage(%s)') %
                                (current, ins_in_opt, ref_ins))

                        if ins_in_opt.ref != ref_ins[0]:
                            raise DocpieError(
                                ('%s announced difference in '
                                 'Options(%s) and Usage(%s)') %
                                (current, ins_in_opt, ref_ins))

                        ins = atom_class(*args, **{'ref': ins_in_opt.ref})
                        return (ins,)

        ins = atom_class(*args)

        repeat = token.check_ellipsis_and_drop()
        if repeat:
            ins = Required(ins, repeat=True)
        logger.debug('%s -> %s', current, ins)
        return (ins,)
예제 #3
0
    def get_short_option_with_angle_bracket(self, current, token):
        lt_index = current.find('<')
        prepended = False
        arg_token = None
        if self.stdopt and self.attachopt:
            # -a<sth> -> -a <sth>; -abc<sth> -> -a -bc<sth>
            if not self.attachvalue:
                raise DocpieError(
                    "You can't write %s while attachvalue=False" %
                    current)

            flag = current[:2]
            # -abc<sth>
            if lt_index > 2:
                token.insert(0, '-' + current[2:])
                prepended = True
            # -a<sth>
            else:
                arg_token = Token([current[lt_index:]])
                # rest = atom[lt_index:]
        # -a<sth> -> -a <sth>; -abc<sth> -> -abc <sth>
        else:
            flag = current[:lt_index]
            arg_token = Token([current[lt_index:]])

        return flag, arg_token, prepended
예제 #4
0
 def parse(self, text, name, options):
     self.options = options
     self.set_option_name_2_instance(options)
     if text is not None:
         self.parse_content(text)
     if self.formal_content is None:
         raise DocpieError('"Usage:" not found')
     self.parse_2_instance(name)
     self.fix_option_and_empty()
예제 #5
0
 def till_end_bracket(self, start):
     # start bracket should not in self
     end = self._brackets[start]
     count = dict.fromkeys(self._brackets, 0)
     count[start] = 1
     element = []
     while self:
         this = self.pop(0)
         for each_start, each_end in self._brackets.items():
             count[each_start] += (this == each_start)
             count[each_start] -= (this == each_end)
         if this == end and all(x == 0 for x in count.values()):
             if any(x < 0 for x in count.values()):
                 raise DocpieError("brackets not in pair")
             return element
         element.append(this)
     else:
         raise DocpieError("brackets not in pair")
예제 #6
0
    def find_options(self, title, title_opt_2_ins):
        formal_title = self.formal_title(title)
        if self.namedoptions:
            for exists_title, options in title_opt_2_ins.items():
                if (formal_title == self.formal_title(exists_title)):
                    logger.debug('find %s for %s', exists_title, title)
                    return options
            else:
                logger.debug('%s options not found in %s',
                             title, title_opt_2_ins)
                raise DocpieError('%s options not found' % title)

        return sum(title_opt_2_ins.values(), [])
예제 #7
0
    def parse_line_to_lis(self, line, name=None):
        if name is not None:
            _, find_name, line = line.partition(name)
            if not find_name:
                raise DocpieError(
                    '%s is not in usage pattern %s' % (name, _))

        # wrapped_space = self.wrap_symbol_re.sub(r' \1 ', line.strip())
        # logger.debug(wrapped_space)
        # result = [x for x in self.split_re.split(wrapped_space) if x]

        angle_bracket_re = self.angle_bracket_re
        wrap_symbol_re = self.wrap_symbol_re

        with warnings.catch_warnings():
            warnings.simplefilter('ignore')
            try:
                sep_by_angle = angle_bracket_re.split(line)
            except ValueError:
                sep_by_angle = [line]

        wrap_space = []
        for index, each_block in enumerate(sep_by_angle):
            if index % 2:
                wrap_space.append(each_block)
                continue

            if not each_block:
                continue

            warped_space = wrap_symbol_re.sub(r' \1 ', each_block)

            wrap_space.append(warped_space)

        wraped = ''.join(wrap_space)

        with warnings.catch_warnings():
            warnings.simplefilter('ignore')
            try:
                sep = self.split_re.split(wraped)
            except ValueError:
                sep = [wraped]

        result = list(filter(None, sep))

        # drop name
        if name is None:
            result.pop(0)

        return result
예제 #8
0
    def get_long_option_with_arg(self, current, token):
        flag, arg = current.split('=', 1)
        if Atom.get_class(flag)[0] is Option:
            if arg:
                arg_token = Token([arg])
            else:
                next_arg = token.next()
                if next_arg not in '([':
                    raise DocpieError('format error: %s' % current)
                tk = [next_arg]
                tk.extend(token.till_end_bracket(next_arg))
                tk.append(')' if next_arg == '(' else ']')
                arg_token = Token(tk)

            if token.current() == '...':
                arg_token.append(token.next())

            return flag, arg_token
예제 #9
0
    def _init(self):
        uparser = UsageParser(self.usage_name, self.case_sensitive,
                              self.stdopt, self.attachopt, self.attachvalue,
                              self.namedoptions)
        oparser = OptionParser(self.option_name, self.case_sensitive,
                               self.stdopt, self.attachopt, self.attachvalue,
                               self.namedoptions)

        uparser.parse_content(self.doc)
        self.usage_text = usage_text = uparser.raw_content
        # avoid usage contains "Options:" word
        if usage_text is None:
            assert self.usage_name.lower() not in self.doc.lower()
            raise DocpieError('usage title %r not found in doc' %
                              (self.usage_name, ))
        prefix, _, suffix = self.doc.partition(usage_text)

        oparser.parse(prefix + suffix)
        self.option_sections = oparser.raw_content
        self.options = oparser.instances

        uparser.parse(None, self.name, self.options)
        self.usages = uparser.instances

        self.opt_names_required_max_args = {}

        for opt_ins in uparser.all_options:
            if opt_ins.ref:
                # max_arg = max(opt_ins.arg_range())
                max_arg = max(opt_ins.ref.arg_range())
            else:
                max_arg = 0

            for each_name in opt_ins.names:
                self.opt_names_required_max_args[each_name] = max_arg

        self.opt_names = []
        for options in self.options.values():
            for each_option in options:
                self.opt_names.append(each_option[0].names)

        self.set_config(help=self.help,
                        version=self.version,
                        extra=dict(self.extra))
예제 #10
0
    def parse_option_with_arg(self, current, token):
        flag = None
        arg_token = None
        prepended = False

        # --all=<sth>... -> --all=<sth> ...
        # --all=(<sth> <else>)... -> --all= ( <sth> <else> ) ...
        if current.startswith('--') and '=' in current:
            flag, arg_token = self.get_long_option_with_arg(current, token)

        # -a<sth> -aSTH -asth, -a, -abc<sth>
        elif current.startswith('-') and not current.startswith('--'):
            flag, arg_token, prepended = \
                self.get_short_option_with_arg(current, token)

        if flag is not None:
            logger.debug('parsing flag %s, %s, %s', flag, arg_token, token)
            if arg_token is not None:
                ref_lis = self.parse_pattern(arg_token)
                ref = Required(*ref_lis).fix()
            else:
                ref = None
            ins = Option(flag, ref=ref)

            for opt_2_ins in self.titled_opt_to_ins.values():
                if flag in opt_2_ins:
                    opt_ins = opt_2_ins[flag][0]
                    ins.names.update(opt_ins.names)
                    # != won't work on pypy
                    if not (ins == opt_ins):
                        raise DocpieError(
                            '%s announces differently in '
                            'Options(%r) and Usage(%r)' %
                            (flag, opt_ins, ins))
                    break

            if token.current() == '...':
                ins = Required(ins, repeat=True)
                token.next()

            return (ins,)

        if prepended:
            token.pop(0)
예제 #11
0
    def parse_opt_str(self, opt):

        repeat = False

        # -sth=<goes> ON -> -sth, <goes>, ON
        opt_lis = self.opt_str_to_list(opt)
        logger.debug('%r -> %s' % (opt, opt_lis))

        first = opt_lis.pop(0)
        if not first.startswith('-'):
            raise DocpieError('option %s does not start with "-"' % first)

        # if self.stdopt:
        # -sth -> name=-s, value=th
        # else:
        # -sth -> name=-sth, value=''
        name, value = self.split_short_by_cfg(first)
        opt_ins = Option(name)
        if value == '...':
            repeat = True
            # -f... <sth>
            if opt_lis and not opt_lis[0].startswith('-'):
                raise DocpieError(
                    'option "%s" has argument following "..."', opt)
        elif value:
            args_ins = [Required(Argument(value))]
        else:
            args_ins = []

        if opt_lis and opt_lis[0] == '...':
            repeat = True
            opt_lis.pop(0)
            if opt_lis and not opt_lis[0].startswith('-'):
                raise DocpieError(
                    'option "%s" has argument following "..."', opt)

        args = []    # store the current args after option
        for each in opt_lis:
            if each.startswith('-'):    # alias
                name, value = self.split_short_by_cfg(each)
                opt_ins.names.add(name)
                if value:
                    args_ins.append(Required(Argument(value)))
                if args:    # trun it into instance
                    if args[0] == '...':
                        if len(args) != 1:
                            raise DocpieError(
                                'Error in %s: "..." followed by non option',
                                opt)
                        repeat = True
                    else:
                        this_arg = Required(
                                            *self.parse_pattern(Token(args))
                                           ).fix()
                        if this_arg is not None:
                            args_ins.append(this_arg)
                del args[:]
            else:
                args.append(each)
        else:
            if args:    # trun it into instance
                if args[0] == '...':
                    if len(args) != 1:
                        raise DocpieError(
                            'Error in %s: "..." followed by non option',
                            opt)
                    repeat = True
                else:
                    this_arg = Required(
                        *self.parse_pattern(Token(args))).fix()
                    if this_arg is not None:
                        args_ins.append(this_arg)

        # option without any args
        if not args_ins:
            return opt_ins, repeat

        # in Option, there should only have one arg list
        # e.g.: -f <file> --file=FILE -> -f/--file (<file>|FILE)
        # because the arg name will now be shown, it parsed as:
        # -f <file> --file=FILE -> -f/--file (<file>)
        current_ins = args_ins.pop(0)
        current_range = current_ins.arg_range()

        # avoid e.g.: -f <a> <b> --file <c>
        for other_ins in args_ins:
            this_range = other_ins.arg_range()
            if this_range != current_range:
                raise DocpieError("%s announced differently (%s, %s)" % (
                                  opt_ins, this_range, current_range))

        if len(current_range) > 1:
            logger.debug('too many possibilities: '
                         'option %s expect %s arguments',
                         name, '/'.join(map(str, current_range)))

        # TODO: check if current_ins contain Command(not allowed in fact)
        opt_ins.ref = current_ins
        return opt_ins, repeat