diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index f2860fad75428..5efb9b3534f14 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -733,6 +733,11 @@ class _BaseOffset: ) return self.is_anchored() + def is_anchored(self) -> bool: + # TODO: Does this make sense for the general case? It would help + # if there were a canonical docstring for what is_anchored means. + return self.n == 1 + class BaseOffset(_BaseOffset): # Here we add __rfoo__ methods that don't play well with cdef classes diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index fa41ce00c4559..6213ea198f2cb 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -219,7 +219,7 @@ def _get_offset(name: str) -> DateOffset: klass = prefix_mapping[split[0]] # handles case where there's no suffix (and will TypeError if too # many '-') - offset = klass._from_name(*split[1:]) + offset = klass._from_name(*split[1:]) # type: ignore except (ValueError, TypeError, KeyError) as err: # bad prefix or suffix raise ValueError(libfreqs.INVALID_FREQ_ERR_MSG.format(name)) from err diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py index 4c06fea51ea8d..187446aa8a749 100644 --- a/pandas/tseries/offsets.py +++ b/pandas/tseries/offsets.py @@ -76,7 +76,22 @@ # DateOffset -class DateOffset(BaseOffset): +class OffsetMeta(type): + """ + Metaclass that allows us to pretend that all BaseOffset subclasses + inherit from DateOffset (which is needed for backward-compatibility). + """ + + @classmethod + def __instancecheck__(cls, obj) -> bool: + return isinstance(obj, BaseOffset) + + @classmethod + def __subclasscheck__(cls, obj) -> bool: + return issubclass(obj, BaseOffset) + + +class DateOffset(BaseOffset, metaclass=OffsetMeta): """ Standard kind of date increment used for a date range. @@ -275,11 +290,6 @@ def apply_index(self, i): "applied vectorized" ) - def is_anchored(self) -> bool: - # TODO: Does this make sense for the general case? It would help - # if there were a canonical docstring for what is_anchored means. - return self.n == 1 - def is_on_offset(self, dt): if self.normalize and not is_normalized(dt): return False @@ -287,13 +297,9 @@ def is_on_offset(self, dt): return True -class SingleConstructorOffset(DateOffset): - # All DateOffset subclasses (other than Tick) subclass SingleConstructorOffset - __init__ = BaseOffset.__init__ - _attributes = BaseOffset._attributes - apply_index = BaseOffset.apply_index - is_on_offset = BaseOffset.is_on_offset - _adjust_dst = True +class SingleConstructorOffset(BaseOffset): + _params = cache_readonly(BaseOffset._params.fget) + freqstr = cache_readonly(BaseOffset.freqstr.fget) @classmethod def _from_name(cls, suffix=None): @@ -2389,7 +2395,7 @@ def generate_range(start=None, end=None, periods=None, offset=BDay()): prefix_mapping = { - offset._prefix: offset + offset._prefix: offset # type: ignore for offset in [ YearBegin, # 'AS' YearEnd, # 'A'