def parse(
        self,
        datetime_string: str,
        fmt: Union[List[str], str],
        normalize_whitespace: bool = False,
    ) -> datetime:
        """
        Parses a datetime string using a specified format.

        :param datetime_string: The datetime string to parse.
        :param fmt: The format string or list of format strings to use for parsing.
        :param normalize_whitespace: Whether to normalize whitespace in the datetime string (default is False).
        :type datetime_string: str
        :type fmt: Union[List[str], str]
        :type normalize_whitespace: bool
        :returns: The parsed datetime object.
        :rtype: datetime
        :raises ParserMatchError: If the datetime string does not match the specified format.

        Usage::

        >>> import arrow.parser
        >>> arrow.parser.DateTimeParser().parse('2021-10-12 14:30:00', 'YYYY-MM-DD HH:mm:ss')
        datetime.datetime(2021, 10, 12, 14, 30)


        """
        if normalize_whitespace:
            datetime_string = re.sub(r"\s+", " ", datetime_string)

        if isinstance(fmt, list):
            return self._parse_multiformat(datetime_string, fmt)

        try:
            fmt_tokens: List[_FORMAT_TYPE]
            fmt_pattern_re: Pattern[str]
            fmt_tokens, fmt_pattern_re = self._generate_pattern_re(fmt)
        except re.error as e:
            raise ParserMatchError(
                f"Failed to generate regular expression pattern: {e}."
            )

        match = fmt_pattern_re.search(datetime_string)

        if match is None:
            raise ParserMatchError(
                f"Failed to match {fmt!r} when parsing {datetime_string!r}."
            )

        parts: _Parts = {}
        for token in fmt_tokens:
            value: Union[Tuple[str, str, str], str]
            if token == "Do":
                value = match.group("value")
            elif token == "W":
                value = (match.group("year"), match.group("week"), match.group("day"))
            else:
                value = match.group(token)

            if value is None:
                raise ParserMatchError(
                    f"Unable to find a match group for the specified token {token!r}."
                )

            self._parse_token(token, value, parts)  # type: ignore[arg-type]

        return self._build_datetime(parts)

        Parses a datetime string using a specified format.

        :param datetime_string: The datetime string to parse.
        :param fmt: The format string or list of format strings to use for parsing.
        :param normalize_whitespace: Whether to normalize whitespace in the datetime string (default is False).
        :type datetime_string: str
        :type fmt: Union[List[str], str]
        :type normalize_whitespace: bool
        :returns: The parsed datetime object.
        :rtype: datetime
        :raises ParserMatchError: If the datetime string does not match the specified format.

        Usage::

        >>> import arrow.parser
        >>> arrow.parser.DateTimeParser().parse('2021-10-12 14:30:00', 'YYYY-MM-DD HH:mm:ss')
        datetime.datetime(2021, 10, 12, 14, 30)


        
arrow/parser.py