Skip to content

Commit 97e6261

Browse files
jbrockmendelPingviinituutti
authored andcommitted
CLN: standardize different freq message (pandas-dev#24283)
* standardize different freq message * implement raise_on_incompatible * fixup missing import * freqstr cases * update tested messages
1 parent 3109ced commit 97e6261

File tree

6 files changed

+81
-42
lines changed

6 files changed

+81
-42
lines changed

pandas/_libs/tslibs/period.pyx

+14-7
Original file line numberDiff line numberDiff line change
@@ -1455,7 +1455,9 @@ def extract_ordinals(object[:] values, freq):
14551455
ordinals[i] = p.ordinal
14561456

14571457
if p.freqstr != freqstr:
1458-
msg = DIFFERENT_FREQ_INDEX.format(freqstr, p.freqstr)
1458+
msg = DIFFERENT_FREQ.format(cls="PeriodIndex",
1459+
own_freq=freqstr,
1460+
other_freq=p.freqstr)
14591461
raise IncompatibleFrequency(msg)
14601462

14611463
except AttributeError:
@@ -1545,9 +1547,8 @@ cdef int64_t[:] localize_dt64arr_to_period(int64_t[:] stamps,
15451547
return result
15461548

15471549

1548-
_DIFFERENT_FREQ = "Input has different freq={1} from Period(freq={0})"
1549-
DIFFERENT_FREQ_INDEX = ("Input has different freq={1} "
1550-
"from PeriodIndex(freq={0})")
1550+
DIFFERENT_FREQ = ("Input has different freq={other_freq} "
1551+
"from {cls}(freq={own_freq})")
15511552

15521553

15531554
class IncompatibleFrequency(ValueError):
@@ -1596,7 +1597,9 @@ cdef class _Period(object):
15961597
def __richcmp__(self, other, op):
15971598
if is_period_object(other):
15981599
if other.freq != self.freq:
1599-
msg = _DIFFERENT_FREQ.format(self.freqstr, other.freqstr)
1600+
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
1601+
own_freq=self.freqstr,
1602+
other_freq=other.freqstr)
16001603
raise IncompatibleFrequency(msg)
16011604
return PyObject_RichCompareBool(self.ordinal, other.ordinal, op)
16021605
elif other is NaT:
@@ -1637,7 +1640,9 @@ cdef class _Period(object):
16371640
if base == self.freq.rule_code:
16381641
ordinal = self.ordinal + other.n
16391642
return Period(ordinal=ordinal, freq=self.freq)
1640-
msg = _DIFFERENT_FREQ.format(self.freqstr, other.freqstr)
1643+
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
1644+
own_freq=self.freqstr,
1645+
other_freq=other.freqstr)
16411646
raise IncompatibleFrequency(msg)
16421647
else: # pragma no cover
16431648
return NotImplemented
@@ -1684,7 +1689,9 @@ cdef class _Period(object):
16841689
return Period(ordinal=ordinal, freq=self.freq)
16851690
elif is_period_object(other):
16861691
if other.freq != self.freq:
1687-
msg = _DIFFERENT_FREQ.format(self.freqstr, other.freqstr)
1692+
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
1693+
own_freq=self.freqstr,
1694+
other_freq=other.freqstr)
16881695
raise IncompatibleFrequency(msg)
16891696
# GH 23915 - mul by base freq since __add__ is agnostic of n
16901697
return (self.ordinal - other.ordinal) * self.freq.base

pandas/core/arrays/datetimelike.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from pandas._libs import NaT, algos, iNaT, lib
99
from pandas._libs.tslibs.period import (
10-
DIFFERENT_FREQ_INDEX, IncompatibleFrequency, Period)
10+
DIFFERENT_FREQ, IncompatibleFrequency, Period)
1111
from pandas._libs.tslibs.timedeltas import Timedelta, delta_to_nanoseconds
1212
from pandas._libs.tslibs.timestamps import (
1313
RoundTo, maybe_integer_op_deprecated, round_nsint64)
@@ -736,7 +736,9 @@ def _sub_period_array(self, other):
736736
raise ValueError("cannot subtract arrays/indices of "
737737
"unequal length")
738738
if self.freq != other.freq:
739-
msg = DIFFERENT_FREQ_INDEX.format(self.freqstr, other.freqstr)
739+
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
740+
own_freq=self.freqstr,
741+
other_freq=other.freqstr)
740742
raise IncompatibleFrequency(msg)
741743

