diff --git a/doc/source/whatsnew/v2.0.0.rst b/doc/source/whatsnew/v2.0.0.rst index b7995dca0a825..f1442809dba50 100644 --- a/doc/source/whatsnew/v2.0.0.rst +++ b/doc/source/whatsnew/v2.0.0.rst @@ -235,6 +235,7 @@ Other API changes - :func:`pandas.api.dtypes.is_string_dtype` now only returns ``True`` for array-likes with ``dtype=object`` when the elements are inferred to be strings (:issue:`15585`) - Passing a sequence containing ``datetime`` objects and ``date`` objects to :class:`Series` constructor will return with ``object`` dtype instead of ``datetime64[ns]`` dtype, consistent with :class:`Index` behavior (:issue:`49341`) - Passing strings that cannot be parsed as datetimes to :class:`Series` or :class:`DataFrame` with ``dtype="datetime64[ns]"`` will raise instead of silently ignoring the keyword and returning ``object`` dtype (:issue:`24435`) +- Passing a sequence containing a type that cannot be converted to :class:`Timedelta` to :func:`to_timedelta` or to the :class:`Series` or :class:`DataFrame` constructor with ``dtype="timedelta64[ns]"`` or to :class:`TimedeltaIndex` now raises ``TypeError`` instead of ``ValueError`` (:issue:`49525`) - .. --------------------------------------------------------------------------- diff --git a/pandas/_libs/tslibs/timedeltas.pyx b/pandas/_libs/tslibs/timedeltas.pyx index f3de67b705d4d..d6ddaee732f47 100644 --- a/pandas/_libs/tslibs/timedeltas.pyx +++ b/pandas/_libs/tslibs/timedeltas.pyx @@ -364,7 +364,7 @@ cdef convert_to_timedelta64(object ts, str unit): if PyDelta_Check(ts): ts = np.timedelta64(delta_to_nanoseconds(ts), "ns") elif not is_timedelta64_object(ts): - raise ValueError(f"Invalid type for timedelta scalar: {type(ts)}") + raise TypeError(f"Invalid type for timedelta scalar: {type(ts)}") return ts.astype("timedelta64[ns]") diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 72391ca3282c9..6743768852082 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -41,7 +41,6 @@ IncompatibleFrequency, OutOfBoundsDatetime, Timestamp, - is_unitless, tz_compare, ) from pandas._typing import ( @@ -61,7 +60,6 @@ from pandas.compat.numpy import function as nv from pandas.errors import ( DuplicateLabelError, - IntCastingNaNError, InvalidIndexError, ) from pandas.util._decorators import ( @@ -1016,11 +1014,6 @@ def astype(self, dtype, copy: bool = True): values = self._data if isinstance(values, ExtensionArray): - if isinstance(dtype, np.dtype) and dtype.kind == "M" and is_unitless(dtype): - # TODO(2.0): remove this special-casing once this is enforced - # in DTA.astype - raise TypeError(f"Cannot cast {type(self).__name__} to dtype") - with rewrite_exception(type(values).__name__, type(self).__name__): new_values = values.astype(dtype, copy=copy) @@ -1039,22 +1032,12 @@ def astype(self, dtype, copy: bool = True): new_values = cls._from_sequence(self, dtype=dtype, copy=copy) else: - try: - if dtype == str: - # GH#38607 - new_values = values.astype(dtype, copy=copy) - else: - # GH#13149 specifically use astype_nansafe instead of astype - new_values = astype_nansafe(values, dtype=dtype, copy=copy) - except IntCastingNaNError: - raise - except (TypeError, ValueError) as err: - if dtype.kind == "u" and "losslessly" in str(err): - # keep the message from _astype_float_to_int_nansafe - raise - raise TypeError( - f"Cannot cast {type(self).__name__} to dtype {dtype}" - ) from err + if dtype == str: + # GH#38607 see test_astype_str_from_bytes + new_values = values.astype(dtype, copy=copy) + else: + # GH#13149 specifically use astype_nansafe instead of astype + new_values = astype_nansafe(values, dtype=dtype, copy=copy) # pass copy=False because any copying will be done in the astype above if self._is_backward_compat_public_numeric_index: diff --git a/pandas/tests/frame/test_constructors.py b/pandas/tests/frame/test_constructors.py index 54cd39df54c57..d14f419888023 100644 --- a/pandas/tests/frame/test_constructors.py +++ b/pandas/tests/frame/test_constructors.py @@ -141,13 +141,8 @@ def test_array_of_dt64_nat_with_td64dtype_raises(self, frame_or_series): if frame_or_series is DataFrame: arr = arr.reshape(1, 1) - msg = "|".join( - [ - "Could not convert object to NumPy timedelta", - "Invalid type for timedelta scalar: ", - ] - ) - with pytest.raises(ValueError, match=msg): + msg = "Invalid type for timedelta scalar: " + with pytest.raises(TypeError, match=msg): frame_or_series(arr, dtype="m8[ns]") @pytest.mark.parametrize("kind", ["m", "M"]) diff --git a/pandas/tests/indexes/datetimes/methods/test_astype.py b/pandas/tests/indexes/datetimes/methods/test_astype.py index a9a35f26d58a3..ccbfd9217373b 100644 --- a/pandas/tests/indexes/datetimes/methods/test_astype.py +++ b/pandas/tests/indexes/datetimes/methods/test_astype.py @@ -214,6 +214,8 @@ def test_astype_raises(self, dtype): # GH 13149, GH 13209 idx = DatetimeIndex(["2016-05-16", "NaT", NaT, np.NaN]) msg = "Cannot cast DatetimeIndex to dtype" + if dtype == "datetime64": + msg = "Casting to unit-less dtype 'datetime64' is not supported" with pytest.raises(TypeError, match=msg): idx.astype(dtype) diff --git a/pandas/tests/indexes/object/test_astype.py b/pandas/tests/indexes/object/test_astype.py index 91e266e805868..33e45a707df63 100644 --- a/pandas/tests/indexes/object/test_astype.py +++ b/pandas/tests/indexes/object/test_astype.py @@ -19,6 +19,6 @@ def test_astype_invalid_nas_to_tdt64_raises(): # GH#45722 don't cast np.datetime64 NaTs to timedelta64 NaT idx = Index([NaT.asm8] * 2, dtype=object) - msg = r"Cannot cast Index to dtype timedelta64\[ns\]" + msg = r"Invalid type for timedelta scalar: " with pytest.raises(TypeError, match=msg): idx.astype("m8[ns]") diff --git a/pandas/tests/indexes/timedeltas/test_constructors.py b/pandas/tests/indexes/timedeltas/test_constructors.py index af932f0f20695..1447e9080313f 100644 --- a/pandas/tests/indexes/timedeltas/test_constructors.py +++ b/pandas/tests/indexes/timedeltas/test_constructors.py @@ -23,17 +23,19 @@ def test_array_of_dt64_nat_raises(self): nat = np.datetime64("NaT", "ns") arr = np.array([nat], dtype=object) - # TODO: should be TypeError? msg = "Invalid type for timedelta scalar" - with pytest.raises(ValueError, match=msg): + with pytest.raises(TypeError, match=msg): TimedeltaIndex(arr) - with pytest.raises(ValueError, match=msg): + with pytest.raises(TypeError, match=msg): TimedeltaArray._from_sequence(arr) - with pytest.raises(ValueError, match=msg): + with pytest.raises(TypeError, match=msg): sequence_to_td64ns(arr) + with pytest.raises(TypeError, match=msg): + to_timedelta(arr) + @pytest.mark.parametrize("unit", ["Y", "y", "M"]) def test_unit_m_y_raises(self, unit): msg = "Units 'M', 'Y', and 'y' are no longer supported"