Skip to content

Commit 5bac73f

Browse files
committed
BUG: Check for wrong arguments in index subclasses constructors
Tidy up constructor signatures for different types of index
1 parent 63ce781 commit 5bac73f

File tree

10 files changed

+70
-34
lines changed

10 files changed

+70
-34
lines changed

doc/source/whatsnew/v0.23.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,7 @@ Indexing
932932
- Bug in :func:`IntervalIndex.symmetric_difference` where the symmetric difference with a non-``IntervalIndex`` did not raise (:issue:`18475`)
933933
- Bug in :class:`IntervalIndex` where set operations that returned an empty ``IntervalIndex`` had the wrong dtype (:issue:`19101`)
934934
- Bug in :meth:`DataFrame.drop_duplicates` where no ``KeyError`` is raised when passing in columns that don't exist on the ``DataFrame`` (issue:`19726`)
935+
- Bug in ``Index`` subclasses constructors that ignore unexpected keyword arguments (:issue:`19348`)
935936

936937

937938
MultiIndex

pandas/core/indexes/category.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ class CategoricalIndex(Index, accessor.PandasDelegate):
7676
_attributes = ['name']
7777

7878
def __new__(cls, data=None, categories=None, ordered=None, dtype=None,
79-
copy=False, name=None, fastpath=False, **kwargs):
79+
copy=False, name=None, fastpath=False):
8080

8181
if fastpath:
8282
return cls._simple_new(data, name=name, dtype=dtype)

pandas/core/indexes/datetimes.py

+10-7
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,12 @@ class DatetimeIndex(DatelikeOps, TimelikeOps, DatetimeIndexOpsMixin,
213213
Attempt to infer fall dst-transition hours based on order
214214
name : object
215215
Name to be stored in the index
216+
dayfirst : bool, default False
217+
If True, parse dates in `data` with the day first order
218+
(see `pandas.to_datetime`)
219+
yearfirst : bool, default False
220+
If True parse dates in `data` with the year first order
221+
(see `pandas.to_datetime`)
216222
217223
Attributes
218224
----------
@@ -327,10 +333,10 @@ def _add_comparison_methods(cls):
327333
@deprecate_kwarg(old_arg_name='infer_dst', new_arg_name='ambiguous',
328334
mapping={True: 'infer', False: 'raise'})
329335
def __new__(cls, data=None,
330-
freq=None, start=None, end=None, periods=None,
331-
copy=False, name=None, tz=None,
332-
verify_integrity=True, normalize=False,
333-
closed=None, ambiguous='raise', dtype=None, **kwargs):
336+
freq=None, start=None, end=None, periods=None, tz=None,
337+
verify_integrity=True, normalize=False, closed=None,
338+
ambiguous='raise', dayfirst=False, yearfirst=False,
339+
dtype=None, copy=False, name=None):
334340

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

344-
dayfirst = kwargs.pop('dayfirst', None)
345-
yearfirst = kwargs.pop('yearfirst', None)
346-
347350
freq_infer = False
348351
if not isinstance(freq, DateOffset):
349352

pandas/core/indexes/interval.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,8 @@ class IntervalIndex(IntervalMixin, Index):
213213

214214
_mask = None
215215

216-
def __new__(cls, data, closed=None, name=None, copy=False, dtype=None,
217-
fastpath=False, verify_integrity=True):
216+
def __new__(cls, data, closed=None, verify_integrity=True,
217+
dtype=None, copy=False, name=None, fastpath=False):
218218

219219
if fastpath:
220220
return cls._simple_new(data.left, data.right, closed, name,

pandas/core/indexes/multi.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,8 @@ class MultiIndex(Index):
208208
rename = Index.set_names
209209

210210
def __new__(cls, levels=None, labels=None, sortorder=None, names=None,
211-
copy=False, verify_integrity=True, _set_identity=True,
212-
name=None, **kwargs):
211+
verify_integrity=True, _set_identity=True,
212+
dtype=None, copy=False, name=None):
213213

214214
# compat with Index
215215
if name is not None:

pandas/core/indexes/period.py

+12-3
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,17 @@ def _add_comparison_methods(cls):
234234
cls.__ge__ = _period_index_cmp('__ge__', cls)
235235

236236
def __new__(cls, data=None, ordinal=None, freq=None, start=None, end=None,
237-
periods=None, copy=False, name=None, tz=None, dtype=None,
238-
**kwargs):
237+
periods=None, tz=None, dtype=None, copy=False, name=None,
238+
**fields):
239+
240+
valid_field_set = {
241+
'year', 'month', 'day', 'quarter',
242+
'hour', 'minute', 'second'
243+
}
244+
245+
if not set(fields).issubset(valid_field_set):
246+
raise TypeError('__new__() got an unexpected keyword argument {}'.
247+
format(list(set(fields) - valid_field_set)[0]))
239248

240249
if periods is not None:
241250
if is_float(periods):
@@ -267,7 +276,7 @@ def __new__(cls, data=None, ordinal=None, freq=None, start=None, end=None,
267276
data = np.asarray(ordinal, dtype=np.int64)
268277
else:
269278
data, freq = cls._generate_range(start, end, periods,
270-
freq, kwargs)
279+
freq, fields)
271280
return cls._from_ordinals(data, name=name, freq=freq)
272281

