From fbe3800189f48ab597d8795a91ba69a4c221e277 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sun, 5 Jan 2020 17:10:08 -0800 Subject: [PATCH 1/3] Make DTA _check_compatible_with less strict by default --- pandas/core/arrays/datetimelike.py | 9 ++++++--- pandas/core/arrays/datetimes.py | 8 +++++--- pandas/core/arrays/period.py | 2 +- pandas/core/arrays/timedeltas.py | 2 +- pandas/tests/arrays/test_datetimes.py | 2 +- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 2bdd9acaeb70f..7622730a615c4 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -109,7 +109,7 @@ def _unbox_scalar(self, value: Union[Period, Timestamp, Timedelta, NaTType]) -> raise AbstractMethodError(self) def _check_compatible_with( - self, other: Union[Period, Timestamp, Timedelta, NaTType] + self, other: Union[Period, Timestamp, Timedelta, NaTType], strict: bool = False ) -> None: """ Verify that `self` and `other` are compatible. @@ -123,6 +123,9 @@ def _check_compatible_with( Parameters ---------- other + strict : bool, default False + For __setitem__ we may have stricter compatiblity resrictions than + for comparisons. Raises ------ @@ -500,10 +503,10 @@ def __setitem__( return value = type(self)._from_sequence(value, dtype=self.dtype) - self._check_compatible_with(value) + self._check_compatible_with(value, strict=True) value = value.asi8 elif isinstance(value, self._scalar_type): - self._check_compatible_with(value) + self._check_compatible_with(value, strict=True) value = self._unbox_scalar(value) elif is_valid_nat_for_dtype(value, self.dtype): value = iNaT diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index cc54fb5e5af13..c37a7624e2e89 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -545,11 +545,13 @@ def _unbox_scalar(self, value): def _scalar_from_string(self, value): return Timestamp(value, tz=self.tz) - def _check_compatible_with(self, other): + def _check_compatible_with(self, other, strict: bool = False): if other is NaT: return - if not timezones.tz_compare(self.tz, other.tz): - raise ValueError(f"Timezones don't match. '{self.tz} != {other.tz}'") + self._assert_tzawareness_compat(other) + if strict: + if not timezones.tz_compare(self.tz, other.tz): + raise ValueError(f"Timezones don't match. '{self.tz} != {other.tz}'") def _maybe_clear_freq(self): self._freq = None diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index 6b9c7f4e1eb38..cdcf46b892299 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -338,7 +338,7 @@ def _unbox_scalar(self, value: Union[Period, NaTType]) -> int: def _scalar_from_string(self, value: str) -> Period: return Period(value, freq=self.freq) - def _check_compatible_with(self, other): + def _check_compatible_with(self, other, strict: bool = False): if other is NaT: return if self.freqstr != other.freqstr: diff --git a/pandas/core/arrays/timedeltas.py b/pandas/core/arrays/timedeltas.py index 1874517f0f2e4..54e27eb0ad719 100644 --- a/pandas/core/arrays/timedeltas.py +++ b/pandas/core/arrays/timedeltas.py @@ -349,7 +349,7 @@ def _unbox_scalar(self, value): def _scalar_from_string(self, value): return Timedelta(value) - def _check_compatible_with(self, other): + def _check_compatible_with(self, other, strict: bool = False): # we don't have anything to validate. pass diff --git a/pandas/tests/arrays/test_datetimes.py b/pandas/tests/arrays/test_datetimes.py index bca629ae32270..b5f9c8957c2b8 100644 --- a/pandas/tests/arrays/test_datetimes.py +++ b/pandas/tests/arrays/test_datetimes.py @@ -173,7 +173,7 @@ def test_tz_setter_raises(self): def test_setitem_different_tz_raises(self): data = np.array([1, 2, 3], dtype="M8[ns]") arr = DatetimeArray(data, copy=False, dtype=DatetimeTZDtype(tz="US/Central")) - with pytest.raises(ValueError, match="None"): + with pytest.raises(TypeError, match="Cannot compare tz-naive and tz-aware"): arr[0] = pd.Timestamp("2000") with pytest.raises(ValueError, match="US/Central"): From 4527ab9bf29f18e44cb41edcb876de671a1dee92 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 6 Jan 2020 07:35:22 -0800 Subject: [PATCH 2/3] rename strict->setitem --- pandas/core/arrays/datetimelike.py | 8 ++++---- pandas/core/arrays/datetimes.py | 2 +- pandas/core/arrays/period.py | 2 +- pandas/core/arrays/timedeltas.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 7622730a615c4..b9a6daf7c630a 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -109,7 +109,7 @@ def _unbox_scalar(self, value: Union[Period, Timestamp, Timedelta, NaTType]) -> raise AbstractMethodError(self) def _check_compatible_with( - self, other: Union[Period, Timestamp, Timedelta, NaTType], strict: bool = False + self, other: Union[Period, Timestamp, Timedelta, NaTType], setitem: bool = False ) -> None: """ Verify that `self` and `other` are compatible. @@ -123,7 +123,7 @@ def _check_compatible_with( Parameters ---------- other - strict : bool, default False + setitem : bool, default False For __setitem__ we may have stricter compatiblity resrictions than for comparisons. @@ -503,10 +503,10 @@ def __setitem__( return value = type(self)._from_sequence(value, dtype=self.dtype) - self._check_compatible_with(value, strict=True) + self._check_compatible_with(value, setitem=True) value = value.asi8 elif isinstance(value, self._scalar_type): - self._check_compatible_with(value, strict=True) + self._check_compatible_with(value, setitem=True) value = self._unbox_scalar(value) elif is_valid_nat_for_dtype(value, self.dtype): value = iNaT diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index 788a5b1ddcfa1..18a800e911cc4 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -554,7 +554,7 @@ def _unbox_scalar(self, value): def _scalar_from_string(self, value): return Timestamp(value, tz=self.tz) - def _check_compatible_with(self, other, strict: bool = False): + def _check_compatible_with(self, other, setitem: bool = False): if other is NaT: return self._assert_tzawareness_compat(other) diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index 89d936e9c8d49..a0e806fb8f04c 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -341,7 +341,7 @@ def _unbox_scalar(self, value: Union[Period, NaTType]) -> int: def _scalar_from_string(self, value: str) -> Period: return Period(value, freq=self.freq) - def _check_compatible_with(self, other, strict: bool = False): + def _check_compatible_with(self, other, setitem: bool = False): if other is NaT: return if self.freqstr != other.freqstr: diff --git a/pandas/core/arrays/timedeltas.py b/pandas/core/arrays/timedeltas.py index 60646dd50a332..39ef9414564e7 100644 --- a/pandas/core/arrays/timedeltas.py +++ b/pandas/core/arrays/timedeltas.py @@ -357,7 +357,7 @@ def _unbox_scalar(self, value): def _scalar_from_string(self, value): return Timedelta(value) - def _check_compatible_with(self, other, strict: bool = False): + def _check_compatible_with(self, other, setitem: bool = False): # we don't have anything to validate. pass From 376545c021cf3e2875aaa9c5b515792ee8c0d17e Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 6 Jan 2020 08:04:13 -0800 Subject: [PATCH 3/3] fixup --- pandas/core/arrays/datetimes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index 18a800e911cc4..9c88f4f8aa905 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -558,7 +558,8 @@ def _check_compatible_with(self, other, setitem: bool = False): if other is NaT: return self._assert_tzawareness_compat(other) - if strict: + if setitem: + # Stricter check for setitem vs comparison methods if not timezones.tz_compare(self.tz, other.tz): raise ValueError(f"Timezones don't match. '{self.tz} != {other.tz}'")