Skip to content

Commit 5a724b5

Browse files
datapythonistajreback
authored andcommitted
Provide ExtensionDtype.construct_from_string by default (#26562)
1 parent 891a419 commit 5a724b5

File tree

5 files changed

+42
-37
lines changed

5 files changed

+42
-37
lines changed

pandas/core/arrays/integer.py

-11
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,6 @@ def construct_array_type(cls):
7878
"""
7979
return IntegerArray
8080

81-
@classmethod
82-
def construct_from_string(cls, string):
83-
"""
84-
Construction from a string, raise a TypeError if not
85-
possible
86-
"""
87-
if string == cls.name:
88-
return cls()
89-
raise TypeError("Cannot construct a '{}' from "
90-
"'{}'".format(cls, string))
91-
9281

9382
def integer_array(values, dtype=None, copy=False):
9483
"""

pandas/core/dtypes/base.py

+27-12
Original file line numberDiff line numberDiff line change
@@ -172,17 +172,27 @@ def construct_array_type(cls):
172172
raise NotImplementedError
173173

174174
@classmethod
175-
def construct_from_string(cls, string):
176-
"""
177-
Attempt to construct this type from a string.
175+
def construct_from_string(cls, string: str):
176+
r"""
177+
Construct this type from a string.
178+
179+
This is useful mainly for data types that accept parameters.
180+
For example, a period dtype accepts a frequency parameter that
181+
can be set as ``period[H]`` (where H means hourly frequency).
182+
183+
By default, in the abstract class, just the name of the type is
184+
expected. But subclasses can overwrite this method to accept
185+
parameters.
178186
179187
Parameters
180188
----------
181189
string : str
190+
The name of the type, for example ``category``.
182191
183192
Returns
184193
-------
185-
self : instance of 'cls'
194+
ExtensionDtype
195+
Instance of the dtype.
186196
187197
Raises
188198
------
@@ -191,21 +201,26 @@ def construct_from_string(cls, string):
191201
192202
Examples
193203
--------
194-
If the extension dtype can be constructed without any arguments,
195-
the following may be an adequate implementation.
204+
For extension dtypes with arguments the following may be an
205+
adequate implementation.
196206
197207
>>> @classmethod
198-
... def construct_from_string(cls, string)
199-
... if string == cls.name:
200-
... return cls()
208+
... def construct_from_string(cls, string):
209+
... pattern = re.compile(r"^my_type\[(?P<arg_name>.+)\]$")
210+
... match = pattern.match(string)
211+
... if match:
212+
... return cls(**match.groupdict())
201213
... else:
202214
... raise TypeError("Cannot construct a '{}' from "
203-
... "'{}'".format(cls, string))
215+
... "'{}'".format(cls.__name__, string))
204216
"""
205-
raise AbstractMethodError(cls)
217+
if string != cls.name:
218+
raise TypeError("Cannot construct a '{}' from '{}'".format(
219+
cls.__name__, string))
220+
return cls()
206221

207222
@classmethod
208-
def is_dtype(cls, dtype):
223+
def is_dtype(cls, dtype) -> bool:
209224
"""Check if we match 'dtype'.
210225
211226
Parameters

pandas/core/dtypes/dtypes.py

-13
Original file line numberDiff line numberDiff line change
@@ -440,19 +440,6 @@ def construct_array_type(cls):
440440
from pandas import Categorical
441441
return Categorical
442442

443-
@classmethod
444-
def construct_from_string(cls, string):
445-
"""
446-
attempt to construct this type from a string, raise a TypeError if
447-
it's not possible """
448-
try:
449-
if string == 'category':
450-
return cls()
451-
else:
452-
raise TypeError("cannot construct a CategoricalDtype")
453-
except AttributeError:
454-
pass
455-
456443
@staticmethod
457444
def validate_ordered(ordered):
458445
"""

pandas/tests/dtypes/test_dtypes.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def test_equality(self):
8282
def test_construction_from_string(self):
8383
result = CategoricalDtype.construct_from_string('category')
8484
assert is_dtype_equal(self.dtype, result)
85-
msg = "cannot construct a CategoricalDtype"
85+
msg = "Cannot construct a 'CategoricalDtype' from 'foo'"
8686
with pytest.raises(TypeError, match=msg):
8787
CategoricalDtype.construct_from_string('foo')
8888

pandas/tests/extension/base/dtype.py

+14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import warnings
22

33
import numpy as np
4+
import pytest
45

56
import pandas as pd
67

@@ -89,3 +90,16 @@ def test_check_dtype(self, data):
8990

9091
def test_hashable(self, dtype):
9192
hash(dtype) # no error
93+
94+
def test_str(self, dtype):
95+
assert str(dtype) == dtype.name
96+
97+
def test_eq(self, dtype):
98+
assert dtype == dtype.name
99+
assert dtype != 'anonther_type'
100+
101+
def test_construct_from_string(self, dtype):
102+
dtype_instance = dtype.__class__.construct_from_string(dtype.name)
103+
assert isinstance(dtype_instance, dtype.__class__)
104+
with pytest.raises(TypeError):
105+
dtype.__class__.construct_from_string('another_type')

0 commit comments

Comments
 (0)