diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 08f30f467dfa7..b764522d19bbe 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -235,6 +235,7 @@ MultiIndex ^^^^^^^^^^ - Bug in :meth:`MultiIndex.get_loc` where the first level is a :class:`DatetimeIndex` and a string key is passed (:issue:`42465`) - Bug in :meth:`MultiIndex.reindex` when passing a ``level`` that corresponds to an ``ExtensionDtype`` level (:issue:`42043`) +- Bug in :meth:`MultiIndex.get_loc` raising ``TypeError`` instead of ``KeyError`` on nested tuple (:issue:`42440`) - I/O diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 9b56e4cf89498..f7b42bce631f5 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -2846,9 +2846,17 @@ def _maybe_to_slice(loc): # needs linear search within the slice i = self._lexsort_depth lead_key, follow_key = key[:i], key[i:] - start, stop = ( - self.slice_locs(lead_key, lead_key) if lead_key else (0, len(self)) - ) + + if not lead_key: + start = 0 + stop = len(self) + else: + try: + start, stop = self.slice_locs(lead_key, lead_key) + except TypeError as err: + # e.g. test_groupby_example key = ((0, 0, 1, 2), "new_col") + # when self has 5 integer levels + raise KeyError(key) from err if start == stop: raise KeyError(key) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 7d92f7ff11ed3..9884fae7e5624 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -646,8 +646,8 @@ def _get_setitem_indexer(self, key): ax = self.obj._get_axis(0) - if isinstance(ax, MultiIndex) and self.name != "iloc": - with suppress(TypeError, KeyError, InvalidIndexError): + if isinstance(ax, MultiIndex) and self.name != "iloc" and is_hashable(key): + with suppress(KeyError, InvalidIndexError): # TypeError e.g. passed a bool return ax.get_loc(key) diff --git a/pandas/tests/indexes/multi/test_indexing.py b/pandas/tests/indexes/multi/test_indexing.py index ec7ddf8b4d67a..11037eaf4bb11 100644 --- a/pandas/tests/indexes/multi/test_indexing.py +++ b/pandas/tests/indexes/multi/test_indexing.py @@ -1,4 +1,5 @@ from datetime import timedelta +import re import numpy as np import pytest @@ -698,6 +699,14 @@ def test_multiindex_get_loc_list_raises(self): with pytest.raises(TypeError, match=msg): idx.get_loc([]) + def test_get_loc_nested_tuple_raises_keyerror(self): + # raise KeyError, not TypeError + mi = MultiIndex.from_product([range(3), range(4), range(5), range(6)]) + key = ((2, 3, 4), "foo") + + with pytest.raises(KeyError, match=re.escape(str(key))): + mi.get_loc(key) + class TestWhere: def test_where(self):