From d974f28da7fac5c8515b6b28ef196e3d43795c81 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 10 Feb 2020 17:10:29 -0800 Subject: [PATCH 1/5] API: always raise KeyError on __getitem__ and loc.__getitem__, never TypeError --- pandas/core/indexes/base.py | 6 +-- pandas/core/indexes/category.py | 2 +- pandas/core/indexes/datetimelike.py | 4 +- pandas/core/indexing.py | 2 +- pandas/tests/frame/test_constructors.py | 6 +-- pandas/tests/indexing/test_categorical.py | 6 +-- pandas/tests/indexing/test_floats.py | 55 +++++++++-------------- pandas/tests/indexing/test_loc.py | 6 +-- pandas/tests/indexing/test_scalar.py | 16 ++----- 9 files changed, 38 insertions(+), 65 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 719bf13cbd313..9c439fdb3b218 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -3121,7 +3121,7 @@ def _convert_scalar_indexer(self, key, kind: str_t): if kind == "getitem" and is_float(key): if not self.is_floating(): - self._invalid_indexer("label", key) + raise KeyError(key) elif kind == "loc" and is_float(key): @@ -3135,11 +3135,11 @@ def _convert_scalar_indexer(self, key, kind: str_t): "string", "mixed", ]: - self._invalid_indexer("label", key) + raise KeyError(key) elif kind == "loc" and is_integer(key): if not self.holds_integer(): - self._invalid_indexer("label", key) + raise KeyError(key) return key diff --git a/pandas/core/indexes/category.py b/pandas/core/indexes/category.py index 7373f41daefa4..54cd153a414e4 100644 --- a/pandas/core/indexes/category.py +++ b/pandas/core/indexes/category.py @@ -598,7 +598,7 @@ def _convert_scalar_indexer(self, key, kind: str): try: return self.categories._convert_scalar_indexer(key, kind="loc") except TypeError: - self._invalid_indexer("label", key) + raise KeyError(key) return super()._convert_scalar_indexer(key, kind=kind) @Appender(Index._convert_list_indexer.__doc__) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 941b6c876bb36..64b44f4d27e31 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -397,9 +397,9 @@ def _convert_scalar_indexer(self, key, kind: str): is_int = is_integer(key) is_flt = is_float(key) if kind == "loc" and (is_int or is_flt): - self._invalid_indexer("label", key) + raise KeyError(key) elif kind == "getitem" and is_flt: - self._invalid_indexer("label", key) + raise KeyError(key) return super()._convert_scalar_indexer(key, kind=kind) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index c7dcccab00d95..03ba5ab1fbcda 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -1176,7 +1176,7 @@ def _convert_to_indexer(self, key, axis: int, is_setter: bool = False): # try to find out correct indexer, if not type correct raise try: key = labels._convert_scalar_indexer(key, kind="loc") - except TypeError: + except KeyError: # but we will allow setting if not is_setter: raise diff --git a/pandas/tests/frame/test_constructors.py b/pandas/tests/frame/test_constructors.py index 5f4c78449f71d..d13beb5f04a1c 100644 --- a/pandas/tests/frame/test_constructors.py +++ b/pandas/tests/frame/test_constructors.py @@ -1859,11 +1859,7 @@ def check(df): # No NaN found -> error if len(indexer) == 0: - msg = ( - "cannot do label indexing on RangeIndex " - r"with these indexers \[nan\] of type float" - ) - with pytest.raises(TypeError, match=msg): + with pytest.raises(KeyError, match="^nan$"): df.loc[:, np.nan] # single nan should result in Series elif len(indexer) == 1: diff --git a/pandas/tests/indexing/test_categorical.py b/pandas/tests/indexing/test_categorical.py index da935b1c911d0..8a8ac584c16c2 100644 --- a/pandas/tests/indexing/test_categorical.py +++ b/pandas/tests/indexing/test_categorical.py @@ -82,11 +82,7 @@ def test_loc_scalar(self): with pytest.raises(TypeError, match=msg): df.loc["d", "C"] = 10 - msg = ( - "cannot do label indexing on CategoricalIndex with these " - r"indexers \[1\] of type int" - ) - with pytest.raises(TypeError, match=msg): + with pytest.raises(KeyError, match="^1$"): df.loc[1] def test_getitem_scalar(self): diff --git a/pandas/tests/indexing/test_floats.py b/pandas/tests/indexing/test_floats.py index 6cc18a3989266..1b44dbb65e992 100644 --- a/pandas/tests/indexing/test_floats.py +++ b/pandas/tests/indexing/test_floats.py @@ -86,11 +86,9 @@ def test_scalar_non_numeric(self, index_func): # getting for idxr, getitem in [(lambda x: x.iloc, False), (lambda x: x, True)]: - # gettitem on a DataFrame is a KeyError as it is indexing - # via labels on the columns - if getitem and isinstance(s, DataFrame): + if getitem: error = KeyError - msg = r"^3(\.0)?$" + msg = r"^3\.0$" else: error = TypeError msg = ( @@ -109,6 +107,9 @@ def test_scalar_non_numeric(self, index_func): "string", "unicode", "mixed", + "period", + "timedelta64", + "datetime64", }: error = KeyError msg = r"^3\.0$" @@ -163,12 +164,7 @@ def test_scalar_non_numeric(self, index_func): # fallsback to position selection, series only s = Series(np.arange(len(i)), index=i) s[3] - msg = ( - r"cannot do (label|positional) indexing " - r"on {klass} with these indexers \[3\.0\] of " - r"type float".format(klass=type(i).__name__) - ) - with pytest.raises(TypeError, match=msg): + with pytest.raises(KeyError, match="^3.0$"): s[3.0] def test_scalar_with_mixed(self): @@ -178,18 +174,18 @@ def test_scalar_with_mixed(self): # lookup in a pure stringstr # with an invalid indexer - for idxr in [lambda x: x, lambda x: x.iloc]: - - msg = ( - r"cannot do label indexing " - r"on {klass} with these indexers \[1\.0\] of " - r"type float|" - "Cannot index by location index with a non-integer key".format( - klass=Index.__name__ - ) + msg = ( + r"cannot do label indexing " + r"on {klass} with these indexers \[1\.0\] of " + r"type float|" + "Cannot index by location index with a non-integer key".format( + klass=Index.__name__ ) - with pytest.raises(TypeError, match=msg): - idxr(s2)[1.0] + ) + with pytest.raises(KeyError, match="^1.0$"): + s2[1.0] + with pytest.raises(TypeError, match=msg): + s2.iloc[1.0] with pytest.raises(KeyError, match=r"^1\.0$"): s2.loc[1.0] @@ -200,19 +196,12 @@ def test_scalar_with_mixed(self): # mixed index so we have label # indexing - for idxr in [lambda x: x]: + with pytest.raises(KeyError, match="^1.0$"): + s3[1.0] - msg = ( - r"cannot do label indexing " - r"on {klass} with these indexers \[1\.0\] of " - r"type float".format(klass=Index.__name__) - ) - with pytest.raises(TypeError, match=msg): - idxr(s3)[1.0] - - result = idxr(s3)[1] - expected = 2 - assert result == expected + result = s3[1] + expected = 2 + assert result == expected msg = "Cannot index by location index with a non-integer key" with pytest.raises(TypeError, match=msg): diff --git a/pandas/tests/indexing/test_loc.py b/pandas/tests/indexing/test_loc.py index 02652d993e0f3..f4c6900dfcf74 100644 --- a/pandas/tests/indexing/test_loc.py +++ b/pandas/tests/indexing/test_loc.py @@ -16,7 +16,7 @@ class TestLoc(Base): def test_loc_getitem_int(self): # int label - self.check_result("loc", 2, typs=["labels"], fails=TypeError) + self.check_result("loc", 2, typs=["labels"], fails=KeyError) def test_loc_getitem_label(self): @@ -34,8 +34,8 @@ def test_loc_getitem_label_out_of_range(self): self.check_result( "loc", 20, typs=["ints", "uints", "mixed"], fails=KeyError, ) - self.check_result("loc", 20, typs=["labels"], fails=TypeError) - self.check_result("loc", 20, typs=["ts"], axes=0, fails=TypeError) + self.check_result("loc", 20, typs=["labels"], fails=KeyError) + self.check_result("loc", 20, typs=["ts"], axes=0, fails=KeyError) self.check_result("loc", 20, typs=["floats"], axes=0, fails=KeyError) def test_loc_getitem_label_list(self): diff --git a/pandas/tests/indexing/test_scalar.py b/pandas/tests/indexing/test_scalar.py index 899c58eb5edea..aff1d5e6e7f5e 100644 --- a/pandas/tests/indexing/test_scalar.py +++ b/pandas/tests/indexing/test_scalar.py @@ -137,13 +137,9 @@ def test_series_at_raises_type_error(self): result = ser.loc["a"] assert result == 1 - msg = ( - "cannot do label indexing on Index " - r"with these indexers \[0\] of type int" - ) - with pytest.raises(TypeError, match=msg): + with pytest.raises(KeyError, match="^0$"): ser.at[0] - with pytest.raises(TypeError, match=msg): + with pytest.raises(KeyError, match="^0$"): ser.loc[0] def test_frame_raises_type_error(self): @@ -154,13 +150,9 @@ def test_frame_raises_type_error(self): result = df.loc["a", "A"] assert result == 1 - msg = ( - "cannot do label indexing on Index " - r"with these indexers \[0\] of type int" - ) - with pytest.raises(TypeError, match=msg): + with pytest.raises(KeyError, match="^0$"): df.at["a", 0] - with pytest.raises(TypeError, match=msg): + with pytest.raises(KeyError, match="^0$"): df.loc["a", 0] def test_series_at_raises_key_error(self): From e6dd8b2adff33698497da5bfdb0c14543f812005 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 10 Feb 2020 19:22:04 -0800 Subject: [PATCH 2/5] pass instead of raising --- pandas/core/indexes/base.py | 4 ++-- pandas/core/indexes/datetimelike.py | 4 +--- pandas/core/indexes/numeric.py | 8 -------- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 9c439fdb3b218..ec89f8c2a29ae 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -3121,7 +3121,7 @@ def _convert_scalar_indexer(self, key, kind: str_t): if kind == "getitem" and is_float(key): if not self.is_floating(): - raise KeyError(key) + pass elif kind == "loc" and is_float(key): @@ -3135,7 +3135,7 @@ def _convert_scalar_indexer(self, key, kind: str_t): "string", "mixed", ]: - raise KeyError(key) + pass elif kind == "loc" and is_integer(key): if not self.holds_integer(): diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 64b44f4d27e31..ccb14f24bd8ea 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -396,9 +396,7 @@ def _convert_scalar_indexer(self, key, kind: str): # we don't allow float indexing for getitem is_int = is_integer(key) is_flt = is_float(key) - if kind == "loc" and (is_int or is_flt): - raise KeyError(key) - elif kind == "getitem" and is_flt: + if kind == "loc" and is_int: raise KeyError(key) return super()._convert_scalar_indexer(key, kind=kind) diff --git a/pandas/core/indexes/numeric.py b/pandas/core/indexes/numeric.py index 877b3d1d2ba30..85cbb1621227e 100644 --- a/pandas/core/indexes/numeric.py +++ b/pandas/core/indexes/numeric.py @@ -249,14 +249,6 @@ def asi8(self) -> np.ndarray: # do not cache or you'll create a memory leak return self.values.view(self._default_dtype) - @Appender(Index._convert_scalar_indexer.__doc__) - def _convert_scalar_indexer(self, key, kind: str): - assert kind in ["loc", "getitem"] - - # never iloc, which we don't coerce to integers - key = self._maybe_cast_indexer(key) - return super()._convert_scalar_indexer(key, kind=kind) - class Int64Index(IntegerIndex): __doc__ = _num_index_shared_docs["class_descr"] % _int64_descr_args From 951a8d256e368a66172f114ababa178845be8973 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 10 Feb 2020 20:27:24 -0800 Subject: [PATCH 3/5] checkpoint passing --- pandas/core/indexes/base.py | 23 +++-------------------- pandas/core/indexes/datetimelike.py | 2 +- pandas/core/indexes/numeric.py | 10 +++++----- pandas/core/indexing.py | 10 ++++++++++ 4 files changed, 19 insertions(+), 26 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index ec89f8c2a29ae..76a42ccc836af 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -3119,27 +3119,10 @@ def _convert_scalar_indexer(self, key, kind: str_t): # or label indexing if we are using a type able # to be represented in the index - if kind == "getitem" and is_float(key): - if not self.is_floating(): - pass - - elif kind == "loc" and is_float(key): - - # we want to raise KeyError on string/mixed here - # technically we *could* raise a TypeError - # on anything but mixed though - if self.inferred_type not in [ - "floating", - "mixed-integer-float", - "integer-na", - "string", - "mixed", - ]: - pass - - elif kind == "loc" and is_integer(key): + if kind == "loc" and is_integer(key): if not self.holds_integer(): - raise KeyError(key) + # Needed for test_series_at_raises_type_error + pass#raise KeyError(key) return key diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index ccb14f24bd8ea..ff0b505d36bac 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -397,7 +397,7 @@ def _convert_scalar_indexer(self, key, kind: str): is_int = is_integer(key) is_flt = is_float(key) if kind == "loc" and is_int: - raise KeyError(key) + pass#raise KeyError(key) return super()._convert_scalar_indexer(key, kind=kind) diff --git a/pandas/core/indexes/numeric.py b/pandas/core/indexes/numeric.py index 85cbb1621227e..79dbe349f4aac 100644 --- a/pandas/core/indexes/numeric.py +++ b/pandas/core/indexes/numeric.py @@ -378,11 +378,11 @@ def astype(self, dtype, copy=True): def _should_fallback_to_positional(self): return False - @Appender(Index._convert_scalar_indexer.__doc__) - def _convert_scalar_indexer(self, key, kind: str): - assert kind in ["loc", "getitem"] - # no-op for non-iloc - return key + #@Appender(Index._convert_scalar_indexer.__doc__) + #def _convert_scalar_indexer(self, key, kind: str): + # assert kind in ["loc", "getitem"] + # # no-op for non-iloc + # return key @Appender(Index._convert_slice_indexer.__doc__) def _convert_slice_indexer(self, key: slice, kind: str): diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 03ba5ab1fbcda..263c9ea1799ea 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -2113,6 +2113,16 @@ def _convert_key(self, key, is_setter: bool = False): return tuple(lkey) + def __getitem__(self, key): + if self.ndim != 1 or not is_scalar(key): + # FIXME: is_scalar check is a kludge + return super().__getitem__(key) + + # Like Index.get_value, but we do not allow positional fallback + obj = self.obj + loc = obj.index.get_loc(key) + return obj.index._get_values_for_loc(obj, loc, key) + @Appender(IndexingMixin.iat.__doc__) class _iAtIndexer(_ScalarAccessIndexer): From 49ec328e19cce187b6a9f8a2bddd3722ba545b11 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 11 Feb 2020 07:29:59 -0800 Subject: [PATCH 4/5] remove _convert_scalar_indexer --- pandas/core/indexes/base.py | 25 ------------------------- pandas/core/indexes/category.py | 10 ---------- pandas/core/indexes/datetimelike.py | 26 -------------------------- pandas/core/indexes/interval.py | 6 ------ pandas/core/indexes/numeric.py | 6 ------ pandas/core/indexing.py | 26 ++------------------------ pandas/core/series.py | 4 ---- 7 files changed, 2 insertions(+), 101 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 76a42ccc836af..1e009334aaff1 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -3101,31 +3101,6 @@ def _filter_indexer_tolerance( # -------------------------------------------------------------------- # Indexer Conversion Methods - def _convert_scalar_indexer(self, key, kind: str_t): - """ - Convert a scalar indexer. - - Parameters - ---------- - key : label of the slice bound - kind : {'loc', 'getitem'} - """ - assert kind in ["loc", "getitem"] - - if len(self) and not isinstance(self, ABCMultiIndex): - - # we can raise here if we are definitive that this - # is positional indexing (eg. .loc on with a float) - # or label indexing if we are using a type able - # to be represented in the index - - if kind == "loc" and is_integer(key): - if not self.holds_integer(): - # Needed for test_series_at_raises_type_error - pass#raise KeyError(key) - - return key - def _validate_positional_slice(self, key: slice): """ For positional indexing, a slice must have either int or None diff --git a/pandas/core/indexes/category.py b/pandas/core/indexes/category.py index 54cd153a414e4..923d5763d3d87 100644 --- a/pandas/core/indexes/category.py +++ b/pandas/core/indexes/category.py @@ -591,16 +591,6 @@ def get_indexer_non_unique(self, target): indexer, missing = self._engine.get_indexer_non_unique(codes) return ensure_platform_int(indexer), missing - @Appender(Index._convert_scalar_indexer.__doc__) - def _convert_scalar_indexer(self, key, kind: str): - assert kind in ["loc", "getitem"] - if kind == "loc": - try: - return self.categories._convert_scalar_indexer(key, kind="loc") - except TypeError: - raise KeyError(key) - return super()._convert_scalar_indexer(key, kind=kind) - @Appender(Index._convert_list_indexer.__doc__) def _convert_list_indexer(self, keyarr): # Return our indexer or raise if all of the values are not included in diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index ff0b505d36bac..8f93268c1e232 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -17,7 +17,6 @@ is_bool_dtype, is_categorical_dtype, is_dtype_equal, - is_float, is_integer, is_list_like, is_period_dtype, @@ -376,31 +375,6 @@ def _format_attrs(self): # -------------------------------------------------------------------- # Indexing Methods - def _convert_scalar_indexer(self, key, kind: str): - """ - We don't allow integer or float indexing on datetime-like when using - loc. - - Parameters - ---------- - key : label of the slice bound - kind : {'loc', 'getitem'} - """ - - assert kind in ["loc", "getitem"] - - if not is_scalar(key): - raise TypeError(key) - - # we don't allow integer/float indexing for loc - # we don't allow float indexing for getitem - is_int = is_integer(key) - is_flt = is_float(key) - if kind == "loc" and is_int: - pass#raise KeyError(key) - - return super()._convert_scalar_indexer(key, kind=kind) - def _validate_partial_date_slice(self, reso: str): raise NotImplementedError diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 9c4cd6cf72d35..fc8c213b7ea70 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -515,12 +515,6 @@ def _should_fallback_to_positional(self): # positional in this case return self.dtype.subtype.kind in ["m", "M"] - @Appender(Index._convert_scalar_indexer.__doc__) - def _convert_scalar_indexer(self, key, kind: str): - assert kind in ["getitem", "loc"] - # never iloc, so no-op - return key - def _maybe_cast_slice_bound(self, label, side, kind): return getattr(self, side)._maybe_cast_slice_bound(label, side, kind) diff --git a/pandas/core/indexes/numeric.py b/pandas/core/indexes/numeric.py index 79dbe349f4aac..f5a1dee8c125e 100644 --- a/pandas/core/indexes/numeric.py +++ b/pandas/core/indexes/numeric.py @@ -378,12 +378,6 @@ def astype(self, dtype, copy=True): def _should_fallback_to_positional(self): return False - #@Appender(Index._convert_scalar_indexer.__doc__) - #def _convert_scalar_indexer(self, key, kind: str): - # assert kind in ["loc", "getitem"] - # # no-op for non-iloc - # return key - @Appender(Index._convert_slice_indexer.__doc__) def _convert_slice_indexer(self, key: slice, kind: str): assert kind in ["loc", "getitem"] diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index b898c20175ab6..b958be24a6262 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -880,16 +880,7 @@ def _validate_key(self, key, axis: int): # slice of labels (where start-end in labels) # slice of integers (only if in the labels) # boolean - - if isinstance(key, slice): - return - - if com.is_bool_indexer(key): - return - - if not is_list_like_indexer(key): - labels = self.obj._get_axis(axis) - labels._convert_scalar_indexer(key, kind="loc") + pass def _has_valid_setitem_indexer(self, indexer) -> bool: return True @@ -1172,15 +1163,6 @@ def _convert_to_indexer(self, key, axis: int, is_setter: bool = False): if isinstance(key, slice): return labels._convert_slice_indexer(key, kind="loc") - if is_scalar(key): - # try to find out correct indexer, if not type correct raise - try: - key = labels._convert_scalar_indexer(key, kind="loc") - except KeyError: - # but we will allow setting - if not is_setter: - raise - # see if we are positional in nature is_int_index = labels.is_integer() is_int_positional = is_integer(key) and not is_int_index @@ -2107,11 +2089,7 @@ def _convert_key(self, key, is_setter: bool = False): if is_setter: return list(key) - lkey = list(key) - for n, (ax, i) in enumerate(zip(self.obj.axes, key)): - lkey[n] = ax._convert_scalar_indexer(i, kind="loc") - - return tuple(lkey) + return key def __getitem__(self, key): if self.ndim != 1 or not is_scalar(key): diff --git a/pandas/core/series.py b/pandas/core/series.py index 7d74d32bf5e14..8766c9b32b6e9 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -852,8 +852,6 @@ def __getitem__(self, key): return self key_is_scalar = is_scalar(key) - if key_is_scalar: - key = self.index._convert_scalar_indexer(key, kind="getitem") if key_is_scalar or isinstance(self.index, MultiIndex): # Otherwise index.get_value will raise InvalidIndexError @@ -997,8 +995,6 @@ def _get_value(self, label, takeable: bool = False): if takeable: return self._values[label] - # We assume that _convert_scalar_indexer has already been called, - # with kind="loc", if necessary, by the time we get here return self.index.get_value(self, label) def __setitem__(self, key, value): From 9dd625010d4c83714e6320273b4fce6303afab65 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 2 Mar 2020 07:40:17 -0800 Subject: [PATCH 5/5] redundant check --- pandas/core/series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 8b337dabe1831..dcef9e911e8eb 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -849,7 +849,7 @@ def __getitem__(self, key): return self key_is_scalar = is_scalar(key) - if not key_is_scalar and isinstance(key, (list, tuple)): + if isinstance(key, (list, tuple)): key = unpack_1tuple(key) if key_is_scalar or isinstance(self.index, MultiIndex):