diff --git a/pandas/core/arrays/timedeltas.py b/pandas/core/arrays/timedeltas.py index 3f14b61c1ea6c..856a01e41ce13 100644 --- a/pandas/core/arrays/timedeltas.py +++ b/pandas/core/arrays/timedeltas.py @@ -163,11 +163,17 @@ def _simple_new(cls, values, freq=None, dtype=_TD_DTYPE): return result def __new__(cls, values, freq=None, dtype=_TD_DTYPE, copy=False): + return cls._from_sequence(values, freq=freq, dtype=dtype, copy=copy) + + @classmethod + def _from_sequence(cls, data, freq=None, unit=None, + dtype=_TD_DTYPE, copy=False): + if dtype != _TD_DTYPE: + raise ValueError("Only timedelta64[ns] dtype is valid.") freq, freq_infer = dtl.maybe_infer_freq(freq) - values, inferred_freq = sequence_to_td64ns( - values, copy=copy, unit=None) + data, inferred_freq = sequence_to_td64ns(data, copy=copy, unit=unit) if inferred_freq is not None: if freq is not None and freq != inferred_freq: raise ValueError('Inferred frequency {inferred} from passed ' @@ -179,13 +185,13 @@ def __new__(cls, values, freq=None, dtype=_TD_DTYPE, copy=False): freq = inferred_freq freq_infer = False - result = cls._simple_new(values, freq=freq) - # check that we are matching freqs - if inferred_freq is None and len(result) > 0: - if freq is not None and not freq_infer: - cls._validate_frequency(result, freq) + result = cls._simple_new(data, freq=freq) + + if inferred_freq is None and freq is not None: + # this condition precludes `freq_infer` + cls._validate_frequency(result, freq) - if freq_infer: + elif freq_infer: result.freq = to_offset(result.inferred_freq) return result @@ -666,7 +672,7 @@ def sequence_to_td64ns(data, copy=False, unit="ns", errors="raise"): warnings.warn("Passing datetime64-dtype data to TimedeltaIndex is " "deprecated, will raise a TypeError in a future " "version", - FutureWarning, stacklevel=3) + FutureWarning, stacklevel=4) data = ensure_int64(data).view(_TD_DTYPE) else: diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index 4be896049176c..ed4e43df8f41a 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -17,8 +17,7 @@ from pandas.core.arrays import datetimelike as dtl from pandas.core.arrays.timedeltas import ( - TimedeltaArrayMixin as TimedeltaArray, _is_convertible_to_td, _to_m8, - sequence_to_td64ns) + TimedeltaArrayMixin as TimedeltaArray, _is_convertible_to_td, _to_m8) from pandas.core.base import _shared_docs import pandas.core.common as com from pandas.core.indexes.base import Index, _index_shared_docs @@ -132,7 +131,7 @@ def _join_i8_wrapper(joinf, **kwargs): # Constructors def __new__(cls, data=None, unit=None, freq=None, start=None, end=None, - periods=None, closed=None, dtype=None, copy=False, + periods=None, closed=None, dtype=_TD_DTYPE, copy=False, name=None, verify_integrity=None): if verify_integrity is not None: @@ -142,9 +141,8 @@ def __new__(cls, data=None, unit=None, freq=None, start=None, end=None, else: verify_integrity = True - freq, freq_infer = dtl.maybe_infer_freq(freq) - if data is None: + freq, freq_infer = dtl.maybe_infer_freq(freq) warnings.warn("Creating a TimedeltaIndex by passing range " "endpoints is deprecated. Use " "`pandas.timedelta_range` instead.", @@ -167,29 +165,10 @@ def __new__(cls, data=None, unit=None, freq=None, start=None, end=None, # - Cases checked above all return/raise before reaching here - # - data, inferred_freq = sequence_to_td64ns(data, copy=copy, unit=unit) - if inferred_freq is not None: - if freq is not None and freq != inferred_freq: - raise ValueError('Inferred frequency {inferred} from passed ' - 'values does not conform to passed frequency ' - '{passed}' - .format(inferred=inferred_freq, - passed=freq.freqstr)) - elif freq_infer: - freq = inferred_freq - freq_infer = False - verify_integrity = False - - subarr = cls._simple_new(data, name=name, freq=freq) - # check that we are matching freqs - if verify_integrity and len(subarr) > 0: - if freq is not None and not freq_infer: - cls._validate_frequency(subarr, freq) - - if freq_infer: - subarr.freq = to_offset(subarr.inferred_freq) - - return subarr + result = cls._from_sequence(data, freq=freq, unit=unit, + dtype=dtype, copy=copy) + result.name = name + return result @classmethod def _simple_new(cls, values, name=None, freq=None, dtype=_TD_DTYPE): diff --git a/pandas/tests/arrays/test_timedeltas.py b/pandas/tests/arrays/test_timedeltas.py index 4d2664054b1c1..aef30c1bb7744 100644 --- a/pandas/tests/arrays/test_timedeltas.py +++ b/pandas/tests/arrays/test_timedeltas.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import numpy as np +import pytest import pandas as pd from pandas.core.arrays import TimedeltaArrayMixin as TimedeltaArray @@ -8,6 +9,12 @@ class TestTimedeltaArray(object): + def test_from_sequence_dtype(self): + msg = r"Only timedelta64\[ns\] dtype is valid" + with pytest.raises(ValueError, match=msg): + TimedeltaArray._from_sequence([], dtype=object) + with pytest.raises(ValueError, match=msg): + TimedeltaArray([], dtype=object) def test_abs(self): vals = np.array([-3600 * 10**9, 'NaT', 7200 * 10**9], dtype='m8[ns]') diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index 7732c5815b115..0c22b595bc74d 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -1163,7 +1163,7 @@ def test_is_timedelta(self): assert not is_timedelta64_ns_dtype('timedelta64') assert is_timedelta64_ns_dtype('timedelta64[ns]') - tdi = TimedeltaIndex([1e14, 2e14], dtype='timedelta64') + tdi = TimedeltaIndex([1e14, 2e14], dtype='timedelta64[ns]') assert is_timedelta64_dtype(tdi) assert is_timedelta64_ns_dtype(tdi) assert is_timedelta64_ns_dtype(tdi.astype('timedelta64[ns]'))