Skip to content

API: IncompatibleFrequency subclass TypeError #61875

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 18, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions ci/code_checks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then
-i "pandas.Series.dt PR01" `# Accessors are implemented as classes, but we do not document the Parameters section` \
-i "pandas.Period.freq GL08" \
-i "pandas.Period.ordinal GL08" \
-i "pandas.errors.IncompatibleFrequency SA01,SS06,EX01" \
-i "pandas.core.groupby.DataFrameGroupBy.plot PR02" \
-i "pandas.core.groupby.SeriesGroupBy.plot PR02" \
-i "pandas.core.resample.Resampler.quantile PR01,PR07" \
Expand Down
1 change: 1 addition & 0 deletions doc/source/reference/testing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Exceptions and warnings
errors.DuplicateLabelError
errors.EmptyDataError
errors.IncompatibilityWarning
errors.IncompatibleFrequency
errors.IndexingError
errors.InvalidColumnName
errors.InvalidComparison
Expand Down
1 change: 1 addition & 0 deletions doc/source/whatsnew/v3.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ Other API changes
- Index set operations (like union or intersection) will now ignore the dtype of
an empty ``RangeIndex`` or empty ``Index`` with object dtype when determining
the dtype of the resulting Index (:issue:`60797`)
- :class:`IncompatibleFrequency` now subclasses ``TypeError`` instead of ``ValueError``. As a result, joins with mismatched frequencies now cast to object like other non-comparable joins, and arithmetic with indexes with mismatched frequencies align (:issue:`55782`)
- Comparison operations between :class:`Index` and :class:`Series` now consistently return :class:`Series` regardless of which object is on the left or right (:issue:`36759`)
- Numpy functions like ``np.isinf`` that return a bool dtype when called on a :class:`Index` object now return a bool-dtype :class:`Index` instead of ``np.ndarray`` (:issue:`52676`)

Expand Down
2 changes: 1 addition & 1 deletion pandas/_libs/tslibs/period.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ from pandas._typing import (
INVALID_FREQ_ERR_MSG: str
DIFFERENT_FREQ: str

class IncompatibleFrequency(ValueError): ...
class IncompatibleFrequency(TypeError): ...

def periodarr_to_dt64arr(
periodarr: npt.NDArray[np.int64], # const int64_t[:]
Expand Down
6 changes: 5 additions & 1 deletion pandas/_libs/tslibs/period.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1625,7 +1625,11 @@ DIFFERENT_FREQ = ("Input has different freq={other_freq} "
"from {cls}(freq={own_freq})")


class IncompatibleFrequency(ValueError):
class IncompatibleFrequency(TypeError):
"""
Raised when trying to compare or operate between Periods with different
frequencies.
"""
pass


Expand Down
4 changes: 2 additions & 2 deletions pandas/core/arrays/datetimelike.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ def _validate_comparison_value(self, other):
other = self._scalar_type(other)
try:
self._check_compatible_with(other)
except (TypeError, IncompatibleFrequency) as err:
except TypeError as err:
# e.g. tzawareness mismatch
raise InvalidComparison(other) from err

Expand All @@ -558,7 +558,7 @@ def _validate_comparison_value(self, other):
try:
other = self._validate_listlike(other, allow_object=True)
self._check_compatible_with(other)
except (TypeError, IncompatibleFrequency) as err:
except TypeError as err:
if is_object_dtype(getattr(other, "dtype", None)):
# We will have to operate element-wise
pass
Expand Down
3 changes: 1 addition & 2 deletions pandas/core/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
no_default,
)
from pandas._libs.tslibs import (
IncompatibleFrequency,
OutOfBoundsDatetime,
Timestamp,
tz_compare,
Expand Down Expand Up @@ -3139,7 +3138,7 @@ def _union(self, other: Index, sort: bool | None):
# test_union_same_value_duplicated_in_both fails)
try:
return self._outer_indexer(other)[0]
except (TypeError, IncompatibleFrequency):
except TypeError:
# incomparable objects; should only be for object dtype
value_list = list(lvals)

Expand Down
2 changes: 2 additions & 0 deletions pandas/errors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from pandas._config.config import OptionError

from pandas._libs.tslibs import (
IncompatibleFrequency,
OutOfBoundsDatetime,
OutOfBoundsTimedelta,
)
Expand Down Expand Up @@ -917,6 +918,7 @@ class InvalidComparison(Exception):
"DuplicateLabelError",
"EmptyDataError",
"IncompatibilityWarning",
"IncompatibleFrequency",
"IndexingError",
"IntCastingNaNError",
"InvalidColumnName",
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/indexes/period/test_indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ def test_get_indexer2(self):
)

msg = "Input has different freq=None from PeriodArray\\(freq=h\\)"
with pytest.raises(ValueError, match=msg):
with pytest.raises(libperiod.IncompatibleFrequency, match=msg):
idx.get_indexer(target, "nearest", tolerance="1 minute")

tm.assert_numpy_array_equal(
Expand Down
10 changes: 4 additions & 6 deletions pandas/tests/indexes/period/test_join.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import numpy as np
import pytest

from pandas._libs.tslibs import IncompatibleFrequency

from pandas import (
DataFrame,
Expand Down Expand Up @@ -51,8 +48,9 @@ def test_join_does_not_recur(self):
tm.assert_index_equal(res, expected)

def test_join_mismatched_freq_raises(self):
# pre-GH#55782 this raises IncompatibleFrequency
index = period_range("1/1/2000", "1/20/2000", freq="D")
index3 = period_range("1/1/2000", "1/20/2000", freq="2D")
msg = r".*Input has different freq=2D from Period\(freq=D\)"
with pytest.raises(IncompatibleFrequency, match=msg):
index.join(index3)
result = index.join(index3)
expected = index.astype(object).join(index3.astype(object))
tm.assert_index_equal(result, expected)
4 changes: 3 additions & 1 deletion pandas/tests/indexes/period/test_period.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import numpy as np
import pytest

from pandas.errors import IncompatibleFrequency

from pandas import (
Index,
NaT,
Expand Down Expand Up @@ -198,7 +200,7 @@ def test_maybe_convert_timedelta():

offset = offsets.BusinessDay()
msg = r"Input has different freq=B from PeriodIndex\(freq=D\)"
with pytest.raises(ValueError, match=msg):
with pytest.raises(IncompatibleFrequency, match=msg):
pi._maybe_convert_timedelta(offset)


Expand Down
5 changes: 0 additions & 5 deletions pandas/tests/series/test_arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import pytest

from pandas._libs import lib
from pandas._libs.tslibs import IncompatibleFrequency

import pandas as pd
from pandas import (
Expand Down Expand Up @@ -172,10 +171,6 @@ def test_add_series_with_period_index(self):
result = ts + _permute(ts[::2])
tm.assert_series_equal(result, expected)

msg = "Input has different freq=D from Period\\(freq=Y-DEC\\)"
with pytest.raises(IncompatibleFrequency, match=msg):
ts + ts.asfreq("D", how="end")

@pytest.mark.parametrize(
"target_add,input_value,expected_value",
[
Expand Down
Loading