Skip to content

CLN: standardize different freq message #24283

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 8 commits into from
Dec 16, 2018
21 changes: 14 additions & 7 deletions pandas/_libs/tslibs/period.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1455,7 +1455,9 @@ def extract_ordinals(object[:] values, freq):
ordinals[i] = p.ordinal

if p.freqstr != freqstr:
msg = DIFFERENT_FREQ_INDEX.format(freqstr, p.freqstr)
msg = DIFFERENT_FREQ.format(cls="PeriodIndex",
own_freq=freqstr,
other_freq=p.freqstr)
raise IncompatibleFrequency(msg)

except AttributeError:
Expand Down Expand Up @@ -1545,9 +1547,8 @@ cdef int64_t[:] localize_dt64arr_to_period(int64_t[:] stamps,
return result


_DIFFERENT_FREQ = "Input has different freq={1} from Period(freq={0})"
DIFFERENT_FREQ_INDEX = ("Input has different freq={1} "
"from PeriodIndex(freq={0})")
DIFFERENT_FREQ = ("Input has different freq={other_freq} "
"from {cls}(freq={own_freq})")


class IncompatibleFrequency(ValueError):
Expand Down Expand Up @@ -1596,7 +1597,9 @@ cdef class _Period(object):
def __richcmp__(self, other, op):
if is_period_object(other):
if other.freq != self.freq:
msg = _DIFFERENT_FREQ.format(self.freqstr, other.freqstr)
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
own_freq=self.freqstr,
other_freq=other.freqstr)
raise IncompatibleFrequency(msg)
return PyObject_RichCompareBool(self.ordinal, other.ordinal, op)
elif other is NaT:
Expand Down Expand Up @@ -1637,7 +1640,9 @@ cdef class _Period(object):
if base == self.freq.rule_code:
ordinal = self.ordinal + other.n
return Period(ordinal=ordinal, freq=self.freq)
msg = _DIFFERENT_FREQ.format(self.freqstr, other.freqstr)
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
own_freq=self.freqstr,
other_freq=other.freqstr)
raise IncompatibleFrequency(msg)
else: # pragma no cover
return NotImplemented
Expand Down Expand Up @@ -1684,7 +1689,9 @@ cdef class _Period(object):
return Period(ordinal=ordinal, freq=self.freq)
elif is_period_object(other):
if other.freq != self.freq:
msg = _DIFFERENT_FREQ.format(self.freqstr, other.freqstr)
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
own_freq=self.freqstr,
other_freq=other.freqstr)
raise IncompatibleFrequency(msg)
# GH 23915 - mul by base freq since __add__ is agnostic of n
return (self.ordinal - other.ordinal) * self.freq.base
Expand Down
6 changes: 4 additions & 2 deletions pandas/core/arrays/datetimelike.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from pandas._libs import NaT, algos, iNaT, lib
from pandas._libs.tslibs.period import (
DIFFERENT_FREQ_INDEX, IncompatibleFrequency, Period)
DIFFERENT_FREQ, IncompatibleFrequency, Period)
from pandas._libs.tslibs.timedeltas import Timedelta, delta_to_nanoseconds
from pandas._libs.tslibs.timestamps import (
RoundTo, maybe_integer_op_deprecated, round_nsint64)
Expand Down Expand Up @@ -736,7 +736,9 @@ def _sub_period_array(self, other):
raise ValueError("cannot subtract arrays/indices of "
"unequal length")
if self.freq != other.freq:
msg = DIFFERENT_FREQ_INDEX.format(self.freqstr, other.freqstr)
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
own_freq=self.freqstr,
other_freq=other.freqstr)
raise IncompatibleFrequency(msg)

new_values = checked_add_with_arr(self.asi8, -other.asi8,
Expand Down
59 changes: 39 additions & 20 deletions pandas/core/arrays/period.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from pandas._libs.tslibs import NaT, iNaT, period as libperiod
from pandas._libs.tslibs.fields import isleapyear_arr
from pandas._libs.tslibs.period import (
DIFFERENT_FREQ_INDEX, IncompatibleFrequency, Period, get_period_field_arr,
DIFFERENT_FREQ, IncompatibleFrequency, Period, get_period_field_arr,
period_asfreq_arr)
from pandas._libs.tslibs.timedeltas import Timedelta, delta_to_nanoseconds
import pandas.compat as compat
Expand All @@ -30,7 +30,7 @@
from pandas.core.missing import backfill_1d, pad_1d

from pandas.tseries import frequencies
from pandas.tseries.offsets import Tick
from pandas.tseries.offsets import DateOffset, Tick, _delta_to_tick


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

if isinstance(values, type(self)):
if freq is not None and freq != values.freq:
msg = DIFFERENT_FREQ_INDEX.format(values.freq.freqstr,
freq.freqstr)
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
own_freq=values.freq.freqstr,
other_freq=freq.freqstr)
raise IncompatibleFrequency(msg)
values, freq = values._data, values.freq

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

def _check_compatible_with(self, other):
if self.freqstr != other.freqstr:
msg = DIFFERENT_FREQ_INDEX.format(self.freqstr, other.freqstr)
raise IncompatibleFrequency(msg)
_raise_on_incompatible(self, other)

