def _parse_flagfile(self, cmd_flag_map, path_or_file, res_map=None): ret = res_map if res_map is not None else OrderedDict() if callable(getattr(path_or_file, 'read', None)): # enable StringIO and custom flagfile opening f_name = getattr(path_or_file, 'name', None) path = os.path.abspath(f_name) if f_name else repr(path_or_file) ff_text = path_or_file.read() else: path = os.path.abspath(path_or_file) try: with codecs.open(path_or_file, 'r', 'utf-8') as f: ff_text = f.read() except (UnicodeError, EnvironmentError) as ee: raise ArgumentParseError( 'failed to load flagfile "%s", got: %r' % (path, ee)) if path in res_map: # we've already seen this file return res_map ret[path] = cur_file_res = OMD() lines = ff_text.splitlines() for lineno, line in enumerate(lines, 1): try: args = shlex.split(line, comments=True) if not args: continue # comment or empty line flag, value, leftover_args = self._parse_single_flag( cmd_flag_map, args) if leftover_args: raise ArgumentParseError( 'excessive flags or arguments for flag "%s",' ' expected one flag per line' % flag.name) cur_file_res.add(flag.name, value) if flag is self.flagfile_flag: self._parse_flagfile(cmd_flag_map, value, res_map=ret) except FaceException as fe: fe.args = (fe.args[0] + ' (on line %s of flagfile "%s")' % (lineno, path), ) raise return ret
def parse(self, argv): """This method takes a list of strings and converts them into a validated :class:`CommandParseResult` according to the flags, subparsers, and other options configured. Args: argv (list): A required list of strings. Pass ``None`` to use ``sys.argv``. This method may raise ArgumentParseError (or one of its subtypes) if the list of strings fails to parse. .. note:: The *argv* parameter does not automatically default to using ``sys.argv`` because it's best practice for implementing codebases to perform that sort of defaulting in their ``main()``, which should accept an ``argv=None`` parameter. This simple step ensures that the Python CLI application has some sort of programmatic interface that doesn't require subprocessing. See here for an example. """ if argv is None: argv = sys.argv if not argv: raise ArgumentParseError( 'expected non-empty sequence of arguments, not: %r' % (argv, )) flag_map = None # first snip off the first argument, the command itself cmd_name, args = argv[0], list(argv)[1:] # we record our progress as we parse to provide the most # up-to-date info possible to the error and help handlers cpr = CommandParseResult(cmd_name, parser=self, argv=argv) try: # then figure out the subcommand path subcmds, args = self._parse_subcmds(args) cpr.subcmds = tuple(subcmds) prs = self.subprs_map[tuple(subcmds)] if subcmds else self # then look up the subcommand's supported flags # NOTE: get_flag_map() is used so that inheritors, like Command, # can filter by actually-used arguments, not just # available arguments. cmd_flag_map = self.get_flag_map(path=tuple(subcmds)) # parse supported flags and validate their arguments flag_map, flagfile_map, posargs = self._parse_flags( cmd_flag_map, args) cpr.flags = OrderedDict(flag_map) cpr.posargs = tuple(posargs) # take care of dupes and check required flags resolved_flag_map = self._resolve_flags(cmd_flag_map, flag_map, flagfile_map) cpr.flags = OrderedDict(resolved_flag_map) # separate out any trailing arguments from normal positional arguments post_posargs = None # TODO: default to empty list? parsed_post_posargs = None if '--' in posargs: posargs, post_posargs = split(posargs, '--', 1) cpr.posargs, cpr.post_posargs = posargs, post_posargs parsed_post_posargs = prs.post_posargs.parse(post_posargs) cpr.post_posargs = tuple(parsed_post_posargs) parsed_posargs = prs.posargs.parse(posargs) cpr.posargs = tuple(parsed_posargs) except ArgumentParseError as ape: ape.prs_res = cpr raise return cpr
def parse(self, text): choice = self.parse_as(text) if choice not in self.choices: raise ArgumentParseError('expected one of %r, not: %r' % (self.choices, text)) return choice