Skip to content

Commit 17fe9a4

Browse files
MarcoGorellijreback
authored andcommitted
[BUG] Validate dtype when Int64Index, UInt64Index, or Float64Index are cons… (#29545)
1 parent b6d64d2 commit 17fe9a4

File tree

7 files changed

+52
-14
lines changed

7 files changed

+52
-14
lines changed

doc/source/whatsnew/v1.0.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ Numeric
343343
- Bug in :class:`DataFrame` logical operations (`&`, `|`, `^`) not matching :class:`Series` behavior by filling NA values (:issue:`28741`)
344344
- Bug in :meth:`DataFrame.interpolate` where specifying axis by name references variable before it is assigned (:issue:`29142`)
345345
- Improved error message when using `frac` > 1 and `replace` = False (:issue:`27451`)
346+
- Bug in numeric indexes resulted in it being possible to instantiate an :class:`Int64Index`, :class:`UInt64Index`, or :class:`Float64Index` with an invalid dtype (e.g. datetime-like) (:issue:`29539`)
346347
- Bug in :class:`UInt64Index` precision loss while constructing from a list with values in the ``np.uint64`` range (:issue:`29526`)
347348
-
348349

pandas/core/indexes/base.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ def __new__(
381381
pass
382382

383383
# Return an actual float index.
384-
return Float64Index(data, copy=copy, dtype=dtype, name=name)
384+
return Float64Index(data, copy=copy, name=name)
385385

386386
elif inferred == "string":
387387
pass

pandas/core/indexes/numeric.py

+20-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
is_float_dtype,
1616
is_integer_dtype,
1717
is_scalar,
18+
is_signed_integer_dtype,
19+
is_unsigned_integer_dtype,
1820
needs_i8_conversion,
1921
pandas_dtype,
2022
)
@@ -27,6 +29,7 @@
2729
)
2830
from pandas.core.dtypes.missing import isna
2931

32+
from pandas._typing import Dtype
3033
from pandas.core import algorithms
3134
import pandas.core.common as com
3235
from pandas.core.indexes.base import Index, InvalidIndexError, _index_shared_docs
@@ -45,7 +48,7 @@ class NumericIndex(Index):
4548
_is_numeric_dtype = True
4649

4750
def __new__(cls, data=None, dtype=None, copy=False, name=None, fastpath=None):
48-
51+
cls._validate_dtype(dtype)
4952
if fastpath is not None:
5053
warnings.warn(
5154
"The 'fastpath' keyword is deprecated, and will be "
@@ -80,6 +83,22 @@ def __new__(cls, data=None, dtype=None, copy=False, name=None, fastpath=None):
8083
name = data.name
8184
return cls._simple_new(subarr, name=name)
8285

86+
@classmethod
87+
def _validate_dtype(cls, dtype: Dtype) -> None:
88+
if dtype is None:
89+
return
90+
validation_metadata = {
91+
"int64index": (is_signed_integer_dtype, "signed integer"),
92+
"uint64index": (is_unsigned_integer_dtype, "unsigned integer"),
93+
"float64index": (is_float_dtype, "float"),
94+
"rangeindex": (is_signed_integer_dtype, "signed integer"),
95+
}
96+
97+
validation_func, expected = validation_metadata[cls._typ]
98+
if not validation_func(dtype):
99+
msg = f"Incorrect `dtype` passed: expected {expected}, received {dtype}"
100+
raise ValueError(msg)
101+
83102
@Appender(_index_shared_docs["_maybe_cast_slice_bound"])
84103
def _maybe_cast_slice_bound(self, label, side, kind):
85104
assert kind in ["ix", "loc", "getitem", None]

pandas/core/indexes/range.py

-7
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from pandas.core.dtypes.common import (
1515
ensure_platform_int,
1616
ensure_python_int,
17-
is_int64_dtype,
1817
is_integer,
1918
is_integer_dtype,
2019
is_list_like,
@@ -165,12 +164,6 @@ def _simple_new(cls, values, name=None, dtype=None):
165164

166165
# --------------------------------------------------------------------
167166

168-
@staticmethod
169-
def _validate_dtype(dtype):
170-
""" require dtype to be None or int64 """
171-
if not (dtype is None or is_int64_dtype(dtype)):
172-
raise TypeError("Invalid to pass a non-int64 dtype to RangeIndex")
173-
174167
@cache_readonly
175168
def _constructor(self):
176169
""" return the class to use for construction """

pandas/tests/indexes/test_numeric.py

+17
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,23 @@ def test_constructor(self):
167167
result = Index(np.array([np.nan]))
168168
assert pd.isna(result.values).all()
169169

170+
@pytest.mark.parametrize(
171+
"index, dtype",
172+
[
173+
(pd.Int64Index, "float64"),
174+
(pd.UInt64Index, "categorical"),
175+
(pd.Float64Index, "datetime64"),
176+
(pd.RangeIndex, "float64"),
177+
],
178+
)
179+
def test_invalid_dtype(self, index, dtype):
180+
# GH 29539
181+
with pytest.raises(
182+
ValueError,
183+
match=rf"Incorrect `dtype` passed: expected \w+(?: \w+)?, received {dtype}",
184+
):
185+
index([1, 2, 3], dtype=dtype)
186+
170187
def test_constructor_invalid(self):
171188

172189
# invalid

pandas/tests/indexes/test_range.py

+12-3
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,10 @@ def test_constructor_same(self):
110110
result = RangeIndex(index)
111111
tm.assert_index_equal(result, index, exact=True)
112112

113-
with pytest.raises(TypeError):
113+
with pytest.raises(
114+
ValueError,
115+
match="Incorrect `dtype` passed: expected signed integer, received float64",
116+
):
114117
RangeIndex(index, dtype="float64")
115118

116119
def test_constructor_range(self):
@@ -140,7 +143,10 @@ def test_constructor_range(self):
140143
expected = RangeIndex(1, 5, 2)
141144
tm.assert_index_equal(result, expected, exact=True)
142145

143-
with pytest.raises(TypeError):
146+
with pytest.raises(
147+
ValueError,
148+
match="Incorrect `dtype` passed: expected signed integer, received float64",
149+
):
144150
Index(range(1, 5, 2), dtype="float64")
145151
msg = r"^from_range\(\) got an unexpected keyword argument"
146152
with pytest.raises(TypeError, match=msg):
@@ -178,7 +184,10 @@ def test_constructor_corner(self):
178184
RangeIndex(1.1, 10.2, 1.3)
179185

180186
# invalid passed type
181-
with pytest.raises(TypeError):
187+
with pytest.raises(
188+
ValueError,
189+
match="Incorrect `dtype` passed: expected signed integer, received float64",
190+
):
182191
RangeIndex(1, 5, dtype="float64")
183192

184193
@pytest.mark.parametrize(

pandas/tests/series/indexing/test_numeric.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,7 @@ def test_get():
8686
1764.0,
8787
1849.0,
8888
1936.0,
89-
],
90-
dtype="object",
89+
]
9190
),
9291
)
9392

0 commit comments

Comments
 (0)