Skip to content

Commit 656de79

Browse files
jbrockmendelphofl
authored andcommitted
DEPR: enforce Series/DataFrame awareness-mismatch deprecations (pandas-dev#48739)
* DEPR: enforce deprecation on Series(ts_aware, dtype=naive) * DEPR: enforce deprecation on Series(tzaware_seq, dtype=naive) * rename * move whatsnew
1 parent 58c1528 commit 656de79

File tree

4 files changed

+59
-91
lines changed

4 files changed

+59
-91
lines changed

doc/source/whatsnew/v2.0.0.rst

+2
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ Deprecations
146146

147147
Removal of prior version deprecations/changes
148148
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
149+
- Enforced deprecation disallowing passing a timezone-aware :class:`Timestamp` and ``dtype="datetime64[ns]"`` to :class:`Series` or :class:`DataFrame` constructors (:issue:`41555`)
150+
- Enforced deprecation disallowing passing a sequence of timezone-aware values and ``dtype="datetime64[ns]"`` to to :class:`Series` or :class:`DataFrame` constructors (:issue:`41555`)
149151
- Removed Date parser functions :func:`~pandas.io.date_converters.parse_date_time`,
150152
:func:`~pandas.io.date_converters.parse_date_fields`, :func:`~pandas.io.date_converters.parse_all_fields`
151153
and :func:`~pandas.io.date_converters.generic_parser` (:issue:`24518`)

pandas/core/dtypes/cast.py

+8-45
Original file line numberDiff line numberDiff line change
@@ -1358,20 +1358,11 @@ def maybe_cast_to_datetime(
13581358
# didn't specify one
13591359

13601360
if dta.tz is not None:
1361-
warnings.warn(
1362-
"Data is timezone-aware. Converting "
1363-
"timezone-aware data to timezone-naive by "
1364-
"passing dtype='datetime64[ns]' to "
1365-
"DataFrame or Series is deprecated and will "
1366-
"raise in a future version. Use "
1367-
"`pd.Series(values).dt.tz_localize(None)` "
1368-
"instead.",
1369-
FutureWarning,
1370-
stacklevel=find_stack_level(),
1361+
raise ValueError(
1362+
"Cannot convert timezone-aware data to "
1363+
"timezone-naive dtype. Use "
1364+
"pd.Series(values).dt.tz_localize(None) instead."
13711365
)
1372-
# equiv: dta.view(dtype)
1373-
# Note: NOT equivalent to dta.astype(dtype)
1374-
dta = dta.tz_localize(None)
13751366

13761367
# TODO(2.0): Do this astype in sequence_to_datetimes to
13771368
# avoid potential extra copy?
@@ -1678,7 +1669,7 @@ def construct_2d_arraylike_from_scalar(
16781669
shape = (length, width)
16791670

16801671
if dtype.kind in ["m", "M"]:
1681-
value = _maybe_unbox_datetimelike_tz_deprecation(value, dtype)
1672+
value = _maybe_box_and_unbox_datetimelike(value, dtype)
16821673
elif dtype == _dtype_obj:
16831674
if isinstance(value, (np.timedelta64, np.datetime64)):
16841675
# calling np.array below would cast to pytimedelta/pydatetime
@@ -1742,7 +1733,7 @@ def construct_1d_arraylike_from_scalar(
17421733
if not isna(value):
17431734
value = ensure_str(value)
17441735
elif dtype.kind in ["M", "m"]:
1745-
value = _maybe_unbox_datetimelike_tz_deprecation(value, dtype)
1736+
value = _maybe_box_and_unbox_datetimelike(value, dtype)
17461737

17471738
subarr = np.empty(length, dtype=dtype)
17481739
if length:
@@ -1752,42 +1743,14 @@ def construct_1d_arraylike_from_scalar(
17521743
return subarr
17531744

17541745

1755-
def _maybe_unbox_datetimelike_tz_deprecation(value: Scalar, dtype: DtypeObj):
1756-
"""
1757-
Wrap _maybe_unbox_datetimelike with a check for a timezone-aware Timestamp
1758-
along with a timezone-naive datetime64 dtype, which is deprecated.
1759-
"""
1746+
def _maybe_box_and_unbox_datetimelike(value: Scalar, dtype: DtypeObj):
17601747
# Caller is responsible for checking dtype.kind in ["m", "M"]
17611748

17621749
if isinstance(value, datetime):
17631750
# we dont want to box dt64, in particular datetime64("NaT")
17641751
value = maybe_box_datetimelike(value, dtype)
17651752

1766-
try:
1767-
value = _maybe_unbox_datetimelike(value, dtype)
1768-
except TypeError:
1769-
if (
1770-
isinstance(value, Timestamp)
1771-
and value.tzinfo is not None
1772-
and isinstance(dtype, np.dtype)
1773-
and dtype.kind == "M"
1774-
):
1775-
warnings.warn(
1776-
"Data is timezone-aware. Converting "
1777-
"timezone-aware data to timezone-naive by "
1778-
"passing dtype='datetime64[ns]' to "
1779-
"DataFrame or Series is deprecated and will "
1780-
"raise in a future version. Use "
1781-
"`pd.Series(values).dt.tz_localize(None)` "
1782-
"instead.",
1783-
FutureWarning,
1784-
stacklevel=find_stack_level(),
1785-
)
1786-
new_value = value.tz_localize(None)
1787-
return _maybe_unbox_datetimelike(new_value, dtype)
1788-
else:
1789-
raise
1790-
return value
1753+
return _maybe_unbox_datetimelike(value, dtype)
17911754

17921755

17931756
def construct_1d_object_array_from_listlike(values: Sized) -> np.ndarray:

pandas/tests/frame/test_constructors.py

+36-32
Original file line numberDiff line numberDiff line change
@@ -2848,37 +2848,32 @@ def test_constructor_data_aware_dtype_naive(self, tz_aware_fixture, pydt):
28482848
ts = Timestamp("2019", tz=tz)
28492849
if pydt:
28502850
ts = ts.to_pydatetime()
2851-
ts_naive = Timestamp("2019")
28522851

2853-
with tm.assert_produces_warning(FutureWarning):
2854-
result = DataFrame({0: [ts]}, dtype="datetime64[ns]")
2852+
msg = (
2853+
"Cannot convert timezone-aware data to timezone-naive dtype. "
2854+
r"Use pd.Series\(values\).dt.tz_localize\(None\) instead."
2855+
)
2856+
with pytest.raises(ValueError, match=msg):
2857+
DataFrame({0: [ts]}, dtype="datetime64[ns]")
28552858

2856-
expected = DataFrame({0: [ts_naive]})
2857-
tm.assert_frame_equal(result, expected)
2859+
msg2 = "Cannot unbox tzaware Timestamp to tznaive dtype"
2860+
with pytest.raises(TypeError, match=msg2):
2861+
DataFrame({0: ts}, index=[0], dtype="datetime64[ns]")
28582862

2859-
with tm.assert_produces_warning(FutureWarning):
2860-
result = DataFrame({0: ts}, index=[0], dtype="datetime64[ns]")
2861-
tm.assert_frame_equal(result, expected)
2863+
with pytest.raises(ValueError, match=msg):
2864+
DataFrame([ts], dtype="datetime64[ns]")
28622865

2863-
with tm.assert_produces_warning(FutureWarning):
2864-
result = DataFrame([ts], dtype="datetime64[ns]")
2865-
tm.assert_frame_equal(result, expected)
2866+
with pytest.raises(ValueError, match=msg):
2867+
DataFrame(np.array([ts], dtype=object), dtype="datetime64[ns]")
28662868

2867-
with tm.assert_produces_warning(FutureWarning):
2868-
result = DataFrame(np.array([ts], dtype=object), dtype="datetime64[ns]")
2869-
tm.assert_frame_equal(result, expected)
2869+
with pytest.raises(TypeError, match=msg2):
2870+
DataFrame(ts, index=[0], columns=[0], dtype="datetime64[ns]")
28702871

2871-
with tm.assert_produces_warning(FutureWarning):
2872-
result = DataFrame(ts, index=[0], columns=[0], dtype="datetime64[ns]")
2873-
tm.assert_frame_equal(result, expected)
2874-
2875-
with tm.assert_produces_warning(FutureWarning):
2876-
df = DataFrame([Series([ts])], dtype="datetime64[ns]")
2877-
tm.assert_frame_equal(result, expected)
2872+
with pytest.raises(ValueError, match=msg):
2873+
DataFrame([Series([ts])], dtype="datetime64[ns]")
28782874

2879-
with tm.assert_produces_warning(FutureWarning):
2880-
df = DataFrame([[ts]], columns=[0], dtype="datetime64[ns]")
2881-
tm.assert_equal(df, expected)
2875+
with pytest.raises(ValueError, match=msg):
2876+
DataFrame([[ts]], columns=[0], dtype="datetime64[ns]")
28822877

28832878
def test_from_dict(self):
28842879

@@ -3051,8 +3046,11 @@ def get1(obj): # TODO: make a helper in tm?
30513046

30523047
class TestFromScalar:
30533048
@pytest.fixture(params=[list, dict, None])
3054-
def constructor(self, request, frame_or_series):
3055-
box = request.param
3049+
def box(self, request):
3050+
return request.param
3051+
3052+
@pytest.fixture
3053+
def constructor(self, frame_or_series, box):
30563054

30573055
extra = {"index": range(2)}
30583056
if frame_or_series is DataFrame:
@@ -3181,16 +3179,22 @@ def test_out_of_s_bounds_timedelta64(self, constructor):
31813179
dtype = result.dtype if isinstance(result, Series) else result.dtypes.iloc[0]
31823180
assert dtype == object
31833181

3184-
def test_tzaware_data_tznaive_dtype(self, constructor):
3182+
def test_tzaware_data_tznaive_dtype(self, constructor, box, frame_or_series):
31853183
tz = "US/Eastern"
31863184
ts = Timestamp("2019", tz=tz)
3187-
ts_naive = Timestamp("2019")
31883185

3189-
with tm.assert_produces_warning(FutureWarning, match="Data is timezone-aware"):
3190-
result = constructor(ts, dtype="M8[ns]")
3186+
if box is None or (frame_or_series is DataFrame and box is dict):
3187+
msg = "Cannot unbox tzaware Timestamp to tznaive dtype"
3188+
err = TypeError
3189+
else:
3190+
msg = (
3191+
"Cannot convert timezone-aware data to timezone-naive dtype. "
3192+
r"Use pd.Series\(values\).dt.tz_localize\(None\) instead."
3193+
)
3194+
err = ValueError
31913195

3192-
assert np.all(result.dtypes == "M8[ns]")
3193-
assert np.all(result == ts_naive)
3196+
with pytest.raises(err, match=msg):
3197+
constructor(ts, dtype="M8[ns]")
31943198

31953199

31963200
# TODO: better location for this test?

pandas/tests/series/test_constructors.py

+13-14
Original file line numberDiff line numberDiff line change
@@ -1736,24 +1736,23 @@ def test_constructor_data_aware_dtype_naive(self, tz_aware_fixture, pydt):
17361736
ts = Timestamp("2019", tz=tz)
17371737
if pydt:
17381738
ts = ts.to_pydatetime()
1739-
ts_naive = Timestamp("2019")
17401739

1741-
with tm.assert_produces_warning(FutureWarning):
1742-
result = Series([ts], dtype="datetime64[ns]")
1743-
expected = Series([ts_naive])
1744-
tm.assert_series_equal(result, expected)
1740+
msg = (
1741+
"Cannot convert timezone-aware data to timezone-naive dtype. "
1742+
r"Use pd.Series\(values\).dt.tz_localize\(None\) instead."
1743+
)
1744+
with pytest.raises(ValueError, match=msg):
1745+
Series([ts], dtype="datetime64[ns]")
17451746

1746-
with tm.assert_produces_warning(FutureWarning):
1747-
result = Series(np.array([ts], dtype=object), dtype="datetime64[ns]")
1748-
tm.assert_series_equal(result, expected)
1747+
with pytest.raises(ValueError, match=msg):
1748+
Series(np.array([ts], dtype=object), dtype="datetime64[ns]")
17491749

1750-
with tm.assert_produces_warning(FutureWarning):
1751-
result = Series({0: ts}, dtype="datetime64[ns]")
1752-
tm.assert_series_equal(result, expected)
1750+
with pytest.raises(ValueError, match=msg):
1751+
Series({0: ts}, dtype="datetime64[ns]")
17531752

1754-
with tm.assert_produces_warning(FutureWarning):
1755-
result = Series(ts, index=[0], dtype="datetime64[ns]")
1756-
tm.assert_series_equal(result, expected)
1753+
msg = "Cannot unbox tzaware Timestamp to tznaive dtype"
1754+
with pytest.raises(TypeError, match=msg):
1755+
Series(ts, index=[0], dtype="datetime64[ns]")
17571756

17581757
def test_constructor_datetime64(self):
17591758
rng = date_range("1/1/2000 00:00:00", "1/1/2000 1:59:50", freq="10s")

0 commit comments

Comments
 (0)