Skip to content

Commit be5ef22

Browse files
zangell44Pingviinituutti
authored andcommitted
Interval dtype fix (pandas-dev#25338)
1 parent c2a70d2 commit be5ef22

File tree

4 files changed

+29
-12
lines changed

4 files changed

+29
-12
lines changed

doc/source/whatsnew/v0.24.2.rst

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Fixed Regressions
2626

2727
- Fixed regression in :meth:`DataFrame.duplicated()`, where empty dataframe was not returning a boolean dtyped Series. (:issue:`25184`)
2828
- Fixed regression in :meth:`Series.min` and :meth:`Series.max` where ``numeric_only=True`` was ignored when the ``Series`` contained ```Categorical`` data (:issue:`25299`)
29+
- Fixed regression in ``IntervalDtype`` construction where passing an incorrect string with 'Interval' as a prefix could result in a ``RecursionError``. (:issue:`25338`)
2930

3031
.. _whatsnew_0242.enhancements:
3132

pandas/core/dtypes/dtypes.py

+12-7
Original file line numberDiff line numberDiff line change
@@ -931,13 +931,18 @@ def construct_from_string(cls, string):
931931
attempt to construct this type from a string, raise a TypeError
932932
if its not possible
933933
"""
934-
if (isinstance(string, compat.string_types) and
935-
(string.startswith('interval') or
936-
string.startswith('Interval'))):
937-
return cls(string)
934+
if not isinstance(string, compat.string_types):
935+
msg = "a string needs to be passed, got type {typ}"
936+
raise TypeError(msg.format(typ=type(string)))
937+
938+
if (string.lower() == 'interval' or
939+
cls._match.search(string) is not None):
940+
return cls(string)
938941

939-
msg = "a string needs to be passed, got type {typ}"
940-
raise TypeError(msg.format(typ=type(string)))
942+
msg = ('Incorrectly formatted string passed to constructor. '
943+
'Valid formats include Interval or Interval[dtype] '
944+
'where dtype is numeric, datetime, or timedelta')
945+
raise TypeError(msg)
941946

942947
@property
943948
def type(self):
@@ -978,7 +983,7 @@ def is_dtype(cls, dtype):
978983
return True
979984
else:
980985
return False
981-
except ValueError:
986+
except (ValueError, TypeError):
982987
return False
983988
else:
984989
return False

pandas/tests/dtypes/test_dtypes.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -511,10 +511,11 @@ def test_construction_not_supported(self, subtype):
511511
with pytest.raises(TypeError, match=msg):
512512
IntervalDtype(subtype)
513513

514-
def test_construction_errors(self):
514+
@pytest.mark.parametrize('subtype', ['xx', 'IntervalA', 'Interval[foo]'])
515+
def test_construction_errors(self, subtype):
515516
msg = 'could not construct IntervalDtype'
516517
with pytest.raises(TypeError, match=msg):
517-
IntervalDtype('xx')
518+
IntervalDtype(subtype)
518519

519520
def test_construction_from_string(self):
520521
result = IntervalDtype('interval[int64]')
@@ -523,7 +524,7 @@ def test_construction_from_string(self):
523524
assert is_dtype_equal(self.dtype, result)
524525

525526
@pytest.mark.parametrize('string', [
526-
'foo', 'foo[int64]', 0, 3.14, ('a', 'b'), None])
527+
0, 3.14, ('a', 'b'), None])
527528
def test_construction_from_string_errors(self, string):
528529
# these are invalid entirely
529530
msg = 'a string needs to be passed, got type'
@@ -532,10 +533,12 @@ def test_construction_from_string_errors(self, string):
532533
IntervalDtype.construct_from_string(string)
533534

534535
@pytest.mark.parametrize('string', [
535-
'interval[foo]'])
536+
'foo', 'foo[int64]', 'IntervalA'])
536537
def test_construction_from_string_error_subtype(self, string):
537538
# this is an invalid subtype
538-
msg = 'could not construct IntervalDtype'
539+
msg = ("Incorrectly formatted string passed to constructor. "
540+
r"Valid formats include Interval or Interval\[dtype\] "
541+
"where dtype is numeric, datetime, or timedelta")
539542

540543
with pytest.raises(TypeError, match=msg):
541544
IntervalDtype.construct_from_string(string)
@@ -559,6 +562,7 @@ def test_is_dtype(self):
559562
assert not IntervalDtype.is_dtype('U')
560563
assert not IntervalDtype.is_dtype('S')
561564
assert not IntervalDtype.is_dtype('foo')
565+
assert not IntervalDtype.is_dtype('IntervalA')
562566
assert not IntervalDtype.is_dtype(np.object_)
563567
assert not IntervalDtype.is_dtype(np.int64)
564568
assert not IntervalDtype.is_dtype(np.float64)

pandas/tests/series/test_operators.py

+7
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,13 @@ def test_comp_ops_df_compat(self):
563563
with pytest.raises(ValueError, match=msg):
564564
left.to_frame() < right.to_frame()
565565

566+
def test_compare_series_interval_keyword(self):
567+
# GH 25338
568+
s = Series(['IntervalA', 'IntervalB', 'IntervalC'])
569+
result = s == 'IntervalA'
570+
expected = Series([True, False, False])
571+
assert_series_equal(result, expected)
572+
566573

567574
class TestSeriesFlexComparisonOps(object):
568575

0 commit comments

Comments
 (0)