# --------------------------------------------------------------------
# Data / Attributes
Expand Down Expand Up @@ -372,15 +372,13 @@ def __setitem__(
value = period_array(value)

if self.freqstr != value.freqstr:
Copy link
Contributor

Choose a reason for hiding this comment

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

PeriodArray.__setitem__ is being removed, so I was hoping to not change these.

msg = DIFFERENT_FREQ_INDEX.format(self.freqstr, value.freqstr)
raise IncompatibleFrequency(msg)
_raise_on_incompatible(self, value)

value = value.asi8
elif isinstance(value, Period):

if self.freqstr != value.freqstr:
msg = DIFFERENT_FREQ_INDEX.format(self.freqstr, value.freqstr)
raise IncompatibleFrequency(msg)
_raise_on_incompatible(self, value)

value = value.ordinal
elif isna(value):
Expand Down Expand Up @@ -696,8 +694,7 @@ def _add_offset(self, other):
assert not isinstance(other, Tick)
base = frequencies.get_base_alias(other.rule_code)
if base != self.freq.rule_code:
msg = DIFFERENT_FREQ_INDEX.format(self.freqstr, other.freqstr)
raise IncompatibleFrequency(msg)
_raise_on_incompatible(self, other)

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

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

raise IncompatibleFrequency("Input has different freq from "
"{cls}(freq={freqstr})"
.format(cls=type(self).__name__,
freqstr=self.freqstr))
_raise_on_incompatible(self, other)

def _values_for_argsort(self):
return self._data
Expand All @@ -827,6 +818,34 @@ def _values_for_argsort(self):
PeriodArray._add_comparison_ops()


def _raise_on_incompatible(left, right):
"""
Helper function to render a consistent error message when raising
IncompatibleFrequency.

Parameters
----------
left : PeriodArray
right : DateOffset, Period, ndarray, or timedelta-like

Raises
------
IncompatibleFrequency
"""
# GH#24283 error message format depends on whether right is scalar
if isinstance(right, np.ndarray):
other_freq = None
elif isinstance(right, (ABCPeriodIndex, PeriodArray, Period, DateOffset)):
other_freq = right.freqstr
else:
other_freq = _delta_to_tick(Timedelta(right)).freqstr

msg = DIFFERENT_FREQ.format(cls=type(left).__name__,
own_freq=left.freqstr,
other_freq=other_freq)
raise IncompatibleFrequency(msg)


# -------------------------------------------------------------------
# Constructor Helpers

Expand Down
26 changes: 18 additions & 8 deletions pandas/core/indexes/period.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from pandas._libs import index as libindex
from pandas._libs.tslibs import NaT, iNaT, resolution
from pandas._libs.tslibs.period import (
DIFFERENT_FREQ_INDEX, IncompatibleFrequency, Period)
DIFFERENT_FREQ, IncompatibleFrequency, Period)
from pandas.util._decorators import (
Appender, Substitution, cache_readonly, deprecate_kwarg)

Expand Down Expand Up @@ -367,7 +367,10 @@ def _maybe_convert_timedelta(self, other):
base = frequencies.get_base_alias(freqstr)
if base == self.freq.rule_code:
return other.n
msg = DIFFERENT_FREQ_INDEX.format(self.freqstr, other.freqstr)

msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
own_freq=self.freqstr,
other_freq=other.freqstr)
raise IncompatibleFrequency(msg)
elif is_integer(other):
# integer is passed to .shift via
Expand All @@ -376,9 +379,10 @@ def _maybe_convert_timedelta(self, other):
return other

# raise when input doesn't have freq
msg = "Input has different freq from {cls}(freq={freqstr})"
raise IncompatibleFrequency(msg.format(cls=type(self).__name__,
freqstr=self.freqstr))
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
own_freq=self.freqstr,
other_freq=None)
raise IncompatibleFrequency(msg)

# ------------------------------------------------------------------------
# Rendering Methods
Expand Down Expand Up @@ -547,7 +551,9 @@ def astype(self, dtype, copy=True, how='start'):
def searchsorted(self, value, side='left', sorter=None):
if isinstance(value, Period):
if value.freq != self.freq:
msg = DIFFERENT_FREQ_INDEX.format(self.freqstr, value.freqstr)
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
own_freq=self.freqstr,
other_freq=value.freqstr)
raise IncompatibleFrequency(msg)
value = value.ordinal
elif isinstance(value, compat.string_types):
Expand Down Expand Up @@ -631,7 +637,9 @@ def get_indexer(self, target, method=None, limit=None, tolerance=None):
target = ensure_index(target)

if hasattr(target, 'freq') and target.freq != self.freq:
msg = DIFFERENT_FREQ_INDEX.format(self.freqstr, target.freqstr)
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
own_freq=self.freqstr,
other_freq=target.freqstr)
raise IncompatibleFrequency(msg)

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

if self.freq != other.freq:
msg = DIFFERENT_FREQ_INDEX.format(self.freqstr, other.freqstr)
msg = DIFFERENT_FREQ.format(cls=type(self).__name__,
own_freq=self.freqstr,
other_freq=other.freqstr)
raise IncompatibleFrequency(msg)

def _wrap_setop_result(self, other, result):
Expand Down
5 changes: 3 additions & 2 deletions pandas/tests/arithmetic/test_period.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ def test_pi_cmp_nat_mismatched_freq_raises(self, freq):
idx1 = PeriodIndex(['2011-01', '2011-02', 'NaT', '2011-05'], freq=freq)

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

Expand Down Expand Up @@ -1060,14 +1060,15 @@ def test_pi_offset_errors(self):

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

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

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

Expand Down
6 changes: 3 additions & 3 deletions pandas/tests/indexes/period/test_indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ def test_get_loc2(self):
with pytest.raises(ValueError, match=msg):
idx.get_loc('2000-01-10', method='nearest', tolerance='foo')

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

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

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

def test_indexing(self):
Expand Down