Skip to content

Commit

Permalink
BUG: Check for wrong arguments in index subclasses constructors (pand…
Browse files Browse the repository at this point in the history
  • Loading branch information
spacesphere authored and jreback committed Mar 10, 2018
1 parent 4131149 commit bf0dcb5
Show file tree
Hide file tree
Showing 10 changed files with 67 additions and 34 deletions.
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.23.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,7 @@ Indexing
- Bug in :func:`IntervalIndex.symmetric_difference` where the symmetric difference with a non-``IntervalIndex`` did not raise (:issue:`18475`)
- Bug in :class:`IntervalIndex` where set operations that returned an empty ``IntervalIndex`` had the wrong dtype (:issue:`19101`)
- Bug in :meth:`DataFrame.drop_duplicates` where no ``KeyError`` is raised when passing in columns that don't exist on the ``DataFrame`` (issue:`19726`)
- Bug in ``Index`` subclasses constructors that ignore unexpected keyword arguments (:issue:`19348`)


MultiIndex
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/indexes/category.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class CategoricalIndex(Index, accessor.PandasDelegate):
_attributes = ['name']

def __new__(cls, data=None, categories=None, ordered=None, dtype=None,
copy=False, name=None, fastpath=False, **kwargs):
copy=False, name=None, fastpath=False):

