From 5ec1363d206ecf274879353f2ad1c264920dc850 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sat, 23 May 2020 11:30:33 -0700 Subject: [PATCH 1/4] REF: make CustomMixin a cdef class --- pandas/_libs/tslibs/offsets.pyx | 63 ++++++++++++++++++++++----------- pandas/tseries/offsets.py | 40 +++++---------------- 2 files changed, 51 insertions(+), 52 deletions(-) diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index f9ddb6fabc7bb..c0678a8e6a542 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -709,7 +709,7 @@ cdef class BaseOffset: raise ValueError(f'`n` argument must be an integer, got {n}') return nint - def __setstate__(self, state): + cpdef __setstate__(self, state): """Reconstruct an instance from a pickled state""" if 'offset' in state: # Older (<0.22.0) versions have offset attribute instead of _offset @@ -731,6 +731,8 @@ cdef class BaseOffset: self.__dict__.update(state) if 'weekmask' in state and 'holidays' in state: + self.weekmask = state.pop("weekmask") + self.holidays = state.pop("holidays") calendar, holidays = _get_calendar(weekmask=self.weekmask, holidays=self.holidays, calendar=None) @@ -922,7 +924,7 @@ cdef class Tick(SingleConstructorOffset): def __reduce__(self): return (type(self), (self.n,)) - def __setstate__(self, state): + cpdef __setstate__(self, state): self.n = state["n"] self.normalize = False @@ -1024,6 +1026,44 @@ cdef class BusinessMixin(SingleConstructorOffset): self._offset = state["_offset"] +cdef class CustomBusinessMixin(BusinessMixin): + cdef readonly: + object weekmask, holidays, calendar + + def __init__( + self, + n=1, + normalize=False, + weekmask="Mon Tue Wed Thu Fri", + holidays=None, + calendar=None, + offset=timedelta(0) + ): + BusinessMixin.__init__(self, n, normalize, offset) + + calendar, holidays = _get_calendar( + weekmask=weekmask, holidays=holidays, calendar=calendar + ) + # Custom offset instances are identified by the + # following two attributes. See DateOffset._params() + # holidays, weekmask + self.weekmask = weekmask + self.holidays = holidays + self.calendar = calendar + + cpdef __setstate__(self, state): + weekmask = state.pop("weekmask") + holidays = state.pop("holidays") + calendar, holidays = _get_calendar(weekmask=weekmask, + holidays=holidays, + calendar=None) + self.weekmask = weekmask + self.calendar = calendar + self.holidays = holidays + + BusinessMixin.__setstate__(self, state) + + class BusinessHourMixin(BusinessMixin): _adjust_dst = False @@ -1121,25 +1161,6 @@ class BusinessHourMixin(BusinessMixin): assert False -class CustomMixin: - """ - Mixin for classes that define and validate calendar, holidays, - and weekdays attributes. - """ - - def __init__(self, weekmask, holidays, calendar): - calendar, holidays = _get_calendar( - weekmask=weekmask, holidays=holidays, calendar=calendar - ) - # Custom offset instances are identified by the - # following two attributes. See DateOffset._params() - # holidays, weekmask - - object.__setattr__(self, "weekmask", weekmask) - object.__setattr__(self, "holidays", holidays) - object.__setattr__(self, "calendar", calendar) - - class WeekOfMonthMixin(SingleConstructorOffset): """ Mixin for methods common to WeekOfMonth and LastWeekOfMonth. diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py index b8e95a9c72118..9316b80fa7f1f 100644 --- a/pandas/tseries/offsets.py +++ b/pandas/tseries/offsets.py @@ -17,7 +17,6 @@ ApplyTypeError, BaseOffset, BusinessMixin, - CustomMixin, Day, Hour, Micro, @@ -300,7 +299,7 @@ def is_on_offset(self, dt): return True -class BusinessDay(BusinessMixin, SingleConstructorOffset): +class BusinessDay(BusinessMixin): """ DateOffset subclass representing possibly n business days. """ @@ -585,7 +584,8 @@ def apply(self, other): # adjust by business days first if bd != 0: - if isinstance(self, CustomMixin): # GH 30593 + if self._prefix.startswith("C"): + # GH#30593 this is a CustomFooOffset skip_bd = CustomBusinessDay( n=bd, weekmask=self.weekmask, @@ -679,7 +679,7 @@ def _is_on_offset(self, dt): return False -class CustomBusinessDay(CustomMixin, BusinessDay): +class CustomBusinessDay(liboffsets.CustomBusinessMixin, BusinessDay): """ DateOffset subclass representing custom business days excluding holidays. @@ -708,18 +708,6 @@ def __reduce__(self): tup = (self.n, self.normalize, self.weekmask, self.holidays, None, self.offset) return type(self), tup - def __init__( - self, - n=1, - normalize=False, - weekmask="Mon Tue Wed Thu Fri", - holidays=None, - calendar=None, - offset=timedelta(0), - ): - BusinessDay.__init__(self, n, normalize, offset) - CustomMixin.__init__(self, weekmask, holidays, calendar) - @apply_wraps def apply(self, other): if self.n <= 0: @@ -760,7 +748,7 @@ def is_on_offset(self, dt: datetime) -> bool: return np.is_busday(day64, busdaycal=self.calendar) -class CustomBusinessHour(CustomMixin, BusinessHour): +class CustomBusinessHour(liboffsets.CustomBusinessMixin, BusinessHour): """ DateOffset subclass representing possibly n custom business days. """ @@ -783,7 +771,9 @@ def __init__( offset=timedelta(0), ): BusinessHour.__init__(self, n, normalize, start=start, end=end, offset=offset) - CustomMixin.__init__(self, weekmask, holidays, calendar) + liboffsets.CustomBusinessMixin.__init__( + self, n, normalize, weekmask, holidays, calendar, offset + ) # --------------------------------------------------------------------- @@ -827,7 +817,7 @@ class BusinessMonthBegin(liboffsets.MonthOffset): @doc(bound="bound") -class _CustomBusinessMonth(CustomMixin, BusinessMixin, liboffsets.MonthOffset): +class _CustomBusinessMonth(liboffsets.CustomBusinessMixin, liboffsets.MonthOffset): """ DateOffset subclass representing custom business month(s). @@ -857,18 +847,6 @@ class _CustomBusinessMonth(CustomMixin, BusinessMixin, liboffsets.MonthOffset): is_on_offset = BaseOffset.is_on_offset # override MonthOffset method apply_index = BaseOffset.apply_index # override MonthOffset method - def __init__( - self, - n=1, - normalize=False, - weekmask="Mon Tue Wed Thu Fri", - holidays=None, - calendar=None, - offset=timedelta(0), - ): - BusinessMixin.__init__(self, n, normalize, offset) - CustomMixin.__init__(self, weekmask, holidays, calendar) - def __reduce__(self): # None for self.calendar bc np.busdaycalendar doesnt pickle nicely return ( From 5a5a80be9b71c6f17b1b26117d35d14f770474a4 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sat, 23 May 2020 12:08:39 -0700 Subject: [PATCH 2/4] update doc --- doc/source/reference/offset_frequency.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/source/reference/offset_frequency.rst b/doc/source/reference/offset_frequency.rst index c8c591e296449..b6de9efd2bf85 100644 --- a/doc/source/reference/offset_frequency.rst +++ b/doc/source/reference/offset_frequency.rst @@ -126,6 +126,9 @@ Properties CustomBusinessDay.normalize CustomBusinessDay.rule_code CustomBusinessDay.n + CustomBusinessDay.weekmask + CustomBusinessDay.calendar + CustomBusinessDay.holidays Methods ~~~~~~~ @@ -159,6 +162,9 @@ Properties CustomBusinessHour.normalize CustomBusinessHour.rule_code CustomBusinessHour.n + CustomBusinessHour.weekmask + CustomBusinessHour.calendar + CustomBusinessHour.holidays Methods ~~~~~~~ @@ -329,6 +335,9 @@ Properties CustomBusinessMonthEnd.normalize CustomBusinessMonthEnd.rule_code CustomBusinessMonthEnd.n + CustomBusinessMonthEnd.weekmask + CustomBusinessMonthEnd.calendar + CustomBusinessMonthEnd.holidays Methods ~~~~~~~ @@ -363,6 +372,9 @@ Properties CustomBusinessMonthBegin.normalize CustomBusinessMonthBegin.rule_code CustomBusinessMonthBegin.n + CustomBusinessMonthBegin.weekmask + CustomBusinessMonthBegin.calendar + CustomBusinessMonthBegin.holidays Methods ~~~~~~~ @@ -1377,6 +1389,9 @@ Properties CBMonthEnd.offset CBMonthEnd.rule_code CBMonthEnd.n + CBMonthEnd.weekmask + CBMonthEnd.holidays + CBMonthEnd.calendar Methods ~~~~~~~ @@ -1418,6 +1433,9 @@ Properties CBMonthBegin.offset CBMonthBegin.rule_code CBMonthBegin.n + CBMonthBegin.weekmask + CBMonthBegin.holidays + CBMonthBegin.calendar Methods ~~~~~~~ @@ -1456,6 +1474,9 @@ Properties CDay.offset CDay.rule_code CDay.n + CDay.weekmask + CDay.calendar + CDay.holidays Methods ~~~~~~~ From 1c63dc28497954442594080c88c074d98a0e3068 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 27 May 2020 10:48:56 -0700 Subject: [PATCH 3/4] post-merge fixup --- pandas/_libs/tslibs/offsets.pyx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index 45c794e90630d..8e5634253bd39 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -3261,7 +3261,7 @@ cdef class Easter(SingleConstructorOffset): # Custom Offset classes -class CustomBusinessDay(CustomMixin, BusinessDay): +cdef class CustomBusinessDay(BusinessDay): """ DateOffset subclass representing custom business days excluding holidays. @@ -3300,12 +3300,12 @@ class CustomBusinessDay(CustomMixin, BusinessDay): offset=timedelta(0), ): BusinessDay.__init__(self, n, normalize, offset) - CustomMixin.__init__(self, weekmask, holidays, calendar) + self._init_custom(weekmask, holidays, calendar) - def __setstate__(self, state): + cpdef __setstate__(self, state): self.holidays = state.pop("holidays") self.weekmask = state.pop("weekmask") - super().__setstate__(state) + BusinessDay.__setstate__(self, state) @apply_wraps def apply(self, other): @@ -3347,7 +3347,7 @@ class CustomBusinessDay(CustomMixin, BusinessDay): return np.is_busday(day64, busdaycal=self.calendar) -class CustomBusinessHour(CustomMixin, BusinessHour): +class CustomBusinessHour(BusinessHour): """ DateOffset subclass representing possibly n custom business days. """ @@ -3370,7 +3370,7 @@ class CustomBusinessHour(CustomMixin, BusinessHour): offset=timedelta(0), ): BusinessHour.__init__(self, n, normalize, start=start, end=end, offset=offset) - CustomMixin.__init__(self, weekmask, holidays, calendar) + self._init_custom(weekmask, holidays, calendar) def __reduce__(self): # None for self.calendar bc np.busdaycalendar doesnt pickle nicely @@ -3389,7 +3389,7 @@ class CustomBusinessHour(CustomMixin, BusinessHour): ) -class _CustomBusinessMonth(CustomMixin, BusinessMixin, MonthOffset): +class _CustomBusinessMonth(BusinessMixin, MonthOffset): """ DateOffset subclass representing custom business month(s). @@ -3429,7 +3429,7 @@ class _CustomBusinessMonth(CustomMixin, BusinessMixin, MonthOffset): offset=timedelta(0), ): BusinessMixin.__init__(self, n, normalize, offset) - CustomMixin.__init__(self, weekmask, holidays, calendar) + self._init_custom(weekmask, holidays, calendar) def __reduce__(self): # None for self.calendar bc np.busdaycalendar doesnt pickle nicely From 3b303a5cf9ec3d3f4a2a90e17881e1a248f8da5f Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 27 May 2020 12:07:01 -0700 Subject: [PATCH 4/4] Troubleshoot docbuild --- doc/source/reference/offset_frequency.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/source/reference/offset_frequency.rst b/doc/source/reference/offset_frequency.rst index 8f9b669b2237d..ee89df3114048 100644 --- a/doc/source/reference/offset_frequency.rst +++ b/doc/source/reference/offset_frequency.rst @@ -59,6 +59,9 @@ Properties BusinessDay.normalize BusinessDay.rule_code BusinessDay.n + BusinessDay.weekmask + BusinessDay.holidays + BusinessDay.calendar Methods ~~~~~~~ @@ -95,6 +98,9 @@ Properties BusinessHour.n BusinessHour.start BusinessHour.end + BusinessHour.weekmask + BusinessHour.holidays + BusinessHour.calendar Methods ~~~~~~~ @@ -1253,6 +1259,9 @@ Properties BDay.offset BDay.rule_code BDay.n + BDay.weekmask + BDay.holidays + BDay.calendar Methods ~~~~~~~ @@ -1473,6 +1482,7 @@ Methods CDay.rollforward CDay.__call__ + .. _api.frequencies: ===========