From ffc70e3a62f8d4db987a53e15d6ae73c4df86fb7 Mon Sep 17 00:00:00 2001 From: Lorenzo Gaifas Date: Fri, 2 Sep 2022 11:21:25 +0200 Subject: [PATCH 1/3] Reject any non-array, non-ns datetimes and timedeltas Co-authored-by: Kislovskiy --- pandas/core/dtypes/cast.py | 41 ++++++------------------ pandas/tests/series/test_constructors.py | 8 +++++ 2 files changed, 17 insertions(+), 32 deletions(-) diff --git a/pandas/core/dtypes/cast.py b/pandas/core/dtypes/cast.py index 4244217da7865..c4dda24f857d9 100644 --- a/pandas/core/dtypes/cast.py +++ b/pandas/core/dtypes/cast.py @@ -1308,7 +1308,7 @@ def maybe_cast_to_datetime( # TODO: _from_sequence would raise ValueError in cases where # _ensure_nanosecond_dtype raises TypeError dtype = cast(np.dtype, dtype) - dtype = _ensure_nanosecond_dtype(dtype) + _ensure_nanosecond_dtype(dtype) res = TimedeltaArray._from_sequence(value, dtype=dtype) return res @@ -1319,7 +1319,7 @@ def maybe_cast_to_datetime( vdtype = getattr(value, "dtype", None) if is_datetime64 or is_datetime64tz: - dtype = _ensure_nanosecond_dtype(dtype) + _ensure_nanosecond_dtype(dtype) value = np.array(value, copy=False) @@ -1437,17 +1437,9 @@ def sanitize_to_nanoseconds(values: np.ndarray, copy: bool = False) -> np.ndarra return values -def _ensure_nanosecond_dtype(dtype: DtypeObj) -> DtypeObj: +def _ensure_nanosecond_dtype(dtype: DtypeObj) -> None: """ - Convert dtypes with granularity less than nanosecond to nanosecond - - >>> _ensure_nanosecond_dtype(np.dtype("M8[s]")) - dtype('>> _ensure_nanosecond_dtype(np.dtype("m8[ps]")) - Traceback (most recent call last): - ... - TypeError: cannot convert timedeltalike to dtype [timedelta64[ps]] + Reject datetime/timedelta dtypes with granularity different from ns """ msg = ( f"The '{dtype.name}' dtype has no unit. " @@ -1459,28 +1451,13 @@ def _ensure_nanosecond_dtype(dtype: DtypeObj) -> DtypeObj: if not isinstance(dtype, np.dtype): # i.e. datetime64tz - pass + return - elif dtype.kind == "M" and dtype != DT64NS_DTYPE: - # pandas supports dtype whose granularity is less than [ns] - # e.g., [ps], [fs], [as] - if dtype <= np.dtype("M8[ns]"): - if dtype.name == "datetime64": - raise ValueError(msg) - dtype = DT64NS_DTYPE - else: - raise TypeError(f"cannot convert datetimelike to dtype [{dtype}]") + if dtype.name in ("datetime64", "timedelta64"): + raise ValueError(msg) - elif dtype.kind == "m" and dtype != TD64NS_DTYPE: - # pandas supports dtype whose granularity is less than [ns] - # e.g., [ps], [fs], [as] - if dtype <= np.dtype("m8[ns]"): - if dtype.name == "timedelta64": - raise ValueError(msg) - dtype = TD64NS_DTYPE - else: - raise TypeError(f"cannot convert timedeltalike to dtype [{dtype}]") - return dtype + if dtype not in (TD64NS_DTYPE, DT64NS_DTYPE): + raise TypeError("Only [ns] granularity is supported for timedelta and datetime") # TODO: other value-dependent functions to standardize here include diff --git a/pandas/tests/series/test_constructors.py b/pandas/tests/series/test_constructors.py index f79714ae6455c..a49b41c8747c9 100644 --- a/pandas/tests/series/test_constructors.py +++ b/pandas/tests/series/test_constructors.py @@ -1578,6 +1578,14 @@ def test_convert_non_ns(self): expected = Series(date_range("20130101 00:00:01", periods=3, freq="s")) tm.assert_series_equal(ser, expected) + # try to construct from a sequence asking for non-ns timedelta64 + with pytest.raises(TypeError, match=r"Only \[ns\] granularity"): + Series([1000000, 200000, 3000000], dtype="timedelta64[s]") + + # try to construct from a sequence asking for non-ns datetime64 + with pytest.raises(TypeError, match=r"Only \[ns\] granularity"): + Series([1000000, 200000, 3000000], dtype="datetime64[s]") + @pytest.mark.parametrize( "index", [ From 55f75dc4a4313bb2a21a370ecd8e4b10f51fdb95 Mon Sep 17 00:00:00 2001 From: kislovskiy Date: Fri, 2 Sep 2022 14:09:12 +0000 Subject: [PATCH 2/3] remove unsupported tests and fix rised error --- pandas/tests/series/test_constructors.py | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/pandas/tests/series/test_constructors.py b/pandas/tests/series/test_constructors.py index a49b41c8747c9..090335f20a91d 100644 --- a/pandas/tests/series/test_constructors.py +++ b/pandas/tests/series/test_constructors.py @@ -1579,11 +1579,11 @@ def test_convert_non_ns(self): tm.assert_series_equal(ser, expected) # try to construct from a sequence asking for non-ns timedelta64 - with pytest.raises(TypeError, match=r"Only \[ns\] granularity"): + with pytest.raises(TypeError, match=r"Only \[ns\] granularity is supported"): Series([1000000, 200000, 3000000], dtype="timedelta64[s]") # try to construct from a sequence asking for non-ns datetime64 - with pytest.raises(TypeError, match=r"Only \[ns\] granularity"): + with pytest.raises(TypeError, match=r"Only \[ns\] granularity is supported"): Series([1000000, 200000, 3000000], dtype="datetime64[s]") @pytest.mark.parametrize( @@ -1650,8 +1650,8 @@ def test_constructor_generic_timestamp_no_frequency(self, dtype, request): @pytest.mark.parametrize( "dtype,msg", [ - ("m8[ps]", "cannot convert timedeltalike"), - ("M8[ps]", "cannot convert datetimelike"), + ("m8[ps]", "Only \[ns\] granularity is supported"), + ("M8[ps]", "Only \[ns\] granularity is supported"), ], ) def test_constructor_generic_timestamp_bad_frequency(self, dtype, msg): @@ -1894,22 +1894,6 @@ def test_constructor_dtype_timedelta_alternative_construct(self): expected = Series(pd.to_timedelta([1000000, 200000, 3000000], unit="ns")) tm.assert_series_equal(result, expected) - def test_constructor_dtype_timedelta_ns_s(self): - # GH#35465 - result = Series([1000000, 200000, 3000000], dtype="timedelta64[ns]") - expected = Series([1000000, 200000, 3000000], dtype="timedelta64[s]") - tm.assert_series_equal(result, expected) - - def test_constructor_dtype_timedelta_ns_s_astype_int64(self): - # GH#35465 - result = Series([1000000, 200000, 3000000], dtype="timedelta64[ns]").astype( - "int64" - ) - expected = Series([1000000, 200000, 3000000], dtype="timedelta64[s]").astype( - "int64" - ) - tm.assert_series_equal(result, expected) - @pytest.mark.filterwarnings( "ignore:elementwise comparison failed:DeprecationWarning" ) From 7448b8f199a6dd55fed9c2f11d91bf59d9102a8a Mon Sep 17 00:00:00 2001 From: Lorenzo Gaifas Date: Mon, 5 Sep 2022 11:20:21 +0200 Subject: [PATCH 3/3] happy flake --- pandas/tests/series/test_constructors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/series/test_constructors.py b/pandas/tests/series/test_constructors.py index 090335f20a91d..2959479f426fc 100644 --- a/pandas/tests/series/test_constructors.py +++ b/pandas/tests/series/test_constructors.py @@ -1650,8 +1650,8 @@ def test_constructor_generic_timestamp_no_frequency(self, dtype, request): @pytest.mark.parametrize( "dtype,msg", [ - ("m8[ps]", "Only \[ns\] granularity is supported"), - ("M8[ps]", "Only \[ns\] granularity is supported"), + (r"m8[ps]", r"Only \[ns\] granularity is supported"), + (r"M8[ps]", r"Only \[ns\] granularity is supported"), ], ) def test_constructor_generic_timestamp_bad_frequency(self, dtype, msg):