From b3ad6addc030c4d8adff64a7aa927969595a5869 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Thu, 14 May 2020 13:35:22 -0700 Subject: [PATCH 1/2] REF: inherit BaseOffset in BusinessMixin --- pandas/_libs/tslibs/offsets.pyx | 12 +++++-- pandas/tseries/offsets.py | 57 ++++++++++++--------------------- 2 files changed, 30 insertions(+), 39 deletions(-) diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index 3a9639a700953..8d86c5e53eb9b 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -788,10 +788,13 @@ cdef class _Tick(ABCTick): object.__setattr__(self, "n", state["n"]) -class BusinessMixin: +class BusinessMixin(BaseOffset): """ Mixin to business types to provide related functions. """ + def __init__(self, n=1, normalize=False, offset=timedelta(0)): + BaseOffset.__init__(self, n, normalize) + object.__setattr__(self, "_offset", offset) @property def offset(self): @@ -815,7 +818,11 @@ class BusinessMixin: class BusinessHourMixin(BusinessMixin): _adjust_dst = False - def __init__(self, start="09:00", end="17:00", offset=timedelta(0)): + def __init__( + self, n=1, normalize=False, start="09:00", end="17:00", offset=timedelta(0) + ): + BusinessMixin.__init__(self, n, normalize, offset) + # must be validated here to equality check if np.ndim(start) == 0: # i.e. not is_list_like @@ -859,7 +866,6 @@ class BusinessHourMixin(BusinessMixin): object.__setattr__(self, "start", start) object.__setattr__(self, "end", end) - object.__setattr__(self, "_offset", offset) def _repr_attrs(self) -> str: out = super()._repr_attrs() diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py index 4912dc0eb349e..377734afa9ebb 100644 --- a/pandas/tseries/offsets.py +++ b/pandas/tseries/offsets.py @@ -296,7 +296,7 @@ def is_on_offset(self, dt): return True -class SingleConstructorOffset(BaseOffset): +class SingleConstructorMixin: _params = cache_readonly(BaseOffset._params.fget) freqstr = cache_readonly(BaseOffset.freqstr.fget) @@ -308,6 +308,10 @@ def _from_name(cls, suffix=None): return cls() +class SingleConstructorOffset(SingleConstructorMixin, BaseOffset): + pass + + class BusinessDay(BusinessMixin, SingleConstructorOffset): """ DateOffset subclass representing possibly n business days. @@ -316,10 +320,6 @@ class BusinessDay(BusinessMixin, SingleConstructorOffset): _prefix = "B" _attributes = frozenset(["n", "normalize", "offset"]) - def __init__(self, n=1, normalize=False, offset=timedelta(0)): - BaseOffset.__init__(self, n, normalize) - object.__setattr__(self, "_offset", offset) - def _offset_str(self) -> str: def get_str(td): off_str = "" @@ -419,7 +419,15 @@ def is_on_offset(self, dt: datetime) -> bool: return dt.weekday() < 5 -class BusinessHourMixin(liboffsets.BusinessHourMixin): +class BusinessHour(SingleConstructorMixin, liboffsets.BusinessHourMixin): + """ + DateOffset subclass representing possibly n business hours. + """ + + _prefix = "BH" + _anchor = 0 + _attributes = frozenset(["n", "normalize", "start", "end", "offset"]) + @cache_readonly def next_bday(self): """ @@ -679,22 +687,6 @@ def _is_on_offset(self, dt): return False -class BusinessHour(BusinessHourMixin, SingleConstructorOffset): - """ - DateOffset subclass representing possibly n business hours. - """ - - _prefix = "BH" - _anchor = 0 - _attributes = frozenset(["n", "normalize", "start", "end", "offset"]) - - def __init__( - self, n=1, normalize=False, start="09:00", end="17:00", offset=timedelta(0) - ): - BaseOffset.__init__(self, n, normalize) - super().__init__(start=start, end=end, offset=offset) - - class CustomBusinessDay(CustomMixin, BusinessDay): """ DateOffset subclass representing custom business days excluding holidays. @@ -727,9 +719,7 @@ def __init__( calendar=None, offset=timedelta(0), ): - BaseOffset.__init__(self, n, normalize) - object.__setattr__(self, "_offset", offset) - + BusinessDay.__init__(self, n, normalize, offset) CustomMixin.__init__(self, weekmask, holidays, calendar) @apply_wraps @@ -772,7 +762,7 @@ def is_on_offset(self, dt: datetime) -> bool: return np.is_busday(day64, busdaycal=self.calendar) -class CustomBusinessHour(CustomMixin, BusinessHourMixin, SingleConstructorOffset): +class CustomBusinessHour(CustomMixin, BusinessHour): """ DateOffset subclass representing possibly n custom business days. """ @@ -794,11 +784,8 @@ def __init__( end="17:00", offset=timedelta(0), ): - BaseOffset.__init__(self, n, normalize) - object.__setattr__(self, "_offset", offset) - + BusinessHour.__init__(self, n, normalize, start=start, end=end, offset=offset) CustomMixin.__init__(self, weekmask, holidays, calendar) - BusinessHourMixin.__init__(self, start=start, end=end, offset=offset) # --------------------------------------------------------------------- @@ -898,9 +885,7 @@ def __init__( calendar=None, offset=timedelta(0), ): - BaseOffset.__init__(self, n, normalize) - object.__setattr__(self, "_offset", offset) - + BusinessMixin.__init__(self, n, normalize, offset) CustomMixin.__init__(self, weekmask, holidays, calendar) @cache_readonly @@ -980,9 +965,9 @@ def __init__(self, n=1, normalize=False, day_of_month=None): BaseOffset.__init__(self, n, normalize) if day_of_month is None: - object.__setattr__(self, "day_of_month", self._default_day_of_month) - else: - object.__setattr__(self, "day_of_month", int(day_of_month)) + day_of_month = self._default_day_of_month + + object.__setattr__(self, "day_of_month", int(day_of_month)) if not self._min_day_of_month <= self.day_of_month <= 27: raise ValueError( "day_of_month must be " From 17cf9d5124b9c16461bc53569167274fb2a7d7d8 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Thu, 14 May 2020 14:43:00 -0700 Subject: [PATCH 2/2] mypy fixup --- pandas/tseries/frequencies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index 6213ea198f2cb..d95ffd5b0876d 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -165,7 +165,7 @@ def to_offset(freq) -> Optional[DateOffset]: ) stride = int(stride) offset = _get_offset(name) - offset = offset * int(np.fabs(stride) * stride_sign) + offset = offset * int(np.fabs(stride) * stride_sign) # type: ignore if delta is None: delta = offset else: