From 2341c9b7ce23450f1a1af52a281c6aa637927192 Mon Sep 17 00:00:00 2001 From: Brock Date: Wed, 7 Jul 2021 17:13:41 -0700 Subject: [PATCH 1/4] BUG: MultiIndex.get_loc re-raise TypeError as KeyError --- pandas/core/indexes/multi.py | 12 +++++++++--- pandas/core/indexing.py | 4 ++-- pandas/tests/indexes/multi/test_indexing.py | 9 +++++++++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 05580b03feba1..262d55fece80d 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -2862,9 +2862,15 @@ 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)) - ) + + try: + start, stop = ( + self.slice_locs(lead_key, lead_key) if lead_key else (0, len(self)) + ) + 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 e362213c52bd5..967affb5b1fdf 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -650,8 +650,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): From bc204b00a816a04205e57a5e67607e39bed93af3 Mon Sep 17 00:00:00 2001 From: Brock Date: Wed, 7 Jul 2021 17:16:13 -0700 Subject: [PATCH 2/4] do less inside try/except --- pandas/core/indexes/multi.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 262d55fece80d..59f22f6ed1122 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -2863,14 +2863,16 @@ def _maybe_to_slice(loc): i = self._lexsort_depth lead_key, follow_key = key[:i], key[i:] - try: - start, stop = ( - self.slice_locs(lead_key, lead_key) if lead_key else (0, len(self)) - ) - 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 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) From d8603a9e673f23217f2446b1939a3204ec923770 Mon Sep 17 00:00:00 2001 From: Brock Date: Thu, 8 Jul 2021 10:41:57 -0700 Subject: [PATCH 3/4] whatsnew --- doc/source/whatsnew/v1.4.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 92cf2bed9ca47..33540d94d8e59 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -223,6 +223,7 @@ Missing MultiIndex ^^^^^^^^^^ - 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:`42240`) - I/O From be02c0d390dc71528f3af85f501e4049d480a921 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Thu, 8 Jul 2021 12:53:15 -0700 Subject: [PATCH 4/4] Update doc/source/whatsnew/v1.4.0.rst Co-authored-by: attack68 <24256554+attack68@users.noreply.github.com> --- doc/source/whatsnew/v1.4.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 33540d94d8e59..bd48038c24e14 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -223,7 +223,7 @@ Missing MultiIndex ^^^^^^^^^^ - 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:`42240`) +- Bug in :meth:`MultiIndex.get_loc` raising ``TypeError`` instead of ``KeyError`` on nested tuple (:issue:`42440`) - I/O