742744
new_values = checked_add_with_arr(self.asi8, -other.asi8,

pandas/core/arrays/period.py

+39-20
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from pandas._libs.tslibs import NaT, iNaT, period as libperiod
88
from pandas._libs.tslibs.fields import isleapyear_arr
99
from pandas._libs.tslibs.period import (
10-
DIFFERENT_FREQ_INDEX, IncompatibleFrequency, Period, get_period_field_arr,
10+
DIFFERENT_FREQ, IncompatibleFrequency, Period, get_period_field_arr,
1111
period_asfreq_arr)
1212
from pandas._libs.tslibs.timedeltas import Timedelta, delta_to_nanoseconds
1313
import pandas.compat as compat
@@ -30,7 +30,7 @@
3030
from pandas.core.missing import backfill_1d, pad_1d
3131

3232
from pandas.tseries import frequencies
33-
from pandas.tseries.offsets import Tick
33+
from pandas.tseries.offsets import DateOffset, Tick, _delta_to_tick
3434

3535

3636
def _field_accessor(name, alias, docstring=None):
@@ -166,8 +166,9 @@ def __init__(self, values, freq=None, dtype=None, copy=False):
166166

167167
if isinstance(values, type(self)):
168168
if freq is not None and freq != values.freq:
169-
msg = DIFFERENT_FREQ_INDEX.format(values.freq.freqstr,
170-
freq.freqstr)
169+
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
170+
own_freq=values.freq.freqstr,
171+
other_freq=freq.freqstr)
171172
raise IncompatibleFrequency(msg)
172173
values, freq = values._data, values.freq
173174

@@ -239,8 +240,7 @@ def _generate_range(cls, start, end, periods, freq, fields):
239240

240241
def _check_compatible_with(self, other):
241242
if self.freqstr != other.freqstr:
242-
msg = DIFFERENT_FREQ_INDEX.format(self.freqstr, other.freqstr)
243-
raise IncompatibleFrequency(msg)
243+
_raise_on_incompatible(self, other)
244244