if fastpath:
return cls._simple_new(data, name=name, dtype=dtype)
Expand Down
16 changes: 9 additions & 7 deletions pandas/core/indexes/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ class DatetimeIndex(DatelikeOps, TimelikeOps, DatetimeIndexOpsMixin,
Attempt to infer fall dst-transition hours based on order
name : object
Name to be stored in the index
dayfirst : bool, default False
If True, parse dates in `data` with the day first order
yearfirst : bool, default False
If True parse dates in `data` with the year first order
Attributes
----------
Expand Down Expand Up @@ -272,6 +276,7 @@ class DatetimeIndex(DatelikeOps, TimelikeOps, DatetimeIndexOpsMixin,
Index : The base pandas Index type
TimedeltaIndex : Index of timedelta64 data
PeriodIndex : Index of Period data
pandas.to_datetime : Convert argument to datetime
"""

_typ = 'datetimeindex'
Expand Down Expand Up @@ -327,10 +332,10 @@ def _add_comparison_methods(cls):
@deprecate_kwarg(old_arg_name='infer_dst', new_arg_name='ambiguous',
mapping={True: 'infer', False: 'raise'})
def __new__(cls, data=None,
freq=None, start=None, end=None, periods=None,
copy=False, name=None, tz=None,
verify_integrity=True, normalize=False,
closed=None, ambiguous='raise', dtype=None, **kwargs):
freq=None, start=None, end=None, periods=None, tz=None,
normalize=False, closed=None, ambiguous='raise',
dayfirst=False, yearfirst=False, dtype=None,
copy=False, name=None, verify_integrity=True):

# This allows to later ensure that the 'copy' parameter is honored:
if isinstance(data, Index):
Expand All @@ -341,9 +346,6 @@ def __new__(cls, data=None,
if name is None and hasattr(data, 'name'):
name = data.name

dayfirst = kwargs.pop('dayfirst', None)
yearfirst = kwargs.pop('yearfirst', None)

freq_infer = False
if not isinstance(freq, DateOffset):

Expand Down
4 changes: 2 additions & 2 deletions pandas/core/indexes/interval.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,8 @@ class IntervalIndex(IntervalMixin, Index):

_mask = None

def __new__(cls, data, closed=None, name=None, copy=False, dtype=None,
fastpath=False, verify_integrity=True):
def __new__(cls, data, closed=None, dtype=None, copy=False,
name=None, fastpath=False, verify_integrity=True):

if fastpath:
return cls._simple_new(data.left, data.right, closed, name,
Expand Down
4 changes: 2 additions & 2 deletions pandas/core/indexes/multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,8 @@ class MultiIndex(Index):
rename = Index.set_names

def __new__(cls, levels=None, labels=None, sortorder=None, names=None,
copy=False, verify_integrity=True, _set_identity=True,
name=None, **kwargs):
dtype=None, copy=False, name=None,
verify_integrity=True, _set_identity=True):

# compat with Index
if name is not None:
Expand Down
13 changes: 10 additions & 3 deletions pandas/core/indexes/period.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,15 @@ def _add_comparison_methods(cls):
cls.__ge__ = _period_index_cmp('__ge__', cls)

def __new__(cls, data=None, ordinal=None, freq=None, start=None, end=None,
periods=None, copy=False, name=None, tz=None, dtype=None,
**kwargs):
periods=None, tz=None, dtype=None, copy=False, name=None,
**fields):

valid_field_set = {'year', 'month', 'day', 'quarter',
'hour', 'minute', 'second'}

if not set(fields).issubset(valid_field_set):
raise TypeError('__new__() got an unexpected keyword argument {}'.
format(list(set(fields) - valid_field_set)[0]))

if periods is not None:
if is_float(periods):
Expand Down Expand Up @@ -267,7 +274,7 @@ def __new__(cls, data=None, ordinal=None, freq=None, start=None, end=None,
data = np.asarray(ordinal, dtype=np.int64)
else:
data, freq = cls._generate_range(start, end, periods,
freq, kwargs)
freq, fields)
return cls._from_ordinals(data, name=name, freq=freq)

if isinstance(data, PeriodIndex):
Expand Down
6 changes: 3 additions & 3 deletions pandas/core/indexes/range.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ class RangeIndex(Int64Index):
_typ = 'rangeindex'
_engine_type = libindex.Int64Engine

def __new__(cls, start=None, stop=None, step=None, name=None, dtype=None,
fastpath=False, copy=False, **kwargs):
def __new__(cls, start=None, stop=None, step=None,
dtype=None, copy=False, name=None, fastpath=False):

if fastpath:
return cls._simple_new(start, stop, step, name=name)
Expand Down Expand Up @@ -550,7 +550,7 @@ def __getitem__(self, key):
stop = self._start + self._step * stop
step = self._step * step

return RangeIndex(start, stop, step, self.name, fastpath=True)
return RangeIndex(start, stop, step, name=self.name, fastpath=True)

# fall back to Int64Index
return super_getitem(key)
Expand Down
7 changes: 3 additions & 4 deletions pandas/core/indexes/timedeltas.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,9 @@ def _add_comparison_methods(cls):

freq = None

def __new__(cls, data=None, unit=None,
freq=None, start=None, end=None, periods=None,
copy=False, name=None,
closed=None, verify_integrity=True, **kwargs):
def __new__(cls, data=None, unit=None, freq=None, start=None, end=None,
periods=None, closed=None, dtype=None, copy=False,
name=None, verify_integrity=True):

if isinstance(data, TimedeltaIndex) and freq is None and name is None:
if copy:
Expand Down
7 changes: 7 additions & 0 deletions pandas/tests/indexes/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2326,3 +2326,10 @@ def test_generated_op_names(opname, indices):
opname = '__{name}__'.format(name=opname)
method = getattr(index, opname)
assert method.__name__ == opname


@pytest.mark.parametrize('idx_maker', tm.index_subclass_makers_generator())
def test_index_subclass_constructor_wrong_kwargs(idx_maker):
# GH #19348
with tm.assert_raises_regex(TypeError, 'unexpected keyword argument'):
idx_maker(foo='bar')
41 changes: 29 additions & 12 deletions pandas/util/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1539,16 +1539,16 @@ def makeUnicodeIndex(k=10, name=None):
return Index(randu_array(nchars=10, size=k), name=name)


def makeCategoricalIndex(k=10, n=3, name=None):
def makeCategoricalIndex(k=10, n=3, name=None, **kwargs):
""" make a length k index or n categories """
x = rands_array(nchars=4, size=n)
return CategoricalIndex(np.random.choice(x, k), name=name)
return CategoricalIndex(np.random.choice(x, k), name=name, **kwargs)


def makeIntervalIndex(k=10, name=None):
def makeIntervalIndex(k=10, name=None, **kwargs):
""" make a length k IntervalIndex """
x = np.linspace(0, 100, num=(k + 1))
return IntervalIndex.from_breaks(x, name=name)
return IntervalIndex.from_breaks(x, name=name, **kwargs)


def makeBoolIndex(k=10, name=None):
Expand All @@ -1567,31 +1567,37 @@ def makeUIntIndex(k=10, name=None):
return Index([2**63 + i for i in lrange(k)], name=name)


def makeRangeIndex(k=10, name=None):
return RangeIndex(0, k, 1, name=name)
def makeRangeIndex(k=10, name=None, **kwargs):
return RangeIndex(0, k, 1, name=name, **kwargs)


def makeFloatIndex(k=10, name=None):
values = sorted(np.random.random_sample(k)) - np.random.random_sample(1)
return Index(values * (10 ** np.random.randint(0, 9)), name=name)


def makeDateIndex(k=10, freq='B', name=None):
def makeDateIndex(k=10, freq='B', name=None, **kwargs):
dt = datetime(2000, 1, 1)
dr = bdate_range(dt, periods=k, freq=freq, name=name)
return DatetimeIndex(dr, name=name)
return DatetimeIndex(dr, name=name, **kwargs)


def makeTimedeltaIndex(k=10, freq='D', name=None):
return TimedeltaIndex(start='1 day', periods=k, freq=freq, name=name)
def makeTimedeltaIndex(k=10, freq='D', name=None, **kwargs):
return TimedeltaIndex(start='1 day', periods=k, freq=freq,
name=name, **kwargs)


def makePeriodIndex(k=10, name=None):
def makePeriodIndex(k=10, name=None, **kwargs):
dt = datetime(2000, 1, 1)
dr = PeriodIndex(start=dt, periods=k, freq='B', name=name)
dr = PeriodIndex(start=dt, periods=k, freq='B', name=name, **kwargs)
return dr


def makeMultiIndex(k=10, names=None, **kwargs):
return MultiIndex.from_product(
(('foo', 'bar'), (1, 2)), names=names, **kwargs)


def all_index_generator(k=10):
"""Generator which can be iterated over to get instances of all the various
index classes.
Expand All @@ -1609,6 +1615,17 @@ def all_index_generator(k=10):
yield make_index_func(k=k)


def index_subclass_makers_generator():
make_index_funcs = [
makeDateIndex, makePeriodIndex,
makeTimedeltaIndex, makeRangeIndex,
makeIntervalIndex, makeCategoricalIndex,
makeMultiIndex
]
for make_index_func in make_index_funcs:
yield make_index_func


def all_timeseries_index_generator(k=10):
"""Generator which can be iterated over to get instances of all the classes
which represent time-seires.
Expand Down

0 comments on commit bf0dcb5

Please sign in to comment.