273282
if isinstance(data, PeriodIndex):

pandas/core/indexes/range.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ class RangeIndex(Int64Index):
6565
_typ = 'rangeindex'
6666
_engine_type = libindex.Int64Engine
6767

68-
def __new__(cls, start=None, stop=None, step=None, name=None, dtype=None,
69-
fastpath=False, copy=False, **kwargs):
68+
def __new__(cls, start=None, stop=None, step=None,
69+
dtype=None, copy=False, name=None, fastpath=False):
7070

7171
if fastpath:
7272
return cls._simple_new(start, stop, step, name=name)
@@ -550,7 +550,7 @@ def __getitem__(self, key):
550550
stop = self._start + self._step * stop
551551
step = self._step * step
552552

553-
return RangeIndex(start, stop, step, self.name, fastpath=True)
553+
return RangeIndex(start, stop, step, name=self.name, fastpath=True)
554554

555555
# fall back to Int64Index
556556
return super_getitem(key)

pandas/core/indexes/timedeltas.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -197,10 +197,9 @@ def _add_comparison_methods(cls):
197197

198198
freq = None
199199

200-
def __new__(cls, data=None, unit=None,
201-
freq=None, start=None, end=None, periods=None,
202-
copy=False, name=None,
203-
closed=None, verify_integrity=True, **kwargs):
200+
def __new__(cls, data=None, unit=None, freq=None, start=None, end=None,
201+
periods=None, closed=None, verify_integrity=True,
202+
dtype=None, copy=False, name=None):
204203

205204
if isinstance(data, TimedeltaIndex) and freq is None and name is None:
206205
if copy:

pandas/tests/indexes/test_base.py

+7
Original file line numberDiff line numberDiff line change
@@ -2326,3 +2326,10 @@ def test_generated_op_names(opname, indices):
23262326
opname = '__{name}__'.format(name=opname)
23272327
method = getattr(index, opname)
23282328
assert method.__name__ == opname
2329+
2330+
2331+
@pytest.mark.parametrize('idx_maker', tm.index_subclass_makers_generator())
2332+
def test_index_subclass_constructor_wrong_kwargs(idx_maker):
2333+
# GH #19348
2334+
with tm.assert_raises_regex(TypeError, 'unexpected keyword argument'):
2335+
idx_maker(foo='bar')

pandas/util/testing.py

+29-12
Original file line numberDiff line numberDiff line change
@@ -1539,16 +1539,16 @@ def makeUnicodeIndex(k=10, name=None):
15391539
return Index(randu_array(nchars=10, size=k), name=name)
15401540

15411541

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

15471547

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

15531553

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

15691569

1570-
def makeRangeIndex(k=10, name=None):
1571-
return RangeIndex(0, k, 1, name=name)
1570+
def makeRangeIndex(k=10, name=None, **kwargs):
1571+
return RangeIndex(0, k, 1, name=name, **kwargs)
15721572

15731573

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

15781578

1579-
def makeDateIndex(k=10, freq='B', name=None):
1579+
def makeDateIndex(k=10, freq='B', name=None, **kwargs):
15801580
dt = datetime(2000, 1, 1)
15811581
dr = bdate_range(dt, periods=k, freq=freq, name=name)
1582-
return DatetimeIndex(dr, name=name)
1582+
return DatetimeIndex(dr, name=name, **kwargs)
15831583

15841584

1585-
def makeTimedeltaIndex(k=10, freq='D', name=None):
1586-
return TimedeltaIndex(start='1 day', periods=k, freq=freq, name=name)
1585+
def makeTimedeltaIndex(k=10, freq='D', name=None, **kwargs):
1586+
return TimedeltaIndex(start='1 day', periods=k, freq=freq,
1587+
name=name, **kwargs)
15871588

15881589

1589-
def makePeriodIndex(k=10, name=None):
1590+
def makePeriodIndex(k=10, name=None, **kwargs):
15901591
dt = datetime(2000, 1, 1)
1591-
dr = PeriodIndex(start=dt, periods=k, freq='B', name=name)
1592+
dr = PeriodIndex(start=dt, periods=k, freq='B', name=name, **kwargs)
15921593
return dr
15931594

15941595

1596+
def makeMultiIndex(k=10, names=None, **kwargs):
1597+
return MultiIndex.from_product(
1598+
(('foo', 'bar'), (1, 2)), names=names, **kwargs)
1599+
1600+
15951601
def all_index_generator(k=10):
15961602
"""Generator which can be iterated over to get instances of all the various
15971603
index classes.
@@ -1609,6 +1615,17 @@ def all_index_generator(k=10):
16091615
yield make_index_func(k=k)
16101616

16111617

1618+
def index_subclass_makers_generator():
1619+
make_index_funcs = [
1620+
makeDateIndex, makePeriodIndex,
1621+
makeTimedeltaIndex, makeRangeIndex,
1622+
makeIntervalIndex, makeCategoricalIndex,
1623+
makeMultiIndex
1624+
]
1625+
for make_index_func in make_index_funcs:
1626+
yield make_index_func
1627+
1628+
16121629
def all_timeseries_index_generator(k=10):
16131630
"""Generator which can be iterated over to get instances of all the classes
16141631
which represent time-seires.

0 commit comments

Comments
 (0)