Repository: pandas-dev/pandas
File: pandas/io/parsers/python_parser.py (Python)

Code before:
        # Check that there are no rows with too many
        # elements in their row (rows with too few
        # elements are padded with NaN).
        if max_len > col_len and self.index_col is not False and self.usecols is None:
            footers = self.skipfooter if self.skipfooter else 0
            bad_lines = []

            iter_content = enumerate(content)
            content_len = len(content)
            content = []

            for i, _content in iter_content:
                actual_len = len(_content)
                if actual_len > col_len:
                    if callable(self.on_bad_lines):
                        new_l = self.on_bad_lines(_content)
                        if new_l is not None:
                            # Truncate extra elements and warn.
                            new_l = cast(list[Scalar], new_l)
                            if len(new_l) > col_len:
                                warnings.warn(
                                    "Length of header or names does not match length "
                                    "of data. This leads "
                                    "to a loss of data with index_col=False.",
                                    ParserWarning,
                                    stacklevel=find_stack_level(),
                                )
                                new_l = new_l[:col_len]
                            content.append(new_l)
                    elif self.on_bad_lines == self.BadLineHandleMethod.ERROR:
                        row_num = self.pos - (content_len - i + footers)
                        bad_lines.append((row_num, actual_len))
                        break
                    elif self.on_bad_lines in (
                        self.BadLineHandleMethod.WARN,
                        self.BadLineHandleMethod.SKIP,
                    ):
                        row_num = self.pos - (content_len - i + footers)
                        bad_lines.append((row_num, actual_len))
                        # For WARN and SKIP, don't append the bad content
                    else:
                        content.append(_content)
                else:
                    content.append(_content)

            for row_num, actual_len in bad_lines:
                msg = (
                    f"Expected {col_len} fields in line {row_num + 1}, saw {actual_len}"
                )
                if (
                    self.delimiter

Review comment (suggestion):
I'm not a fan of warning here in addition to `alert_malformed`. Can you use whether `self.on_bad_lines` is callable to determine a better warning in this case (e.g. "Length from `on_bad_lines` callable does not match..."), and then raise the appropriate warning in `_alert_malformed`.

Code after:
        max_len = max(len(row) for row in content)

        # Check that there are no rows with too many
        # elements in their row (rows with too few
        # elements are padded with NaN).
        if max_len > col_len and self.index_col is not False and self.usecols is None:
            footers = self.skipfooter if self.skipfooter else 0
            bad_lines = []

            iter_content = enumerate(content)
            content_len = len(content)
            content = []

            for i, _content in iter_content:
                actual_len = len(_content)
                if actual_len > col_len:
                    if callable(self.on_bad_lines):
                        new_l = self.on_bad_lines(_content)
                        if new_l is not None:
                            new_l = cast(list[Scalar], new_l)
                            if len(new_l) > col_len:
                                row_num = self.pos - (content_len - i + footers)
                                bad_lines.append((row_num, len(new_l), "callable"))
                                new_l = new_l[:col_len]
                            content.append(new_l)

                    elif self.on_bad_lines in (
                        self.BadLineHandleMethod.ERROR,
                        self.BadLineHandleMethod.WARN,
                    ):
                        row_num = self.pos - (content_len - i + footers)
                        bad_lines.append((row_num, actual_len, "normal"))
                        if self.on_bad_lines == self.BadLineHandleMethod.ERROR:
                            break
                else:
                    content.append(_content)

            for row_num, actual_len, source in bad_lines:
                msg = (
                    f"Expected {col_len} fields in line {row_num + 1}, saw {actual_len}"
                )
                if source == "callable":
                    msg += " from bad_lines callable"
                elif (
                    self.delimiter
                    and len(self.delimiter) > 1
                    and self.quoting != csv.QUOTE_NONE
                ):
                    # see gh-13374
                    reason = (
                        "Error could possibly be due to quotes being "