Helper function to do the main argument parsing.

    This function goes through args and does the bulk of the flag parsing.
    It will find the corresponding flag in our flag dictionary, and call its
    .parse() method on the flag value.

    Args:
      args: [str], a list of strings with the arguments to parse.
      known_only: bool, if True, parse and remove known flags; return the rest
           untouched. Unknown flags specified by --undefok are not returned.

    Returns:
      A tuple with the following:
          unknown_flags: List of (flag name, arg) for flags we don't know about.
          unparsed_args: List of arguments we did not parse.

    Raises:
       Error: Raised on any parsing error.
       ValueError: Raised on flag value parsing error.

def _parse_args(self, args, known_only):
    """Helper function to do the main argument parsing.

    This function goes through args and does the bulk of the flag parsing.
    It will find the corresponding flag in our flag dictionary, and call its
    .parse() method on the flag value.

    Args:
      args: [str], a list of strings with the arguments to parse.
      known_only: bool, if True, parse and remove known flags; return the rest
           untouched. Unknown flags specified by --undefok are not returned.

    Returns:
      A tuple with the following:
          unknown_flags: List of (flag name, arg) for flags we don't know about.
          unparsed_args: List of arguments we did not parse.

    Raises:
       Error: Raised on any parsing error.
       ValueError: Raised on flag value parsing error.
    """
    unparsed_names_and_args = []  # A list of (flag name or None, arg).
    undefok = set()
    retired_flag_func = self.__dict__['__is_retired_flag_func']

    flag_dict = self._flags()
    args = iter(args)
    for arg in args:
      value = None

      def get_value():
        # pylint: disable=cell-var-from-loop
        try:
          return next(args) if value is None else value
        except StopIteration:
          raise _exceptions.Error('Missing value for flag ' + arg)  # pylint: disable=undefined-loop-variable

      if not arg.startswith('-'):
        # A non-argument: default is break, GNU is skip.
        unparsed_names_and_args.append((None, arg))
        if self.is_gnu_getopt():
          continue
        else:
          break

      if arg == '--':
        if known_only:
          unparsed_names_and_args.append((None, arg))
        break

      # At this point, arg must start with '-'.
      if arg.startswith('--'):
        arg_without_dashes = arg[2:]
      else:
        arg_without_dashes = arg[1:]

      if '=' in arg_without_dashes:
        name, value = arg_without_dashes.split('=', 1)
      else:
        name, value = arg_without_dashes, None

      if not name:
        # The argument is all dashes (including one dash).
        unparsed_names_and_args.append((None, arg))
        if self.is_gnu_getopt():
          continue
        else:
          break

      # --undefok is a special case.
      if name == 'undefok':
        value = get_value()
        undefok.update(v.strip() for v in value.split(','))
        undefok.update('no' + v.strip() for v in value.split(','))
        continue

      flag = flag_dict.get(name)
      if flag:
        if flag.boolean and value is None:
          value = 'true'
        else:
          value = get_value()
      elif name.startswith('no') and len(name) > 2:
        # Boolean flags can take the form of --noflag, with no value.
        noflag = flag_dict.get(name[2:])
        if noflag and noflag.boolean:
          if value is not None:
            raise ValueError(arg + ' does not take an argument')
          flag = noflag
          value = 'false'

      if retired_flag_func and not flag:
        is_retired, is_bool = retired_flag_func(name)

        # If we didn't recognize that flag, but it starts with
        # "no" then maybe it was a boolean flag specified in the
        # --nofoo form.
        if not is_retired and name.startswith('no'):
          is_retired, is_bool = retired_flag_func(name[2:])
          is_retired = is_retired and is_bool

        if is_retired:
          if not is_bool and value is None:
            # This happens when a non-bool retired flag is specified
            # in format of "--flag value".
            get_value()
          logging.error('Flag "%s" is retired and should no longer '
                        'be specified. See go/totw/90.', name)
          continue

      if flag:
        flag.parse(value)
        flag.using_default_value = False
      else:
        unparsed_names_and_args.append((name, arg))

    unknown_flags = []
    unparsed_args = []
    for name, arg in unparsed_names_and_args:
      if name is None:
        # Positional arguments.
        unparsed_args.append(arg)
      elif name in undefok:
        # Remove undefok flags.
        continue
      else:
        # This is an unknown flag.
        if known_only:
          unparsed_args.append(arg)
        else:
          unknown_flags.append((name, arg))

    unparsed_args.extend(list(args))
    return unknown_flags, unparsed_args