Skip to content

Commit cd38fa3

Browse files
authored
API: to_timedelta([bad_type]) TypeError instead of ValueError (#49525)
* API: to_timedelta([bad_type]) TypeError instead of ValueError * GH ref
1 parent 01b432c commit cd38fa3

File tree

7 files changed

+19
-36
lines changed

7 files changed

+19
-36
lines changed

doc/source/whatsnew/v2.0.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ Other API changes
235235
- :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`)
236236
- 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`)
237237
- 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`)
238+
- 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`)
238239
-
239240

240241
.. ---------------------------------------------------------------------------

pandas/_libs/tslibs/timedeltas.pyx

+1-1
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ cdef convert_to_timedelta64(object ts, str unit):
366366
if PyDelta_Check(ts):
367367
ts = np.timedelta64(delta_to_nanoseconds(ts), "ns")
368368
elif not is_timedelta64_object(ts):
369-
raise ValueError(f"Invalid type for timedelta scalar: {type(ts)}")
369+
raise TypeError(f"Invalid type for timedelta scalar: {type(ts)}")
370370
return ts.astype("timedelta64[ns]")
371371

372372

pandas/core/indexes/base.py

+6-23
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
IncompatibleFrequency,
4242
OutOfBoundsDatetime,
4343
Timestamp,
44-
is_unitless,
4544
tz_compare,
4645
)
4746
from pandas._typing import (
@@ -61,7 +60,6 @@
6160
from pandas.compat.numpy import function as nv
6261
from pandas.errors import (
6362
DuplicateLabelError,
64-
IntCastingNaNError,
6563
InvalidIndexError,
6664
)
6765
from pandas.util._decorators import (
@@ -1016,11 +1014,6 @@ def astype(self, dtype, copy: bool = True):
10161014

10171015
values = self._data
10181016
if isinstance(values, ExtensionArray):
1019-
if isinstance(dtype, np.dtype) and dtype.kind == "M" and is_unitless(dtype):
1020-
# TODO(2.0): remove this special-casing once this is enforced
1021-
# in DTA.astype
1022-
raise TypeError(f"Cannot cast {type(self).__name__} to dtype")
1023-
10241017
with rewrite_exception(type(values).__name__, type(self).__name__):
10251018
new_values = values.astype(dtype, copy=copy)
10261019

@@ -1039,22 +1032,12 @@ def astype(self, dtype, copy: bool = True):
10391032
new_values = cls._from_sequence(self, dtype=dtype, copy=copy)
10401033

10411034
else:
1042-
try:
1043-
if dtype == str:
1044-
# GH#38607
1045-
new_values = values.astype(dtype, copy=copy)
1046-
else:
1047-
# GH#13149 specifically use astype_nansafe instead of astype
1048-
new_values = astype_nansafe(values, dtype=dtype, copy=copy)
1049-
except IntCastingNaNError:
1050-
raise
1051-
except (TypeError, ValueError) as err:
1052-
if dtype.kind == "u" and "losslessly" in str(err):
1053-
# keep the message from _astype_float_to_int_nansafe
1054-
raise
1055-
raise TypeError(
1056-
f"Cannot cast {type(self).__name__} to dtype {dtype}"
1057-
) from err
1035+
if dtype == str:
1036+
# GH#38607 see test_astype_str_from_bytes
1037+
new_values = values.astype(dtype, copy=copy)
1038+
else:
1039+
# GH#13149 specifically use astype_nansafe instead of astype
1040+
new_values = astype_nansafe(values, dtype=dtype, copy=copy)
10581041

10591042
# pass copy=False because any copying will be done in the astype above
10601043
if self._is_backward_compat_public_numeric_index:

pandas/tests/frame/test_constructors.py

+2-7
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,8 @@ def test_array_of_dt64_nat_with_td64dtype_raises(self, frame_or_series):
141141
if frame_or_series is DataFrame:
142142
arr = arr.reshape(1, 1)
143143

144-
msg = "|".join(
145-
[
146-
"Could not convert object to NumPy timedelta",
147-
"Invalid type for timedelta scalar: <class 'numpy.datetime64'>",
148-
]
149-
)
150-
with pytest.raises(ValueError, match=msg):
144+
msg = "Invalid type for timedelta scalar: <class 'numpy.datetime64'>"
145+
with pytest.raises(TypeError, match=msg):
151146
frame_or_series(arr, dtype="m8[ns]")
152147

153148
@pytest.mark.parametrize("kind", ["m", "M"])

pandas/tests/indexes/datetimes/methods/test_astype.py

+2
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,8 @@ def test_astype_raises(self, dtype):
214214
# GH 13149, GH 13209
215215
idx = DatetimeIndex(["2016-05-16", "NaT", NaT, np.NaN])
216216
msg = "Cannot cast DatetimeIndex to dtype"
217+
if dtype == "datetime64":
218+
msg = "Casting to unit-less dtype 'datetime64' is not supported"
217219
with pytest.raises(TypeError, match=msg):
218220
idx.astype(dtype)
219221

pandas/tests/indexes/object/test_astype.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ def test_astype_invalid_nas_to_tdt64_raises():
1919
# GH#45722 don't cast np.datetime64 NaTs to timedelta64 NaT
2020
idx = Index([NaT.asm8] * 2, dtype=object)
2121

22-
msg = r"Cannot cast Index to dtype timedelta64\[ns\]"
22+
msg = r"Invalid type for timedelta scalar: <class 'numpy.datetime64'>"
2323
with pytest.raises(TypeError, match=msg):
2424
idx.astype("m8[ns]")

pandas/tests/indexes/timedeltas/test_constructors.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,19 @@ def test_array_of_dt64_nat_raises(self):
2323
nat = np.datetime64("NaT", "ns")
2424
arr = np.array([nat], dtype=object)
2525

26-
# TODO: should be TypeError?
2726
msg = "Invalid type for timedelta scalar"
28-
with pytest.raises(ValueError, match=msg):
27+
with pytest.raises(TypeError, match=msg):
2928
TimedeltaIndex(arr)
3029

31-
with pytest.raises(ValueError, match=msg):
30+
with pytest.raises(TypeError, match=msg):
3231
TimedeltaArray._from_sequence(arr)
3332

34-
with pytest.raises(ValueError, match=msg):
33+
with pytest.raises(TypeError, match=msg):
3534
sequence_to_td64ns(arr)
3635

36+
with pytest.raises(TypeError, match=msg):
37+
to_timedelta(arr)
38+
3739
@pytest.mark.parametrize("unit", ["Y", "y", "M"])
3840
def test_unit_m_y_raises(self, unit):
3941
msg = "Units 'M', 'Y', and 'y' are no longer supported"

0 commit comments

Comments
 (0)