Skip to content

Commit

Permalink
Make pd.Period immutable (#17239)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbrockmendel authored and jreback committed Aug 24, 2017
1 parent d45e12b commit 6993c1b
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 6 deletions.
2 changes: 2 additions & 0 deletions doc/source/whatsnew/v0.21.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ Other API Changes
- Moved definition of ``MergeError`` to the ``pandas.errors`` module.
- The signature of :func:`Series.set_axis` and :func:`DataFrame.set_axis` has been changed from ``set_axis(axis, labels)`` to ``set_axis(labels, axis=0)``, for consistency with the rest of the API. The old signature is deprecated and will show a ``FutureWarning`` (:issue:`14636`)
- :func:`Series.argmin` and :func:`Series.argmax` will now raise a ``TypeError`` when used with ``object`` dtypes, instead of a ``ValueError`` (:issue:`13595`)
- :class:`Period` is now immutable, and will now raise an ``AttributeError`` when a user tries to assign a new value to the ``ordinal`` or ``freq`` attributes (:issue:`17116`).


.. _whatsnew_0210.deprecations:

Expand Down
17 changes: 11 additions & 6 deletions pandas/_libs/period.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ from datetime cimport (
PANDAS_FR_ns,
INT32_MIN)


cimport util, lib

from lib cimport is_null_datetimelike, is_period
from pandas._libs import tslib, lib
from pandas._libs.tslib import (Timedelta, Timestamp, iNaT,
Expand Down Expand Up @@ -668,13 +670,17 @@ class IncompatibleFrequency(ValueError):

cdef class _Period(object):

cdef public:
cdef readonly:
int64_t ordinal
object freq

_comparables = ['name', 'freqstr']
_typ = 'period'

def __cinit__(self, ordinal, freq):
self.ordinal = ordinal
self.freq = freq

@classmethod
def _maybe_convert_freq(cls, object freq):

Expand All @@ -698,9 +704,8 @@ cdef class _Period(object):
if ordinal == iNaT:
return NaT
else:
self = _Period.__new__(cls)
self.ordinal = ordinal
self.freq = cls._maybe_convert_freq(freq)
freq = cls._maybe_convert_freq(freq)
self = _Period.__new__(cls, ordinal, freq)
return self

def __richcmp__(self, other, op):
Expand Down Expand Up @@ -752,7 +757,7 @@ cdef class _Period(object):
def __add__(self, other):
if isinstance(self, Period):
if isinstance(other, (timedelta, np.timedelta64,
offsets.Tick, offsets.DateOffset,
offsets.DateOffset,
Timedelta)):
return self._add_delta(other)
elif other is NaT:
Expand All @@ -770,7 +775,7 @@ cdef class _Period(object):
def __sub__(self, other):
if isinstance(self, Period):
if isinstance(other, (timedelta, np.timedelta64,
offsets.Tick, offsets.DateOffset,
offsets.DateOffset,
Timedelta)):
neg_other = -other
return self + neg_other
Expand Down
11 changes: 11 additions & 0 deletions pandas/tests/scalar/test_period.py
Original file line number Diff line number Diff line change
Expand Up @@ -1406,3 +1406,14 @@ def test_period_ops_offset(self):

with tm.assert_raises_regex(period.IncompatibleFrequency, msg):
p - offsets.Hour(2)


def test_period_immutable():
# see gh-17116
per = pd.Period('2014Q1')
with pytest.raises(AttributeError):
per.ordinal = 14

freq = per.freq
with pytest.raises(AttributeError):
per.freq = 2 * freq

0 comments on commit 6993c1b

Please sign in to comment.