Skip to content

Commit

Permalink
Backport PR #25338: Interval dtype fix (#25380)
Browse files Browse the repository at this point in the history
  • Loading branch information
meeseeksmachine authored and gfyoung committed Feb 20, 2019
1 parent f0cf03d commit 48e9955
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 12 deletions.
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.24.2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Fixed Regressions

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

.. _whatsnew_0242.enhancements:

Expand Down
19 changes: 12 additions & 7 deletions pandas/core/dtypes/dtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -932,13 +932,18 @@ def construct_from_string(cls, string):
attempt to construct this type from a string, raise a TypeError
if its not possible
"""
if (isinstance(string, compat.string_types) and
(string.startswith('interval') or
string.startswith('Interval'))):
return cls(string)
if not isinstance(string, compat.string_types):
msg = "a string needs to be passed, got type {typ}"
raise TypeError(msg.format(typ=type(string)))

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

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

@property
def type(self):
Expand Down Expand Up @@ -979,7 +984,7 @@ def is_dtype(cls, dtype):
return True
else:
return False
except ValueError:
except (ValueError, TypeError):
return False
else:
return False
Expand Down
14 changes: 9 additions & 5 deletions pandas/tests/dtypes/test_dtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,10 +501,11 @@ def test_construction_not_supported(self, subtype):
with pytest.raises(TypeError, match=msg):
IntervalDtype(subtype)

def test_construction_errors(self):
@pytest.mark.parametrize('subtype', ['xx', 'IntervalA', 'Interval[foo]'])
def test_construction_errors(self, subtype):
msg = 'could not construct IntervalDtype'
with pytest.raises(TypeError, match=msg):
IntervalDtype('xx')
IntervalDtype(subtype)

def test_construction_from_string(self):
result = IntervalDtype('interval[int64]')
Expand All @@ -513,7 +514,7 @@ def test_construction_from_string(self):
assert is_dtype_equal(self.dtype, result)

@pytest.mark.parametrize('string', [
'foo', 'foo[int64]', 0, 3.14, ('a', 'b'), None])
0, 3.14, ('a', 'b'), None])
def test_construction_from_string_errors(self, string):
# these are invalid entirely
msg = 'a string needs to be passed, got type'
Expand All @@ -522,10 +523,12 @@ def test_construction_from_string_errors(self, string):
IntervalDtype.construct_from_string(string)

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

with pytest.raises(TypeError, match=msg):
IntervalDtype.construct_from_string(string)
Expand All @@ -549,6 +552,7 @@ def test_is_dtype(self):
assert not IntervalDtype.is_dtype('U')
assert not IntervalDtype.is_dtype('S')
assert not IntervalDtype.is_dtype('foo')
assert not IntervalDtype.is_dtype('IntervalA')
assert not IntervalDtype.is_dtype(np.object_)
assert not IntervalDtype.is_dtype(np.int64)
assert not IntervalDtype.is_dtype(np.float64)
Expand Down
7 changes: 7 additions & 0 deletions pandas/tests/series/test_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,13 @@ def test_comp_ops_df_compat(self):
with pytest.raises(ValueError, match=msg):
left.to_frame() < right.to_frame()

def test_compare_series_interval_keyword(self):
# GH 25338
s = Series(['IntervalA', 'IntervalB', 'IntervalC'])
result = s == 'IntervalA'
expected = Series([True, False, False])
assert_series_equal(result, expected)


class TestSeriesFlexComparisonOps(object):

Expand Down

0 comments on commit 48e9955

Please sign in to comment.