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
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,)
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
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()
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")
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(), [])
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
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
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))
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)
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