245245
# --------------------------------------------------------------------
246246
# Data / Attributes
@@ -372,15 +372,13 @@ def __setitem__(
372372
value = period_array(value)
373373

374374
if self.freqstr != value.freqstr:
375-
msg = DIFFERENT_FREQ_INDEX.format(self.freqstr, value.freqstr)
376-
raise IncompatibleFrequency(msg)
375+
_raise_on_incompatible(self, value)
377376

378377
value = value.asi8
379378
elif isinstance(value, Period):
380379

381380
if self.freqstr != value.freqstr:
382-
msg = DIFFERENT_FREQ_INDEX.format(self.freqstr, value.freqstr)
383-
raise IncompatibleFrequency(msg)
381+
_raise_on_incompatible(self, value)
384382

385383
value = value.ordinal
386384
elif isna(value):
@@ -696,8 +694,7 @@ def _add_offset(self, other):
696694
assert not isinstance(other, Tick)
697695
base = frequencies.get_base_alias(other.rule_code)
698696
if base != self.freq.rule_code:
699-
msg = DIFFERENT_FREQ_INDEX.format(self.freqstr, other.freqstr)
700-
raise IncompatibleFrequency(msg)
697+
_raise_on_incompatible(self, other)
701698

702699
# Note: when calling parent class's _add_timedeltalike_scalar,
703700
# it will call delta_to_nanoseconds(delta). Because delta here
@@ -760,10 +757,7 @@ def _add_delta(self, other):
760757
"""
761758
if not isinstance(self.freq, Tick):
762759
# We cannot add timedelta-like to non-tick PeriodArray
763-
raise IncompatibleFrequency("Input has different freq from "
764-
"{cls}(freq={freqstr})"
765-
.format(cls=type(self).__name__,
766-
freqstr=self.freqstr))
760+
_raise_on_incompatible(self, other)
767761

768762
new_ordinals = super(PeriodArray, self)._add_delta(other)
769763
return type(self)(new_ordinals, freq=self.freq)
@@ -815,10 +809,7 @@ def _check_timedeltalike_freq_compat(self, other):
815809
# by which will be added to self.
816810
return delta
817811

818-
raise IncompatibleFrequency("Input has different freq from "
819-
"{cls}(freq={freqstr})"
820-
.format(cls=type(self).__name__,
821-
freqstr=self.freqstr))
812+
_raise_on_incompatible(self, other)
822813

823814
def _values_for_argsort(self):
824815
return self._data
@@ -827,6 +818,34 @@ def _values_for_argsort(self):
827818
PeriodArray._add_comparison_ops()
828819

829820

821+
def _raise_on_incompatible(left, right):
822+
"""
823+
Helper function to render a consistent error message when raising
824+
IncompatibleFrequency.
825+
826+
Parameters
827+
----------
828+
left : PeriodArray
829+
right : DateOffset, Period, ndarray, or timedelta-like
830+
831+
Raises
832+
------
833+
IncompatibleFrequency
834+
"""
835+
# GH#24283 error message format depends on whether right is scalar
836+
if isinstance(right, np.ndarray):
837+
other_freq = None
838+
elif isinstance(right, (ABCPeriodIndex, PeriodArray, Period, DateOffset)):
839+
other_freq = right.freqstr
840+
else:
841+
other_freq = _delta_to_tick(Timedelta(right)).freqstr
842+
843+
msg = DIFFERENT_FREQ.format(cls=type(left).__name__,
844+
own_freq=left.freqstr,
845+
other_freq=other_freq)
846+
raise IncompatibleFrequency(msg)
847+
848+
830849
# -------------------------------------------------------------------
831850
# Constructor Helpers
832851

pandas/core/indexes/period.py

+18-8
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from pandas._libs import index as libindex
88
from pandas._libs.tslibs import NaT, iNaT, resolution
99
from pandas._libs.tslibs.period import (
10-
DIFFERENT_FREQ_INDEX, IncompatibleFrequency, Period)
10+
DIFFERENT_FREQ, IncompatibleFrequency, Period)
1111
from pandas.util._decorators import (
1212
Appender, Substitution, cache_readonly, deprecate_kwarg)
1313

@@ -367,7 +367,10 @@ def _maybe_convert_timedelta(self, other):
367367
base = frequencies.get_base_alias(freqstr)
368368
if base == self.freq.rule_code:
369369
return other.n
370-
msg = DIFFERENT_FREQ_INDEX.format(self.freqstr, other.freqstr)
370+
371+
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
372+
own_freq=self.freqstr,
373+
other_freq=other.freqstr)
371374
raise IncompatibleFrequency(msg)
372375
elif is_integer(other):
373376
# integer is passed to .shift via
@@ -376,9 +379,10 @@ def _maybe_convert_timedelta(self, other):
376379
return other
377380

378381
# raise when input doesn't have freq
379-
msg = "Input has different freq from {cls}(freq={freqstr})"
380-
raise IncompatibleFrequency(msg.format(cls=type(self).__name__,
381-
freqstr=self.freqstr))
382+
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
383+
own_freq=self.freqstr,
384+
other_freq=None)
385+
raise IncompatibleFrequency(msg)
382386

383387
# ------------------------------------------------------------------------
384388
# Rendering Methods
@@ -547,7 +551,9 @@ def astype(self, dtype, copy=True, how='start'):
547551
def searchsorted(self, value, side='left', sorter=None):
548552
if isinstance(value, Period):
549553
if value.freq != self.freq:
550-
msg = DIFFERENT_FREQ_INDEX.format(self.freqstr, value.freqstr)
554+
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
555+
own_freq=self.freqstr,
556+
other_freq=value.freqstr)
551557
raise IncompatibleFrequency(msg)
552558
value = value.ordinal
553559
elif isinstance(value, compat.string_types):
@@ -631,7 +637,9 @@ def get_indexer(self, target, method=None, limit=None, tolerance=None):
631637
target = ensure_index(target)
632638

633639
if hasattr(target, 'freq') and target.freq != self.freq:
634-
msg = DIFFERENT_FREQ_INDEX.format(self.freqstr, target.freqstr)
640+
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
641+
own_freq=self.freqstr,
642+
other_freq=target.freqstr)
635643
raise IncompatibleFrequency(msg)
636644

637645
if isinstance(target, PeriodIndex):
@@ -819,7 +827,9 @@ def _assert_can_do_setop(self, other):
819827
raise ValueError('can only call with other PeriodIndex-ed objects')
820828

821829
if self.freq != other.freq:
822-
msg = DIFFERENT_FREQ_INDEX.format(self.freqstr, other.freqstr)
830+
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
831+
own_freq=self.freqstr,
832+
other_freq=other.freqstr)
823833
raise IncompatibleFrequency(msg)
824834

825835
def _wrap_setop_result(self, other, result):

pandas/tests/arithmetic/test_period.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ def test_pi_cmp_nat_mismatched_freq_raises(self, freq):
206206
idx1 = PeriodIndex(['2011-01', '2011-02', 'NaT', '2011-05'], freq=freq)
207207

208208
diff = PeriodIndex(['2011-02', '2011-01', '2011-04', 'NaT'], freq='4M')
209-
msg = "Input has different freq=4M from PeriodIndex"
209+
msg = "Input has different freq=4M from Period(Array|Index)"
210210
with pytest.raises(IncompatibleFrequency, match=msg):
211211
idx1 > diff
212212

@@ -1060,14 +1060,15 @@ def test_pi_offset_errors(self):
10601060

10611061
# Series op is applied per Period instance, thus error is raised
10621062
# from Period
1063-
msg = r"Input has different freq from Period.*?\(freq=D\)"
10641063
for obj in [idx, ser]:
1064+
msg = r"Input has different freq=2H from Period.*?\(freq=D\)"
10651065
with pytest.raises(IncompatibleFrequency, match=msg):
10661066
obj + pd.offsets.Hour(2)
10671067

10681068
with pytest.raises(IncompatibleFrequency, match=msg):
10691069
pd.offsets.Hour(2) + obj
10701070

1071+
msg = r"Input has different freq=-2H from Period.*?\(freq=D\)"
10711072
with pytest.raises(IncompatibleFrequency, match=msg):
10721073
obj - pd.offsets.Hour(2)
10731074

pandas/tests/indexes/period/test_indexing.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ def test_get_loc2(self):
577577
with pytest.raises(ValueError, match=msg):
578578
idx.get_loc('2000-01-10', method='nearest', tolerance='foo')
579579

580-
msg = 'Input has different freq from PeriodArray\\(freq=D\\)'
580+
msg = 'Input has different freq=None from PeriodArray\\(freq=D\\)'
581581
with pytest.raises(ValueError, match=msg):
582582
idx.get_loc('2000-01-10', method='nearest', tolerance='1 hour')
583583
with pytest.raises(KeyError):
@@ -607,7 +607,7 @@ def test_get_indexer2(self):
607607
tolerance='1 hour'),
608608
np.array([0, -1, 1], dtype=np.intp))
609609

610-
msg = 'Input has different freq from PeriodArray\\(freq=H\\)'
610+
msg = 'Input has different freq=None from PeriodArray\\(freq=H\\)'
611611
with pytest.raises(ValueError, match=msg):
612612
idx.get_indexer(target, 'nearest', tolerance='1 minute')
613613

@@ -626,7 +626,7 @@ def test_get_indexer2(self):
626626
np.timedelta64(1, 'M'), ]
627627
with pytest.raises(
628628
libperiod.IncompatibleFrequency,
629-
match='Input has different freq from'):
629+
match='Input has different freq=None from'):
630630
idx.get_indexer(target, 'nearest', tolerance=tol_bad)
631631

632632
def test_indexing(self):

0 commit comments

Comments
 (0)