Skip to content

Commit 882dbee

Browse files
CLN: more consistent error message for construct_from_string wrong type (#31387)
1 parent abece9c commit 882dbee

File tree

6 files changed

+47
-26
lines changed

6 files changed

+47
-26
lines changed

pandas/core/arrays/numpy_.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,11 @@ def construct_from_string(cls, string):
7474
try:
7575
return cls(np.dtype(string))
7676
except TypeError as err:
77-
raise TypeError(
78-
f"Cannot construct a 'PandasDtype' from '{string}'"
79-
) from err
77+
if not isinstance(string, str):
78+
msg = f"'construct_from_string' expects a string, got {type(string)}"
79+
else:
80+
msg = f"Cannot construct a 'PandasDtype' from '{string}'"
81+
raise TypeError(msg) from err
8082

8183
@classmethod
8284
def construct_array_type(cls):

pandas/core/arrays/sparse/dtype.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ def construct_from_string(cls, string):
206206
-------
207207
SparseDtype
208208
"""
209+
if not isinstance(string, str):
210+
raise TypeError(
211+
f"'construct_from_string' expects a string, got {type(string)}"
212+
)
209213
msg = f"Cannot construct a 'SparseDtype' from '{string}'"
210214
if string.startswith("Sparse"):
211215
try:

pandas/core/dtypes/base.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,9 @@ def construct_from_string(cls, string: str):
235235
... " "'{string}'")
236236
"""
237237
if not isinstance(string, str):
238-
raise TypeError(f"Expects a string, got {type(string).__name__}")
239-
238+
raise TypeError(
239+
f"'construct_from_string' expects a string, got {type(string)}"
240+
)
240241
# error: Non-overlapping equality check (left operand type: "str", right
241242
# operand type: "Callable[[ExtensionDtype], str]") [comparison-overlap]
242243
assert isinstance(cls.name, str), (cls, type(cls.name))

pandas/core/dtypes/dtypes.py

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,9 @@ def construct_from_string(cls, string: str_type) -> "CategoricalDtype":
352352
If a CategoricalDtype cannot be constructed from the input.
353353
"""
354354
if not isinstance(string, str):
355-
raise TypeError(f"Expects a string, got {type(string)}")
355+
raise TypeError(
356+
f"'construct_from_string' expects a string, got {type(string)}"
357+
)
356358
if string != cls.name:
357359
raise TypeError(f"Cannot construct a 'CategoricalDtype' from '{string}'")
358360

@@ -728,22 +730,24 @@ def construct_from_string(cls, string: str_type):
728730
>>> DatetimeTZDtype.construct_from_string('datetime64[ns, UTC]')
729731
datetime64[ns, UTC]
730732
"""
731-
if isinstance(string, str):
732-
msg = f"Cannot construct a 'DatetimeTZDtype' from '{string}'"
733-
match = cls._match.match(string)
734-
if match:
735-
d = match.groupdict()
736-
try:
737-
return cls(unit=d["unit"], tz=d["tz"])
738-
except (KeyError, TypeError, ValueError) as err:
739-
# KeyError if maybe_get_tz tries and fails to get a
740-
# pytz timezone (actually pytz.UnknownTimeZoneError).
741-
# TypeError if we pass a nonsense tz;
742-
# ValueError if we pass a unit other than "ns"
743-
raise TypeError(msg) from err
744-
raise TypeError(msg)
733+
if not isinstance(string, str):
734+
raise TypeError(
735+
f"'construct_from_string' expects a string, got {type(string)}"
736+
)
745737

746-
raise TypeError("Cannot construct a 'DatetimeTZDtype'")
738+
msg = f"Cannot construct a 'DatetimeTZDtype' from '{string}'"
739+
match = cls._match.match(string)
740+
if match:
741+
d = match.groupdict()
742+
try:
743+
return cls(unit=d["unit"], tz=d["tz"])
744+
except (KeyError, TypeError, ValueError) as err:
745+
# KeyError if maybe_get_tz tries and fails to get a
746+
# pytz timezone (actually pytz.UnknownTimeZoneError).
747+
# TypeError if we pass a nonsense tz;
748+
# ValueError if we pass a unit other than "ns"
749+
raise TypeError(msg) from err
750+
raise TypeError(msg)
747751

748752
def __str__(self) -> str_type:
749753
return f"datetime64[{self.unit}, {self.tz}]"
@@ -1075,7 +1079,9 @@ def construct_from_string(cls, string):
10751079
if its not possible
10761080
"""
10771081
if not isinstance(string, str):
1078-
raise TypeError(f"a string needs to be passed, got type {type(string)}")
1082+
raise TypeError(
1083+
f"'construct_from_string' expects a string, got {type(string)}"
1084+
)
10791085

10801086
if string.lower() == "interval" or cls._match.search(string) is not None:
10811087
return cls(string)

pandas/tests/dtypes/test_dtypes.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,11 +244,12 @@ def test_construct_from_string_raises(self):
244244
with pytest.raises(TypeError, match="notatz"):
245245
DatetimeTZDtype.construct_from_string("datetime64[ns, notatz]")
246246

247-
msg = "^Cannot construct a 'DatetimeTZDtype'"
248-
with pytest.raises(TypeError, match=msg):
247+
msg = "'construct_from_string' expects a string, got <class 'list'>"
248+
with pytest.raises(TypeError, match=re.escape(msg)):
249249
# list instead of string
250250
DatetimeTZDtype.construct_from_string(["datetime64[ns, notatz]"])
251251

252+
msg = "^Cannot construct a 'DatetimeTZDtype'"
252253
with pytest.raises(TypeError, match=msg):
253254
# non-nano unit
254255
DatetimeTZDtype.construct_from_string("datetime64[ps, UTC]")
@@ -547,9 +548,9 @@ def test_construction_from_string(self):
547548
@pytest.mark.parametrize("string", [0, 3.14, ("a", "b"), None])
548549
def test_construction_from_string_errors(self, string):
549550
# these are invalid entirely
550-
msg = "a string needs to be passed, got type"
551+
msg = f"'construct_from_string' expects a string, got {type(string)}"
551552

552-
with pytest.raises(TypeError, match=msg):
553+
with pytest.raises(TypeError, match=re.escape(msg)):
553554
IntervalDtype.construct_from_string(string)
554555

555556
@pytest.mark.parametrize("string", ["foo", "foo[int64]", "IntervalA"])

pandas/tests/extension/base/dtype.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,10 @@ def test_construct_from_string_another_type_raises(self, dtype):
105105
msg = f"Cannot construct a '{type(dtype).__name__}' from 'another_type'"
106106
with pytest.raises(TypeError, match=msg):
107107
type(dtype).construct_from_string("another_type")
108+
109+
def test_construct_from_string_wrong_type_raises(self, dtype):
110+
with pytest.raises(
111+
TypeError,
112+
match="'construct_from_string' expects a string, got <class 'int'>",
113+
):
114+
type(dtype).construct_from_string(0)

0 commit comments

Comments
 (0)