Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions lib/matplotlib/axes/_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import itertools
import logging
import math
import datetime
from numbers import Integral, Number, Real

import re
Expand Down Expand Up @@ -9057,6 +9058,19 @@ def violin(self, vpstats, positions=None, vert=None,
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]
Comment thread
timhoffm marked this conversation as resolved.
Outdated
Comment thread
timhoffm marked this conversation as resolved.
Outdated
if (isinstance(pos0, (datetime.datetime, datetime.date))
and not isinstance(width0, datetime.timedelta)):
raise TypeError(
"datetime/date 'position' values, require timedelta 'widths'")
Comment thread
hasanrashid marked this conversation as resolved.
Outdated
elif (isinstance(pos0, np.datetime64)
and not isinstance(width0, np.timedelta64)):
raise TypeError(
"np.datetime64 'position' values, require np.timedelta64 'widths'")
Comment thread
hasanrashid marked this conversation as resolved.
Outdated
# Validate side
_api.check_in_list(["both", "low", "high"], side=side)

Expand Down
75 changes: 75 additions & 0 deletions lib/matplotlib/tests/test_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4121,6 +4121,81 @@ def test_violinplot_sides():
showextrema=True, showmedians=True, side=side)


def violin_plot_stats():
datetimes = [
datetime.datetime(2023, 2, 10),
datetime.datetime(2023, 5, 18),
datetime.datetime(2023, 6, 6)
]
return [{
'coords': datetimes,
'vals': [1.2, 2.8, 1.5],
'mean': 1.84,
'median': 1.5,
'min': 1.2,
'max': 2.8,
'quantiles': [1.2, 1.5, 2.8]
}, {
'coords': datetimes,
'vals': [0.8, 1.1, 0.9],
'mean': 0.94,
'median': 0.9,
'min': 0.8,
'max': 1.1,
'quantiles': [0.8, 0.9, 1.1]
}]
Comment thread
timhoffm marked this conversation as resolved.


def test_datetime_positions_with_datetime64():
"""Test that datetime positions with float widths raise TypeError."""
fig, ax = plt.subplots()
positions = [np.datetime64('2020-01-01'), np.datetime64('2021-01-01')]
widths = [0.5, 1.0]
with pytest.raises(TypeError,
match="np.datetime64 'position' values, require np.timedelta64 'widths'"):
Comment thread
timhoffm marked this conversation as resolved.
Outdated
ax.violin(violin_plot_stats(), positions=positions, widths=widths)


def test_datetime_positions_with_float_widths_raises():
"""Test that datetime positions with float widths raise TypeError."""
fig, ax = plt.subplots()
positions = [datetime.datetime(2020, 1, 1), datetime.datetime(2021, 1, 1)]
widths = [0.5, 1.0]
with pytest.raises(TypeError,
match="datetime/date 'position' values, require timedelta 'widths'"):
Comment thread
timhoffm marked this conversation as resolved.
Outdated
ax.violin(violin_plot_stats(), positions=positions, widths=widths)


def test_datetime_positions_with_scalar_float_width_raises():
"""Test that datetime positions with scalar float width raise TypeError."""
fig, ax = plt.subplots()
positions = [datetime.datetime(2020, 1, 1), datetime.datetime(2021, 1, 1)]
widths = 0.75
with pytest.raises(TypeError,
match="datetime/date 'position' values, require timedelta 'widths'"):
Comment thread
timhoffm marked this conversation as resolved.
Outdated
ax.violin(violin_plot_stats(), positions=positions, widths=widths)


def test_numeric_positions_with_float_widths_ok():
"""Test that numeric positions with float widths work."""
fig, ax = plt.subplots()
positions = [1.0, 2.0]
widths = [0.5, 1.0]
ax.violin(violin_plot_stats(), positions=positions, widths=widths)
Comment thread
timhoffm marked this conversation as resolved.


def test_mixed_positions_datetime_and_numeric_behaves():
Comment thread
hasanrashid marked this conversation as resolved.
Outdated
"""Test that mixed datetime and numeric positions
with float widths raise TypeError.
"""
fig, ax = plt.subplots()
positions = [datetime.datetime(2020, 1, 1), 2.0]
widths = [0.5, 1.0]
with pytest.raises(TypeError,
match="datetime/date 'position' values, require timedelta 'widths'"):

Copilot AI Dec 15, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The match string for pytest.raises is split across two lines without proper line continuation formatting. This could lead to issues if formatting changes or the line is refactored. Consider using proper line continuation with parentheses or joining into a single line for better maintainability and consistency with pytest best practices.

Copilot uses AI. Check for mistakes.
ax.violin(violin_plot_stats(), positions=positions, widths=widths)
Comment thread
timhoffm marked this conversation as resolved.


def test_violinplot_bad_positions():
ax = plt.axes()
# First 9 digits of frac(sqrt(47))
Expand Down
Loading