Skip to content

[BUG] Construction of Interval restricted to numeric/ Timestamp/ Timedelta (#23013) #25768

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/source/whatsnew/v0.25.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ Strings
Interval
^^^^^^^^

-
- Construction of :class:`Interval` is restricted to numeric, :class:`Timestamp` and :class:`Timedelta` endpoints (:issue:`23013`)
-
-

Expand Down
14 changes: 14 additions & 0 deletions pandas/_libs/interval.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ cnp.import_array()
cimport pandas._libs.util as util

from pandas._libs.hashtable cimport Int64Vector, Int64VectorData
from pandas._libs.tslibs.util cimport is_integer_object, is_float_object

from pandas._libs.tslibs import Timestamp
from pandas._libs.tslibs.timedeltas import Timedelta
from pandas._libs.tslibs.timezones cimport tz_compare


Expand Down Expand Up @@ -250,6 +252,10 @@ cdef class Interval(IntervalMixin):
def __init__(self, left, right, str closed='right'):
# note: it is faster to just do these checks than to use a special
# constructor (__cinit__/__new__) to avoid them

self._validate_endpoint(left)
self._validate_endpoint(right)

if closed not in _VALID_CLOSED:
msg = "invalid option for 'closed': {closed}".format(closed=closed)
raise ValueError(msg)
Expand All @@ -266,6 +272,14 @@ cdef class Interval(IntervalMixin):
self.right = right
self.closed = closed

def _validate_endpoint(self, endpoint):
# GH 23013
if not (is_integer_object(endpoint) or is_float_object(endpoint) or
isinstance(endpoint, (Timestamp, Timedelta))):
msg = ("Only numeric, Timestamp and Timedelta endpoints "
"are allowed when constructing an Interval.")
raise ValueError(msg)

def __hash__(self):
return hash((self.left, self.right, self.closed))

Expand Down
6 changes: 6 additions & 0 deletions pandas/tests/indexes/interval/test_construction.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,12 @@ def test_generic_errors(self, constructor):
"""
pass

def test_constructor_string(self):
# GH23013
# When forming the interval from breaks,
# the interval of strings is already forbidden.
pass

def test_constructor_errors(self, constructor):
# mismatched closed within intervals with no constructor override
ivs = [Interval(0, 1, closed='right'), Interval(2, 3, closed='left')]
Expand Down
17 changes: 9 additions & 8 deletions pandas/tests/scalar/interval/test_interval.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import numpy as np
import pytest

from pandas import Interval, Timedelta, Timestamp
from pandas import Interval, Period, Timedelta, Timestamp
import pandas.core.common as com


Expand Down Expand Up @@ -100,13 +100,14 @@ def test_length_timestamp(self, tz, left, right, expected):
('a', 'z'),
(('a', 'b'), ('c', 'd')),
(list('AB'), list('ab')),
(Interval(0, 1), Interval(1, 2))])
def test_length_errors(self, left, right):
# GH 18789
iv = Interval(left, right)
msg = 'cannot compute length between .* and .*'
with pytest.raises(TypeError, match=msg):
iv.length
(Interval(0, 1), Interval(1, 2)),
(Period('2018Q1', freq='Q'), Period('2018Q1', freq='Q'))
])
def test_construct_errors(self, left, right):
# GH 23013
msg = "Only numeric, Timestamp and Timedelta endpoints are allowed"
with pytest.raises(ValueError, match=msg):
Interval(left, right)

def test_math_add(self, closed):
interval = Interval(0, 1, closed=closed)
Expand Down