Beispiel #1
0
    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
Beispiel #2
0
    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
Beispiel #3
0
 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