From 0dae2b3fc12c643af83f6b1f3a03521a0f896510 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Tue, 5 Mar 2019 14:14:36 -0600 Subject: [PATCH 1/2] BUG: Handle readonly arrays in period_array Closes #25403 --- doc/source/whatsnew/v0.24.2.rst | 1 + pandas/core/arrays/period.py | 6 ++++++ pandas/tests/arrays/test_period.py | 16 ++++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index e80b1060e867d..7cc30d75937f9 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -30,6 +30,7 @@ Fixed Regressions - Fixed regression in subtraction between :class:`Series` objects with ``datetime64[ns]`` dtype incorrectly raising ``OverflowError`` when the `Series` on the right contains null values (:issue:`25317`) - Fixed regression in :class:`TimedeltaIndex` where `np.sum(index)` incorrectly returned a zero-dimensional object instead of a scalar (:issue:`25282`) - Fixed regression in ``IntervalDtype`` construction where passing an incorrect string with 'Interval' as a prefix could result in a ``RecursionError``. (:issue:`25338`) +- Fixed regression in creating a period-dtype array from a read-only NumPy array of period objects. (:issue:`25403`) - Fixed regression in :class:`Categorical`, where constructing it from a categorical ``Series`` and an explicit ``categories=`` that differed from that in the ``Series`` created an invalid object which could trigger segfaults. (:issue:`25318`) diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index 3ddceb8c2839d..3686855a2ee0f 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -196,8 +196,14 @@ def _from_sequence(cls, scalars, dtype=None, copy=False): if copy: periods = periods.copy() + # Support readonly `scalars`. + # TODO: remove flag unsetting / setting once Cython supports + # const object. + writeable = periods.flags.writeable + periods.setflags(write=True) freq = freq or libperiod.extract_freq(periods) ordinals = libperiod.extract_ordinals(periods, freq) + periods.setflags(write=writeable) return cls(ordinals, freq=freq) @classmethod diff --git a/pandas/tests/arrays/test_period.py b/pandas/tests/arrays/test_period.py index affe3b3854490..99255d819d28e 100644 --- a/pandas/tests/arrays/test_period.py +++ b/pandas/tests/arrays/test_period.py @@ -41,6 +41,22 @@ def test_period_array_ok(data, freq, expected): tm.assert_numpy_array_equal(result, expected) +def test_period_array_readonly_object(): + # https://github.com/pandas-dev/pandas/issues/25403 + pa = period_array([pd.Period('2019-01-01')]) + arr = np.asarray(pa, dtype='object') + arr.setflags(write=False) + + result = period_array(arr) + tm.assert_period_array_equal(result, pa) + + result = pd.Series(arr) + tm.assert_series_equal(result, pd.Series(pa)) + + result = pd.DataFrame({"A": arr}) + tm.assert_frame_equal(result, pd.DataFrame({"A": pa})) + + def test_from_datetime64_freq_changes(): # https://github.com/pandas-dev/pandas/issues/23438 arr = pd.date_range("2017", periods=3, freq="D") From 60657ce3b0de4a35c796cf17f7b98cc8e8f43d77 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Tue, 5 Mar 2019 14:53:48 -0600 Subject: [PATCH 2/2] fixup --- pandas/_libs/tslibs/period.pyx | 8 ++++++-- pandas/core/arrays/period.py | 6 ------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/pandas/_libs/tslibs/period.pyx b/pandas/_libs/tslibs/period.pyx index a5a50ea59753d..c8eaa2cfd85c2 100644 --- a/pandas/_libs/tslibs/period.pyx +++ b/pandas/_libs/tslibs/period.pyx @@ -1438,7 +1438,9 @@ cdef accessor _get_accessor_func(int code): @cython.wraparound(False) @cython.boundscheck(False) -def extract_ordinals(object[:] values, freq): +def extract_ordinals(ndarray[object] values, freq): + # TODO: Change type to const object[:] when Cython supports that. + cdef: Py_ssize_t i, n = len(values) int64_t[:] ordinals = np.empty(n, dtype=np.int64) @@ -1472,7 +1474,9 @@ def extract_ordinals(object[:] values, freq): return ordinals.base # .base to access underlying np.ndarray -def extract_freq(object[:] values): +def extract_freq(ndarray[object] values): + # TODO: Change type to const object[:] when Cython supports that. + cdef: Py_ssize_t i, n = len(values) object p diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index 3686855a2ee0f..3ddceb8c2839d 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -196,14 +196,8 @@ def _from_sequence(cls, scalars, dtype=None, copy=False): if copy: periods = periods.copy() - # Support readonly `scalars`. - # TODO: remove flag unsetting / setting once Cython supports - # const object. - writeable = periods.flags.writeable - periods.setflags(write=True) freq = freq or libperiod.extract_freq(periods) ordinals = libperiod.extract_ordinals(periods, freq) - periods.setflags(write=writeable) return cls(ordinals, freq=freq) @classmethod