diff --git a/pandas/_libs/tslibs/offsets.pyi b/pandas/_libs/tslibs/offsets.pyi index c3d550c7a5ba9..0390aad23d83a 100644 --- a/pandas/_libs/tslibs/offsets.pyi +++ b/pandas/_libs/tslibs/offsets.pyi @@ -81,6 +81,7 @@ class BaseOffset: @property def freqstr(self) -> str: ... def apply_index(self, dtindex: DatetimeIndex) -> DatetimeIndex: ... + def _apply(self, other): ... def _apply_array(self, dtarr) -> None: ... def rollback(self, dt: datetime) -> datetime: ... def rollforward(self, dt: datetime) -> datetime: ... diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index 565c887d1f40c..7be7381bcb4d1 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -681,6 +681,9 @@ cdef class BaseOffset: res = self._apply_array(dtindex) return type(dtindex)(res) + def _apply(self, other): + raise NotImplementedError("implemented by subclasses") + @apply_array_wraps def _apply_array(self, dtarr): raise NotImplementedError( diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index 28cde46a03758..e96e9b44112d6 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -77,7 +77,6 @@ from pandas.tseries.frequencies import get_period_alias from pandas.tseries.offsets import ( - BDay, Day, Tick, ) @@ -395,7 +394,9 @@ def _generate_range( if isinstance(freq, Tick): i8values = generate_regular_range(start, end, periods, freq) else: - xdr = generate_range(start=start, end=end, periods=periods, offset=freq) + xdr = _generate_range( + start=start, end=end, periods=periods, offset=freq + ) i8values = np.array([x.value for x in xdr], dtype=np.int64) endpoint_tz = start.tz if start is not None else end.tz @@ -2494,7 +2495,12 @@ def _maybe_localize_point(ts, is_none, is_not_none, freq, tz, ambiguous, nonexis return ts -def generate_range(start=None, end=None, periods=None, offset=BDay()): +def _generate_range( + start: Timestamp | None, + end: Timestamp | None, + periods: int | None, + offset: BaseOffset, +): """ Generates a sequence of dates corresponding to the specified time offset. Similar to dateutil.rrule except uses pandas DateOffset @@ -2502,10 +2508,10 @@ def generate_range(start=None, end=None, periods=None, offset=BDay()): Parameters ---------- - start : datetime, (default None) - end : datetime, (default None) - periods : int, (default None) - offset : DateOffset, (default BDay()) + start : Timestamp or None + end : Timestamp or None + periods : int or None + offset : DateOffset, Notes ----- @@ -2520,26 +2526,46 @@ def generate_range(start=None, end=None, periods=None, offset=BDay()): """ offset = to_offset(offset) - start = Timestamp(start) - start = start if start is not NaT else None - end = Timestamp(end) - end = end if end is not NaT else None + # Argument 1 to "Timestamp" has incompatible type "Optional[Timestamp]"; + # expected "Union[integer[Any], float, str, date, datetime64]" + start = Timestamp(start) # type: ignore[arg-type] + # Non-overlapping identity check (left operand type: "Timestamp", right + # operand type: "NaTType") + start = start if start is not NaT else None # type: ignore[comparison-overlap] + # Argument 1 to "Timestamp" has incompatible type "Optional[Timestamp]"; + # expected "Union[integer[Any], float, str, date, datetime64]" + end = Timestamp(end) # type: ignore[arg-type] + # Non-overlapping identity check (left operand type: "Timestamp", right + # operand type: "NaTType") + end = end if end is not NaT else None # type: ignore[comparison-overlap] if start and not offset.is_on_offset(start): - start = offset.rollforward(start) + # Incompatible types in assignment (expression has type "datetime", + # variable has type "Optional[Timestamp]") + start = offset.rollforward(start) # type: ignore[assignment] elif end and not offset.is_on_offset(end): - end = offset.rollback(end) + # Incompatible types in assignment (expression has type "datetime", + # variable has type "Optional[Timestamp]") + end = offset.rollback(end) # type: ignore[assignment] - if periods is None and end < start and offset.n >= 0: + # Unsupported operand types for < ("Timestamp" and "None") + if periods is None and end < start and offset.n >= 0: # type: ignore[operator] end = None periods = 0 if end is None: - end = start + (periods - 1) * offset + # error: No overload variant of "__radd__" of "BaseOffset" matches + # argument type "None" + end = start + (periods - 1) * offset # type: ignore[operator] if start is None: - start = end - (periods - 1) * offset + # error: No overload variant of "__radd__" of "BaseOffset" matches + # argument type "None" + start = end - (periods - 1) * offset # type: ignore[operator] + + start = cast(Timestamp, start) + end = cast(Timestamp, end) cur = start if offset.n >= 0: diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index 7f896c5334313..91eeee936cba1 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -172,7 +172,7 @@ class PeriodArray(dtl.DatelikeOps, libperiod.PeriodMixin): _typ = "periodarray" # ABCPeriodArray _internal_fill_value = np.int64(iNaT) _recognized_scalars = (Period,) - _is_recognized_dtype = is_period_dtype + _is_recognized_dtype = is_period_dtype # check_compatible_with checks freq match _infer_matches = ("period",) @property diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 2dfa505bc4932..f8aafa6e9cd8b 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -1917,7 +1917,7 @@ def _drop_labels_or_levels(self, keys, axis: int = 0): # Perform copy upfront and then use inplace operations below. # This ensures that we always perform exactly one copy. # ``copy`` and/or ``inplace`` options could be added in the future. - dropped = self.copy() + dropped = self.copy(deep=False) if axis == 0: # Handle dropping index levels diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 27ca114519f77..d415cbd035cd1 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -955,8 +955,6 @@ def _getitem_lowerdim(self, tup: tuple): # is equivalent. # (see the other place where we call _handle_lowerdim_multi_index_axis0) with suppress(IndexingError): - # error "_LocationIndexer" has no attribute - # "_handle_lowerdim_multi_index_axis0" return cast(_LocIndexer, self)._handle_lowerdim_multi_index_axis0(tup) tup = self._validate_key_length(tup) @@ -1013,8 +1011,6 @@ def _getitem_nested_tuple(self, tup: tuple): # DataFrame, IndexingError is not raised when slice(None,None,None) # with one row. with suppress(IndexingError): - # error "_LocationIndexer" has no attribute - # "_handle_lowerdim_multi_index_axis0" return cast(_LocIndexer, self)._handle_lowerdim_multi_index_axis0( tup ) diff --git a/pandas/core/internals/concat.py b/pandas/core/internals/concat.py index c8bc9efd824b2..ba84c8b364be7 100644 --- a/pandas/core/internals/concat.py +++ b/pandas/core/internals/concat.py @@ -455,7 +455,7 @@ def get_reindexed_values(self, empty_dtype: DtypeObj, upcasted_na) -> ArrayLike: if upcasted_na is None and self.block.dtype.kind != "V": # No upcasting is necessary fill_value = self.block.fill_value - values = self.block.get_values() + values = self.block.values else: fill_value = upcasted_na diff --git a/pandas/tests/indexes/datetimes/test_date_range.py b/pandas/tests/indexes/datetimes/test_date_range.py index 377974a918ad9..5f4f941057b55 100644 --- a/pandas/tests/indexes/datetimes/test_date_range.py +++ b/pandas/tests/indexes/datetimes/test_date_range.py @@ -34,7 +34,7 @@ offsets, ) import pandas._testing as tm -from pandas.core.arrays.datetimes import generate_range +from pandas.core.arrays.datetimes import _generate_range as generate_range START, END = datetime(2009, 1, 1), datetime(2010, 1, 1) @@ -840,27 +840,45 @@ def test_date_range_with_tz(self, tzstr): class TestGenRangeGeneration: def test_generate(self): - rng1 = list(generate_range(START, END, offset=BDay())) - rng2 = list(generate_range(START, END, offset="B")) + rng1 = list(generate_range(START, END, periods=None, offset=BDay())) + rng2 = list(generate_range(START, END, periods=None, offset="B")) assert rng1 == rng2 def test_generate_cday(self): - rng1 = list(generate_range(START, END, offset=CDay())) - rng2 = list(generate_range(START, END, offset="C")) + rng1 = list(generate_range(START, END, periods=None, offset=CDay())) + rng2 = list(generate_range(START, END, periods=None, offset="C")) assert rng1 == rng2 def test_1(self): - rng = list(generate_range(start=datetime(2009, 3, 25), periods=2)) + rng = list( + generate_range( + start=datetime(2009, 3, 25), end=None, periods=2, offset=BDay() + ) + ) expected = [datetime(2009, 3, 25), datetime(2009, 3, 26)] assert rng == expected def test_2(self): - rng = list(generate_range(start=datetime(2008, 1, 1), end=datetime(2008, 1, 3))) + rng = list( + generate_range( + start=datetime(2008, 1, 1), + end=datetime(2008, 1, 3), + periods=None, + offset=BDay(), + ) + ) expected = [datetime(2008, 1, 1), datetime(2008, 1, 2), datetime(2008, 1, 3)] assert rng == expected def test_3(self): - rng = list(generate_range(start=datetime(2008, 1, 5), end=datetime(2008, 1, 6))) + rng = list( + generate_range( + start=datetime(2008, 1, 5), + end=datetime(2008, 1, 6), + periods=None, + offset=BDay(), + ) + ) expected = [] assert rng == expected diff --git a/pandas/tests/plotting/test_series.py b/pandas/tests/plotting/test_series.py index c49354816b8b0..2c196a7b46d9c 100644 --- a/pandas/tests/plotting/test_series.py +++ b/pandas/tests/plotting/test_series.py @@ -734,7 +734,11 @@ def test_custom_business_day_freq(self): _check_plot_works(s.plot) - @pytest.mark.xfail(reason="GH#24426") + @pytest.mark.xfail( + reason="GH#24426, see also " + "github.com/pandas-dev/pandas/commit/" + "ef1bd69fa42bbed5d09dd17f08c44fc8bfc2b685#r61470674" + ) def test_plot_accessor_updates_on_inplace(self): ser = Series([1, 2, 3, 4]) _, ax = self.plt.subplots() diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index 67ad152dcab30..dc3ddc7361afd 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -902,17 +902,17 @@ def test_addsub_timedeltalike_non_nano(self, dt64, ts, td): assert result._reso == ts._reso assert result == expected - @pytest.mark.xfail(reason="tz_localize not yet implemented for non-nano") def test_addsub_offset(self, ts_tz): # specifically non-Tick offset - off = offsets.YearBegin(1) + off = offsets.YearEnd(1) result = ts_tz + off assert isinstance(result, Timestamp) assert result._reso == ts_tz._reso - # If ts_tz is ever on the last day of the year, the year would be - # incremented by one - assert result.year == ts_tz.year + if ts_tz.month == 12 and ts_tz.day == 31: + assert result.year == ts_tz.year + 1 + else: + assert result.year == ts_tz.year assert result.day == 31 assert result.month == 12 assert tz_compare(result.tz, ts_tz.tz)