From 1783a8cb57dce8d054dff3ba6957657f767bf4e3 Mon Sep 17 00:00:00 2001 From: Brock Date: Sat, 17 Oct 2020 17:17:46 -0700 Subject: [PATCH 1/4] CLN: remove method name from searchsorted excpetion message --- pandas/core/arrays/datetimelike.py | 5 ++--- pandas/tests/arrays/test_datetimelike.py | 2 +- pandas/tests/arrays/test_datetimes.py | 7 +------ pandas/tests/arrays/test_timedeltas.py | 7 +------ pandas/tests/indexes/period/test_searchsorted.py | 7 +------ pandas/tests/indexes/timedeltas/test_searchsorted.py | 2 +- 6 files changed, 7 insertions(+), 23 deletions(-) diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index eb5e5b03fe243..f8717237eef63 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -580,14 +580,13 @@ def _validate_listlike(self, value, opname: str, allow_object: bool = False): elif not type(self)._is_recognized_dtype(value.dtype): raise TypeError( - f"{opname} requires compatible dtype or scalar, " + f"value should be compatible dtype or scalar, " f"not {type(value).__name__}" ) - return value def _validate_searchsorted_value(self, value): - msg = "searchsorted requires compatible dtype or scalar" + msg = f"value should be compatible dtype or scalar, not {type(value).__name__}" if not is_list_like(value): value = self._validate_scalar(value, msg) else: diff --git a/pandas/tests/arrays/test_datetimelike.py b/pandas/tests/arrays/test_datetimelike.py index f22d958dc88e3..93a8fcf5fdbd3 100644 --- a/pandas/tests/arrays/test_datetimelike.py +++ b/pandas/tests/arrays/test_datetimelike.py @@ -407,7 +407,7 @@ def test_setitem_raises(self): def test_setitem_numeric_raises(self, arr1d, box): # We dont case e.g. int64 to our own dtype for setitem - msg = "requires compatible dtype" + msg = "value should be compatible dtype or scalar, not" with pytest.raises(TypeError, match=msg): arr1d[:2] = box([0, 1]) diff --git a/pandas/tests/arrays/test_datetimes.py b/pandas/tests/arrays/test_datetimes.py index 9f136b4979bb7..1d3dfe0ed8a1e 100644 --- a/pandas/tests/arrays/test_datetimes.py +++ b/pandas/tests/arrays/test_datetimes.py @@ -393,12 +393,7 @@ def test_searchsorted_invalid_types(self, other, index): if index: arr = pd.Index(arr) - msg = "|".join( - [ - "searchsorted requires compatible dtype or scalar", - "Unexpected type for 'value'", - ] - ) + msg = "value should be compatible dtype or scalar, not" with pytest.raises(TypeError, match=msg): arr.searchsorted(other) diff --git a/pandas/tests/arrays/test_timedeltas.py b/pandas/tests/arrays/test_timedeltas.py index b3b8f4d55e4de..8f2e863d1e6f8 100644 --- a/pandas/tests/arrays/test_timedeltas.py +++ b/pandas/tests/arrays/test_timedeltas.py @@ -133,12 +133,7 @@ def test_searchsorted_invalid_types(self, other, index): if index: arr = pd.Index(arr) - msg = "|".join( - [ - "searchsorted requires compatible dtype or scalar", - "Unexpected type for 'value'", - ] - ) + msg = "value should be compatible dtype or scalar, not" with pytest.raises(TypeError, match=msg): arr.searchsorted(other) diff --git a/pandas/tests/indexes/period/test_searchsorted.py b/pandas/tests/indexes/period/test_searchsorted.py index f2950b9f6065c..2c434489ff0d7 100644 --- a/pandas/tests/indexes/period/test_searchsorted.py +++ b/pandas/tests/indexes/period/test_searchsorted.py @@ -59,12 +59,7 @@ def test_searchsorted_invalid(self): other = np.array([0, 1], dtype=np.int64) - msg = "|".join( - [ - "searchsorted requires compatible dtype or scalar", - "Unexpected type for 'value'", - ] - ) + msg = "value should be compatible dtype or scalar, not" with pytest.raises(TypeError, match=msg): pidx.searchsorted(other) diff --git a/pandas/tests/indexes/timedeltas/test_searchsorted.py b/pandas/tests/indexes/timedeltas/test_searchsorted.py index 3cf45931cf6b7..68082c29410db 100644 --- a/pandas/tests/indexes/timedeltas/test_searchsorted.py +++ b/pandas/tests/indexes/timedeltas/test_searchsorted.py @@ -21,6 +21,6 @@ def test_searchsorted_different_argument_classes(self, klass): ) def test_searchsorted_invalid_argument_dtype(self, arg): idx = TimedeltaIndex(["1 day", "2 days", "3 days"]) - msg = "searchsorted requires compatible dtype" + msg = "value should be compatible dtype or scalar, not" with pytest.raises(TypeError, match=msg): idx.searchsorted(arg) From 9feba7d8cf25ba207df65430414d08b3a4ecfa74 Mon Sep 17 00:00:00 2001 From: Brock Date: Sun, 18 Oct 2020 07:41:58 -0700 Subject: [PATCH 2/4] standardize insert exception message --- pandas/core/arrays/datetimelike.py | 2 +- pandas/tests/indexes/datetimes/test_insert.py | 6 ++++-- pandas/tests/indexes/timedeltas/test_insert.py | 6 ++++-- pandas/tests/indexing/test_coercion.py | 6 +++--- pandas/tests/indexing/test_partial.py | 2 +- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index f8717237eef63..dd293d7881006 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -608,7 +608,7 @@ def _validate_setitem_value(self, value): return self._unbox(value, setitem=True) def _validate_insert_value(self, value): - msg = f"cannot insert {type(self).__name__} with incompatible label" + msg = f"value should be compatible dtype or scalar, not {type(value).__name__}" value = self._validate_scalar(value, msg) self._check_compatible_with(value, setitem=True) diff --git a/pandas/tests/indexes/datetimes/test_insert.py b/pandas/tests/indexes/datetimes/test_insert.py index b4f6cc3798f4f..fc6d4f7fb0e20 100644 --- a/pandas/tests/indexes/datetimes/test_insert.py +++ b/pandas/tests/indexes/datetimes/test_insert.py @@ -21,7 +21,9 @@ def test_insert_nat(self, tz, null): @pytest.mark.parametrize("tz", [None, "UTC", "US/Eastern"]) def test_insert_invalid_na(self, tz): idx = DatetimeIndex(["2017-01-01"], tz=tz) - with pytest.raises(TypeError, match="incompatible label"): + + msg = "value should be compatible dtype or scalar, not timedelta64" + with pytest.raises(TypeError, match=msg): idx.insert(0, np.timedelta64("NaT")) def test_insert_empty_preserves_freq(self, tz_naive_fixture): @@ -174,7 +176,7 @@ def test_insert_mismatched_types_raises(self, tz_aware_fixture, item): tz = tz_aware_fixture dti = date_range("2019-11-04", periods=9, freq="-1D", name=9, tz=tz) - msg = "incompatible label" + msg = "value should be compatible dtype or scalar, not " with pytest.raises(TypeError, match=msg): dti.insert(1, item) diff --git a/pandas/tests/indexes/timedeltas/test_insert.py b/pandas/tests/indexes/timedeltas/test_insert.py index 1ebc0a4b1eca0..aa7c618d5bcba 100644 --- a/pandas/tests/indexes/timedeltas/test_insert.py +++ b/pandas/tests/indexes/timedeltas/test_insert.py @@ -79,7 +79,9 @@ def test_insert_nat(self, null): def test_insert_invalid_na(self): idx = TimedeltaIndex(["4day", "1day", "2day"], name="idx") - with pytest.raises(TypeError, match="incompatible label"): + + msg = "value should be compatible dtype or scalar, not datetime64" + with pytest.raises(TypeError, match=msg): idx.insert(0, np.datetime64("NaT")) @pytest.mark.parametrize( @@ -89,7 +91,7 @@ def test_insert_mismatched_types_raises(self, item): # GH#33703 dont cast these to td64 tdi = TimedeltaIndex(["4day", "1day", "2day"], name="idx") - msg = "incompatible label" + msg = "value should be compatible dtype or scalar, not " with pytest.raises(TypeError, match=msg): tdi.insert(1, item) diff --git a/pandas/tests/indexing/test_coercion.py b/pandas/tests/indexing/test_coercion.py index 752ecd47fe089..c8d7445a60103 100644 --- a/pandas/tests/indexing/test_coercion.py +++ b/pandas/tests/indexing/test_coercion.py @@ -444,7 +444,7 @@ def test_insert_index_datetimes(self, fill_val, exp_dtype): with pytest.raises(TypeError, match=msg): obj.insert(1, pd.Timestamp("2012-01-01", tz="Asia/Tokyo")) - msg = "cannot insert DatetimeArray with incompatible label" + msg = "value should be compatible dtype or scalar, not" with pytest.raises(TypeError, match=msg): obj.insert(1, 1) @@ -461,12 +461,12 @@ def test_insert_index_timedelta64(self): ) # ToDo: must coerce to object - msg = "cannot insert TimedeltaArray with incompatible label" + msg = "value should be compatible dtype or scalar, not" with pytest.raises(TypeError, match=msg): obj.insert(1, pd.Timestamp("2012-01-01")) # ToDo: must coerce to object - msg = "cannot insert TimedeltaArray with incompatible label" + msg = "value should be compatible dtype or scalar, not" with pytest.raises(TypeError, match=msg): obj.insert(1, 1) diff --git a/pandas/tests/indexing/test_partial.py b/pandas/tests/indexing/test_partial.py index 337ec683ee745..6219fc886ed5b 100644 --- a/pandas/tests/indexing/test_partial.py +++ b/pandas/tests/indexing/test_partial.py @@ -335,7 +335,7 @@ def test_partial_set_invalid(self): df = orig.copy() # don't allow not string inserts - msg = "cannot insert DatetimeArray with incompatible label" + msg = "value should be compatible dtype or scalar, not" with pytest.raises(TypeError, match=msg): df.loc[100.0, :] = df.iloc[0] From f9e5290831224c104412d67c7a413bb23b79de71 Mon Sep 17 00:00:00 2001 From: Brock Date: Sun, 18 Oct 2020 11:00:24 -0700 Subject: [PATCH 3/4] merge/revert --- pandas/_libs/lib.pyx | 36 +++++++++++++++-- pandas/core/arrays/datetimelike.py | 7 ++-- pandas/core/groupby/groupby.py | 4 +- pandas/core/indexes/base.py | 16 ++++---- pandas/core/window/expanding.py | 4 +- pandas/tests/arrays/test_datetimelike.py | 2 +- pandas/tests/arrays/test_datetimes.py | 7 +++- pandas/tests/arrays/test_timedeltas.py | 7 +++- pandas/tests/dtypes/test_inference.py | 6 +++ pandas/tests/indexes/datetimes/test_insert.py | 6 +-- .../tests/indexes/multi/test_equivalence.py | 40 +++++++++++++++++++ .../tests/indexes/period/test_searchsorted.py | 7 +++- .../tests/indexes/timedeltas/test_insert.py | 6 +-- .../indexes/timedeltas/test_searchsorted.py | 2 +- pandas/tests/indexing/test_coercion.py | 6 +-- pandas/tests/indexing/test_partial.py | 2 +- 16 files changed, 123 insertions(+), 35 deletions(-) diff --git a/pandas/_libs/lib.pyx b/pandas/_libs/lib.pyx index 922dcd7e74aa0..f4caafb3a9fe7 100644 --- a/pandas/_libs/lib.pyx +++ b/pandas/_libs/lib.pyx @@ -1414,10 +1414,12 @@ def infer_dtype(value: object, skipna: bool = True) -> str: return "time" elif is_decimal(val): - return "decimal" + if is_decimal_array(values): + return "decimal" elif is_complex(val): - return "complex" + if is_complex_array(values): + return "complex" elif util.is_float_object(val): if is_float_array(values): @@ -1702,6 +1704,34 @@ cpdef bint is_float_array(ndarray values): return validator.validate(values) +cdef class ComplexValidator(Validator): + cdef inline bint is_value_typed(self, object value) except -1: + return ( + util.is_complex_object(value) + or (util.is_float_object(value) and is_nan(value)) + ) + + cdef inline bint is_array_typed(self) except -1: + return issubclass(self.dtype.type, np.complexfloating) + + +cdef bint is_complex_array(ndarray values): + cdef: + ComplexValidator validator = ComplexValidator(len(values), values.dtype) + return validator.validate(values) + + +cdef class DecimalValidator(Validator): + cdef inline bint is_value_typed(self, object value) except -1: + return is_decimal(value) + + +cdef bint is_decimal_array(ndarray values): + cdef: + DecimalValidator validator = DecimalValidator(len(values), values.dtype) + return validator.validate(values) + + cdef class StringValidator(Validator): cdef inline bint is_value_typed(self, object value) except -1: return isinstance(value, str) @@ -2546,8 +2576,6 @@ def fast_multiget(dict mapping, ndarray keys, default=np.nan): # kludge, for Series return np.empty(0, dtype='f8') - keys = getattr(keys, 'values', keys) - for i in range(n): val = keys[i] if val in mapping: diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index e0b2b6d05cf26..cc2c753857032 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -578,13 +578,14 @@ def _validate_listlike(self, value, opname: str, allow_object: bool = False): elif not type(self)._is_recognized_dtype(value.dtype): raise TypeError( - f"value should be compatible dtype or scalar, " + f"{opname} requires compatible dtype or scalar, " f"not {type(value).__name__}" ) + return value def _validate_searchsorted_value(self, value): - msg = f"value should be compatible dtype or scalar, not {type(value).__name__}" + msg = "searchsorted requires compatible dtype or scalar" if not is_list_like(value): value = self._validate_scalar(value, msg) else: @@ -606,7 +607,7 @@ def _validate_setitem_value(self, value): return self._unbox(value, setitem=True) def _validate_insert_value(self, value): - msg = f"value should be compatible dtype or scalar, not {type(value).__name__}" + msg = f"cannot insert {type(self).__name__} with incompatible label" value = self._validate_scalar(value, msg) self._check_compatible_with(value, setitem=True) diff --git a/pandas/core/groupby/groupby.py b/pandas/core/groupby/groupby.py index 3938adce6736a..ab01f99ba11f9 100644 --- a/pandas/core/groupby/groupby.py +++ b/pandas/core/groupby/groupby.py @@ -1196,12 +1196,12 @@ def reset_identity(values): # when the ax has duplicates # so we resort to this # GH 14776, 30667 - if ax.has_duplicates: + if ax.has_duplicates and not result.axes[self.axis].equals(ax): indexer, _ = result.index.get_indexer_non_unique(ax.values) indexer = algorithms.unique1d(indexer) result = result.take(indexer, axis=self.axis) else: - result = result.reindex(ax, axis=self.axis) + result = result.reindex(ax, axis=self.axis, copy=False) elif self.group_keys: diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 2ebf2389823e9..3713eb6da60ac 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -65,7 +65,6 @@ ) from pandas.core.dtypes.concat import concat_compat from pandas.core.dtypes.generic import ( - ABCCategorical, ABCDatetimeIndex, ABCMultiIndex, ABCPandasArray, @@ -83,6 +82,7 @@ from pandas.core.arrays.datetimes import tz_to_dtype, validate_tz_from_dtype from pandas.core.base import IndexOpsMixin, PandasObject import pandas.core.common as com +from pandas.core.construction import extract_array from pandas.core.indexers import deprecate_ndim_indexing from pandas.core.indexes.frozen import FrozenList from pandas.core.ops import get_op_result_name @@ -5376,11 +5376,13 @@ def _cmp_method(self, other, op): if len(self) != len(other): raise ValueError("Lengths must match to compare") - if is_object_dtype(self.dtype) and isinstance(other, ABCCategorical): - left = type(other)(self._values, dtype=other.dtype) - return op(left, other) - elif is_object_dtype(self.dtype) and isinstance(other, ExtensionArray): - # e.g. PeriodArray + if not isinstance(other, ABCMultiIndex): + other = extract_array(other, extract_numpy=True) + else: + other = np.asarray(other) + + if is_object_dtype(self.dtype) and isinstance(other, ExtensionArray): + # e.g. PeriodArray, Categorical with np.errstate(all="ignore"): result = op(self._values, other) @@ -5395,7 +5397,7 @@ def _cmp_method(self, other, op): else: with np.errstate(all="ignore"): - result = ops.comparison_op(self._values, np.asarray(other), op) + result = ops.comparison_op(self._values, other, op) return result diff --git a/pandas/core/window/expanding.py b/pandas/core/window/expanding.py index 0505913aaf8cc..94875ba86db65 100644 --- a/pandas/core/window/expanding.py +++ b/pandas/core/window/expanding.py @@ -139,8 +139,8 @@ def aggregate(self, func, *args, **kwargs): @Substitution(name="expanding") @Appender(_shared_docs["count"]) - def count(self, **kwargs): - return super().count(**kwargs) + def count(self): + return super().count() @Substitution(name="expanding") @Appender(_shared_docs["apply"]) diff --git a/pandas/tests/arrays/test_datetimelike.py b/pandas/tests/arrays/test_datetimelike.py index 5be85793485dc..a961cf14b2e5c 100644 --- a/pandas/tests/arrays/test_datetimelike.py +++ b/pandas/tests/arrays/test_datetimelike.py @@ -416,7 +416,7 @@ def test_setitem_raises(self): def test_setitem_numeric_raises(self, arr1d, box): # We dont case e.g. int64 to our own dtype for setitem - msg = "value should be compatible dtype or scalar, not" + msg = "requires compatible dtype" with pytest.raises(TypeError, match=msg): arr1d[:2] = box([0, 1]) diff --git a/pandas/tests/arrays/test_datetimes.py b/pandas/tests/arrays/test_datetimes.py index 1d3dfe0ed8a1e..9f136b4979bb7 100644 --- a/pandas/tests/arrays/test_datetimes.py +++ b/pandas/tests/arrays/test_datetimes.py @@ -393,7 +393,12 @@ def test_searchsorted_invalid_types(self, other, index): if index: arr = pd.Index(arr) - msg = "value should be compatible dtype or scalar, not" + msg = "|".join( + [ + "searchsorted requires compatible dtype or scalar", + "Unexpected type for 'value'", + ] + ) with pytest.raises(TypeError, match=msg): arr.searchsorted(other) diff --git a/pandas/tests/arrays/test_timedeltas.py b/pandas/tests/arrays/test_timedeltas.py index 8f2e863d1e6f8..b3b8f4d55e4de 100644 --- a/pandas/tests/arrays/test_timedeltas.py +++ b/pandas/tests/arrays/test_timedeltas.py @@ -133,7 +133,12 @@ def test_searchsorted_invalid_types(self, other, index): if index: arr = pd.Index(arr) - msg = "value should be compatible dtype or scalar, not" + msg = "|".join( + [ + "searchsorted requires compatible dtype or scalar", + "Unexpected type for 'value'", + ] + ) with pytest.raises(TypeError, match=msg): arr.searchsorted(other) diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index c6c54ccb357d5..7fa83eeac8400 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -709,6 +709,9 @@ def test_decimals(self): result = lib.infer_dtype(arr, skipna=True) assert result == "mixed" + result = lib.infer_dtype(arr[::-1], skipna=True) + assert result == "mixed" + arr = np.array([Decimal(1), Decimal("NaN"), Decimal(3)]) result = lib.infer_dtype(arr, skipna=True) assert result == "decimal" @@ -729,6 +732,9 @@ def test_complex(self, skipna): result = lib.infer_dtype(arr, skipna=skipna) assert result == "mixed" + result = lib.infer_dtype(arr[::-1], skipna=skipna) + assert result == "mixed" + # gets cast to complex on array construction arr = np.array([1, np.nan, 1 + 1j]) result = lib.infer_dtype(arr, skipna=skipna) diff --git a/pandas/tests/indexes/datetimes/test_insert.py b/pandas/tests/indexes/datetimes/test_insert.py index fc6d4f7fb0e20..b4f6cc3798f4f 100644 --- a/pandas/tests/indexes/datetimes/test_insert.py +++ b/pandas/tests/indexes/datetimes/test_insert.py @@ -21,9 +21,7 @@ def test_insert_nat(self, tz, null): @pytest.mark.parametrize("tz", [None, "UTC", "US/Eastern"]) def test_insert_invalid_na(self, tz): idx = DatetimeIndex(["2017-01-01"], tz=tz) - - msg = "value should be compatible dtype or scalar, not timedelta64" - with pytest.raises(TypeError, match=msg): + with pytest.raises(TypeError, match="incompatible label"): idx.insert(0, np.timedelta64("NaT")) def test_insert_empty_preserves_freq(self, tz_naive_fixture): @@ -176,7 +174,7 @@ def test_insert_mismatched_types_raises(self, tz_aware_fixture, item): tz = tz_aware_fixture dti = date_range("2019-11-04", periods=9, freq="-1D", name=9, tz=tz) - msg = "value should be compatible dtype or scalar, not " + msg = "incompatible label" with pytest.raises(TypeError, match=msg): dti.insert(1, item) diff --git a/pandas/tests/indexes/multi/test_equivalence.py b/pandas/tests/indexes/multi/test_equivalence.py index 184cedea7dc5c..fec1c0e44cd9f 100644 --- a/pandas/tests/indexes/multi/test_equivalence.py +++ b/pandas/tests/indexes/multi/test_equivalence.py @@ -84,6 +84,46 @@ def test_equals_op(idx): tm.assert_series_equal(series_a == item, Series(expected3)) +def test_compare_tuple(): + # GH#21517 + mi = MultiIndex.from_product([[1, 2]] * 2) + + all_false = np.array([False, False, False, False]) + + result = mi == mi[0] + expected = np.array([True, False, False, False]) + tm.assert_numpy_array_equal(result, expected) + + result = mi != mi[0] + tm.assert_numpy_array_equal(result, ~expected) + + result = mi < mi[0] + tm.assert_numpy_array_equal(result, all_false) + + result = mi <= mi[0] + tm.assert_numpy_array_equal(result, expected) + + result = mi > mi[0] + tm.assert_numpy_array_equal(result, ~expected) + + result = mi >= mi[0] + tm.assert_numpy_array_equal(result, ~all_false) + + +def test_compare_tuple_strs(): + # GH#34180 + + mi = MultiIndex.from_tuples([("a", "b"), ("b", "c"), ("c", "a")]) + + result = mi == ("c", "a") + expected = np.array([False, False, True]) + tm.assert_numpy_array_equal(result, expected) + + result = mi == ("c",) + expected = np.array([False, False, False]) + tm.assert_numpy_array_equal(result, expected) + + def test_equals_multi(idx): assert idx.equals(idx) assert not idx.equals(idx.values) diff --git a/pandas/tests/indexes/period/test_searchsorted.py b/pandas/tests/indexes/period/test_searchsorted.py index 2c434489ff0d7..f2950b9f6065c 100644 --- a/pandas/tests/indexes/period/test_searchsorted.py +++ b/pandas/tests/indexes/period/test_searchsorted.py @@ -59,7 +59,12 @@ def test_searchsorted_invalid(self): other = np.array([0, 1], dtype=np.int64) - msg = "value should be compatible dtype or scalar, not" + msg = "|".join( + [ + "searchsorted requires compatible dtype or scalar", + "Unexpected type for 'value'", + ] + ) with pytest.raises(TypeError, match=msg): pidx.searchsorted(other) diff --git a/pandas/tests/indexes/timedeltas/test_insert.py b/pandas/tests/indexes/timedeltas/test_insert.py index aa7c618d5bcba..1ebc0a4b1eca0 100644 --- a/pandas/tests/indexes/timedeltas/test_insert.py +++ b/pandas/tests/indexes/timedeltas/test_insert.py @@ -79,9 +79,7 @@ def test_insert_nat(self, null): def test_insert_invalid_na(self): idx = TimedeltaIndex(["4day", "1day", "2day"], name="idx") - - msg = "value should be compatible dtype or scalar, not datetime64" - with pytest.raises(TypeError, match=msg): + with pytest.raises(TypeError, match="incompatible label"): idx.insert(0, np.datetime64("NaT")) @pytest.mark.parametrize( @@ -91,7 +89,7 @@ def test_insert_mismatched_types_raises(self, item): # GH#33703 dont cast these to td64 tdi = TimedeltaIndex(["4day", "1day", "2day"], name="idx") - msg = "value should be compatible dtype or scalar, not " + msg = "incompatible label" with pytest.raises(TypeError, match=msg): tdi.insert(1, item) diff --git a/pandas/tests/indexes/timedeltas/test_searchsorted.py b/pandas/tests/indexes/timedeltas/test_searchsorted.py index 68082c29410db..3cf45931cf6b7 100644 --- a/pandas/tests/indexes/timedeltas/test_searchsorted.py +++ b/pandas/tests/indexes/timedeltas/test_searchsorted.py @@ -21,6 +21,6 @@ def test_searchsorted_different_argument_classes(self, klass): ) def test_searchsorted_invalid_argument_dtype(self, arg): idx = TimedeltaIndex(["1 day", "2 days", "3 days"]) - msg = "value should be compatible dtype or scalar, not" + msg = "searchsorted requires compatible dtype" with pytest.raises(TypeError, match=msg): idx.searchsorted(arg) diff --git a/pandas/tests/indexing/test_coercion.py b/pandas/tests/indexing/test_coercion.py index c8d7445a60103..752ecd47fe089 100644 --- a/pandas/tests/indexing/test_coercion.py +++ b/pandas/tests/indexing/test_coercion.py @@ -444,7 +444,7 @@ def test_insert_index_datetimes(self, fill_val, exp_dtype): with pytest.raises(TypeError, match=msg): obj.insert(1, pd.Timestamp("2012-01-01", tz="Asia/Tokyo")) - msg = "value should be compatible dtype or scalar, not" + msg = "cannot insert DatetimeArray with incompatible label" with pytest.raises(TypeError, match=msg): obj.insert(1, 1) @@ -461,12 +461,12 @@ def test_insert_index_timedelta64(self): ) # ToDo: must coerce to object - msg = "value should be compatible dtype or scalar, not" + msg = "cannot insert TimedeltaArray with incompatible label" with pytest.raises(TypeError, match=msg): obj.insert(1, pd.Timestamp("2012-01-01")) # ToDo: must coerce to object - msg = "value should be compatible dtype or scalar, not" + msg = "cannot insert TimedeltaArray with incompatible label" with pytest.raises(TypeError, match=msg): obj.insert(1, 1) diff --git a/pandas/tests/indexing/test_partial.py b/pandas/tests/indexing/test_partial.py index 6219fc886ed5b..337ec683ee745 100644 --- a/pandas/tests/indexing/test_partial.py +++ b/pandas/tests/indexing/test_partial.py @@ -335,7 +335,7 @@ def test_partial_set_invalid(self): df = orig.copy() # don't allow not string inserts - msg = "value should be compatible dtype or scalar, not" + msg = "cannot insert DatetimeArray with incompatible label" with pytest.raises(TypeError, match=msg): df.loc[100.0, :] = df.iloc[0] From 0ebbee5ecf5132b256b3bf0622a88695982a525f Mon Sep 17 00:00:00 2001 From: Brock Date: Sun, 18 Oct 2020 15:17:55 -0700 Subject: [PATCH 4/4] CLN: consolidate exception messages in datetimelike --- pandas/core/arrays/datetimelike.py | 15 +++++++-------- pandas/core/indexes/datetimelike.py | 4 +--- pandas/tests/arrays/test_datetimelike.py | 5 ++++- pandas/tests/arrays/test_datetimes.py | 2 +- pandas/tests/arrays/test_timedeltas.py | 2 +- pandas/tests/indexes/period/test_searchsorted.py | 2 +- .../tests/indexes/timedeltas/test_searchsorted.py | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index cc2c753857032..de0a246861961 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -444,7 +444,7 @@ def _validate_comparison_value(self, other, opname: str): else: try: - other = self._validate_listlike(other, opname, allow_object=True) + other = self._validate_listlike(other, allow_object=True) self._check_compatible_with(other) except TypeError as err: if is_object_dtype(getattr(other, "dtype", None)): @@ -548,7 +548,7 @@ def _validate_scalar(self, value, msg: Optional[str] = None): return value - def _validate_listlike(self, value, opname: str, allow_object: bool = False): + def _validate_listlike(self, value, allow_object: bool = False): if isinstance(value, type(self)): return value @@ -578,10 +578,9 @@ def _validate_listlike(self, value, opname: str, allow_object: bool = False): elif not type(self)._is_recognized_dtype(value.dtype): raise TypeError( - f"{opname} requires compatible dtype or scalar, " - f"not {type(value).__name__}" + f"value should be a '{self._scalar_type.__name__}', 'NaT', " + f"or array of those. Got '{type(value).__name__}' instead." ) - return value def _validate_searchsorted_value(self, value): @@ -589,7 +588,7 @@ def _validate_searchsorted_value(self, value): if not is_list_like(value): value = self._validate_scalar(value, msg) else: - value = self._validate_listlike(value, "searchsorted") + value = self._validate_listlike(value) rv = self._unbox(value) return self._rebox_native(rv) @@ -600,7 +599,7 @@ def _validate_setitem_value(self, value): f"or array of those. Got '{type(value).__name__}' instead." ) if is_list_like(value): - value = self._validate_listlike(value, "setitem") + value = self._validate_listlike(value) else: value = self._validate_scalar(value, msg) @@ -622,7 +621,7 @@ def _validate_where_value(self, other): if not is_list_like(other): other = self._validate_scalar(other, msg) else: - other = self._validate_listlike(other, "where") + other = self._validate_listlike(other) return self._unbox(other, setitem=True) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 017dc6527944a..19e41b649f71a 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -668,9 +668,7 @@ def _wrap_joined_index(self, joined: np.ndarray, other): @doc(Index._convert_arr_indexer) def _convert_arr_indexer(self, keyarr): try: - return self._data._validate_listlike( - keyarr, "convert_arr_indexer", allow_object=True - ) + return self._data._validate_listlike(keyarr, allow_object=True) except (ValueError, TypeError): return com.asarray_tuplesafe(keyarr) diff --git a/pandas/tests/arrays/test_datetimelike.py b/pandas/tests/arrays/test_datetimelike.py index a961cf14b2e5c..ed7c7c31c6b8d 100644 --- a/pandas/tests/arrays/test_datetimelike.py +++ b/pandas/tests/arrays/test_datetimelike.py @@ -416,7 +416,10 @@ def test_setitem_raises(self): def test_setitem_numeric_raises(self, arr1d, box): # We dont case e.g. int64 to our own dtype for setitem - msg = "requires compatible dtype" + msg = ( + f"value should be a '{arr1d._scalar_type.__name__}', " + "'NaT', or array of those. Got" + ) with pytest.raises(TypeError, match=msg): arr1d[:2] = box([0, 1]) diff --git a/pandas/tests/arrays/test_datetimes.py b/pandas/tests/arrays/test_datetimes.py index 9f136b4979bb7..78721fc2fe1c1 100644 --- a/pandas/tests/arrays/test_datetimes.py +++ b/pandas/tests/arrays/test_datetimes.py @@ -396,7 +396,7 @@ def test_searchsorted_invalid_types(self, other, index): msg = "|".join( [ "searchsorted requires compatible dtype or scalar", - "Unexpected type for 'value'", + "value should be a 'Timestamp', 'NaT', or array of those. Got", ] ) with pytest.raises(TypeError, match=msg): diff --git a/pandas/tests/arrays/test_timedeltas.py b/pandas/tests/arrays/test_timedeltas.py index b3b8f4d55e4de..75d6f7d276518 100644 --- a/pandas/tests/arrays/test_timedeltas.py +++ b/pandas/tests/arrays/test_timedeltas.py @@ -136,7 +136,7 @@ def test_searchsorted_invalid_types(self, other, index): msg = "|".join( [ "searchsorted requires compatible dtype or scalar", - "Unexpected type for 'value'", + "value should be a 'Timedelta', 'NaT', or array of those. Got", ] ) with pytest.raises(TypeError, match=msg): diff --git a/pandas/tests/indexes/period/test_searchsorted.py b/pandas/tests/indexes/period/test_searchsorted.py index f2950b9f6065c..6ffdbbfcd2ce6 100644 --- a/pandas/tests/indexes/period/test_searchsorted.py +++ b/pandas/tests/indexes/period/test_searchsorted.py @@ -62,7 +62,7 @@ def test_searchsorted_invalid(self): msg = "|".join( [ "searchsorted requires compatible dtype or scalar", - "Unexpected type for 'value'", + "value should be a 'Period', 'NaT', or array of those. Got", ] ) with pytest.raises(TypeError, match=msg): diff --git a/pandas/tests/indexes/timedeltas/test_searchsorted.py b/pandas/tests/indexes/timedeltas/test_searchsorted.py index 3cf45931cf6b7..e3b52058469f0 100644 --- a/pandas/tests/indexes/timedeltas/test_searchsorted.py +++ b/pandas/tests/indexes/timedeltas/test_searchsorted.py @@ -21,6 +21,6 @@ def test_searchsorted_different_argument_classes(self, klass): ) def test_searchsorted_invalid_argument_dtype(self, arg): idx = TimedeltaIndex(["1 day", "2 days", "3 days"]) - msg = "searchsorted requires compatible dtype" + msg = "value should be a 'Timedelta', 'NaT', or array of those. Got" with pytest.raises(TypeError, match=msg): idx.searchsorted(arg)