Repository: matplotlib/matplotlib
File: lib/matplotlib/axes/_axes.py (Python)

Code before:
        if vert is not None:
            _api.warn_deprecated(
                "3.11",
                name="vert: bool",
                alternative="orientation: {'vertical', 'horizontal'}",
            )
            orientation = 'vertical' if vert else 'horizontal'
        _api.check_in_list(['horizontal', 'vertical'], orientation=orientation)

        # Validate positions
        if positions is None:
            positions = range(1, N + 1)
        elif len(positions) != N:
            raise ValueError(datashape_message.format("positions"))

        # Validate widths
        if np.isscalar(widths):
            widths = [widths] * N
        elif len(widths) != N:
            raise ValueError(datashape_message.format("widths"))

        # For usability / better error message:
        # Validate that datetime-like positions have timedelta-like widths.
        # Checking only the first element is good enough for standard misuse cases
        pos0 = positions[0]
        width0 = widths if np.isscalar(widths) else widths[0]
        if (isinstance(pos0, (datetime.datetime, datetime.date))
                and not isinstance(width0, datetime.timedelta)):
            raise TypeError(
                "datetime/date 'position' values, require timedelta 'widths'")
        elif (isinstance(pos0, np.datetime64)
                and not isinstance(width0, np.timedelta64)):
            raise TypeError(
                "np.datetime64 'position' values, require np.timedelta64 'widths'")
        # Validate side
        _api.check_in_list(["both", "low", "high"], side=side)

        # Calculate ranges for statistics lines (shape (2, N)).
        line_ends = [[-0.25 if side in ['both', 'low'] else 0],
                     [0.25 if side in ['both', 'high'] else 0]] \
                          * np.array(widths) + positions

        # Make a cycle of color to iterate through, using 'none' as fallback
        def cycle_color(color, alpha=None):
            rgba = mcolors.to_rgba_array(color, alpha=alpha)
            color_cycler = itertools.chain(itertools.cycle(rgba),
                                           itertools.repeat('none'))
            color_list = []
            for _ in range(N):
                color_list.append(next(color_cycler))
            return color_list

Review comment (suggestion):
Accessing `positions[0]` without checking if positions is non-empty could raise an IndexError. While the code validates that `len(positions) == N` on line 9052, if `N` (which equals `len(vpstats)`) is 0, positions could be an empty list. Consider adding a check for empty positions/vpstats or documenting that vpstats must be non-empty.

Code after:
        if vert is not None:
            _api.warn_deprecated(
                "3.11",
                name="vert: bool",
                alternative="orientation: {'vertical', 'horizontal'}",
            )
            orientation = 'vertical' if vert else 'horizontal'
        _api.check_in_list(['horizontal', 'vertical'], orientation=orientation)

        # Validate positions
        if positions is None:
            positions = range(1, N + 1)
        elif len(positions) != N:
            raise ValueError(datashape_message.format("positions"))
        # Validate widths
        if np.isscalar(widths):
            widths = [widths] * N
        elif len(widths) != N:
            raise ValueError(datashape_message.format("widths"))

        # For usability / better error message:
        # Validate that datetime-like positions have timedelta-like widths.
        # Checking only the first element is good enough for standard misuse cases
        if N > 0:  # No need to validate if there is no data
            pos0 = positions[0]
            width0 = widths[0]
            if (isinstance(pos0, (datetime.datetime, datetime.date))
                and not isinstance(width0, datetime.timedelta)):
                raise TypeError(
                    "datetime/date 'position' values require timedelta 'widths'. "
                    "For example, use positions=[datetime.date(2024, 1, 1)] "
                    "and widths=[datetime.timedelta(days=1)].")
            elif (isinstance(pos0, np.datetime64)
                and not isinstance(width0, np.timedelta64)):
                raise TypeError(
                    "np.datetime64 'position' values require np.timedelta64 'widths'")
        _api.check_in_list(["both", "low", "high"], side=side)

        # Calculate ranges for statistics lines (shape (2, N)).
        line_ends = [[-0.25 if side in ['both', 'low'] else 0],
                     [0.25 if side in ['both', 'high'] else 0]] \
                          * np.array(widths) + positions

        # Make a cycle of color to iterate through, using 'none' as fallback
        def cycle_color(color, alpha=None):
            rgba = mcolors.to_rgba_array(color, alpha=alpha)
            color_cycler = itertools.chain(itertools.cycle(rgba),
                                           itertools.repeat('none'))
            color_list = []
            for _ in range(N):
                color_list.append(next(color_cycler))