From 9a1c4524f2dd4eaf7a164cced0eff2832e0210e8 Mon Sep 17 00:00:00 2001 From: Lirong Date: Thu, 25 Sep 2025 01:06:28 +0000 Subject: [PATCH 01/20] strict to true for info --- pandas/io/formats/info.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/pandas/io/formats/info.py b/pandas/io/formats/info.py index eb579f7149d44..47792c6e6a8e0 100644 --- a/pandas/io/formats/info.py +++ b/pandas/io/formats/info.py @@ -868,12 +868,14 @@ def _get_gross_column_widths(self) -> Sequence[int]: body_column_widths = self._get_body_column_widths() return [ max(*widths) - for widths in zip(self.header_column_widths, body_column_widths) + for widths in zip( + self.header_column_widths, body_column_widths, strict=True + ) ] def _get_body_column_widths(self) -> Sequence[int]: """Get widths of table content columns.""" - strcols: Sequence[Sequence[str]] = list(zip(*self.strrows)) + strcols: Sequence[Sequence[str]] = list(zip(*self.strrows, strict=True)) return [max(len(x) for x in col) for col in strcols] def _gen_rows(self) -> Iterator[Sequence[str]]: @@ -899,7 +901,9 @@ def add_header_line(self) -> None: header_line = self.SPACING.join( [ _put_str(header, col_width) - for header, col_width in zip(self.headers, self.gross_column_widths) + for header, col_width in zip( + self.headers, self.gross_column_widths, strict=True + ) ] ) self._lines.append(header_line) @@ -920,7 +924,9 @@ def add_body_lines(self) -> None: body_line = self.SPACING.join( [ _put_str(col, gross_colwidth) - for col, gross_colwidth in zip(row, self.gross_column_widths) + for col, gross_colwidth in zip( + row, self.gross_column_widths, strict=True + ) ] ) self._lines.append(body_line) @@ -980,6 +986,7 @@ def _gen_rows_without_counts(self) -> Iterator[Sequence[str]]: self._gen_line_numbers(), self._gen_columns(), self._gen_dtypes(), + strict=True, ) def _gen_rows_with_counts(self) -> Iterator[Sequence[str]]: @@ -989,6 +996,7 @@ def _gen_rows_with_counts(self) -> Iterator[Sequence[str]]: self._gen_columns(), self._gen_non_null_counts(), self._gen_dtypes(), + strict=True, ) def _gen_line_numbers(self) -> Iterator[str]: @@ -1092,10 +1100,7 @@ def _gen_rows_without_counts(self) -> Iterator[Sequence[str]]: def _gen_rows_with_counts(self) -> Iterator[Sequence[str]]: """Iterator with string representation of body data with counts.""" - yield from zip( - self._gen_non_null_counts(), - self._gen_dtypes(), - ) + yield from zip(self._gen_non_null_counts(), self._gen_dtypes(), strict=True) def _get_dataframe_dtype_counts(df: DataFrame) -> Mapping[str, int]: From 37fec5598c8cb9bfd1667cbd7bc584e693d56212 Mon Sep 17 00:00:00 2001 From: Lirong Date: Fri, 3 Oct 2025 20:57:36 +0000 Subject: [PATCH 02/20] add allow slice to is_hashable --- pandas/core/dtypes/inference.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index 928e44e51b3cf..ba1039dacc2a5 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -376,7 +376,7 @@ def is_named_tuple(obj: object) -> bool: return isinstance(obj, abc.Sequence) and hasattr(obj, "_fields") -def is_hashable(obj: object) -> TypeGuard[Hashable]: +def is_hashable(obj: object, allow_slice: bool=None) -> TypeGuard[Hashable]: """ Return True if hash(obj) will succeed, False otherwise. @@ -390,13 +390,17 @@ def is_hashable(obj: object) -> TypeGuard[Hashable]: ---------- obj : object The object to check for hashability. Any Python object can be passed here. + allow_slice: bool or None + - If True: return True if the object is hashable (including slices). + - If False: return True if the object is hashable and not a slice. + - If None: return True of the object is hashable. without checking for slice type. Returns ------- bool True if object can be hashed (i.e., does not raise TypeError when - passed to hash()), and False otherwise (e.g., if object is mutable - like a list or dictionary). + passed to hash()) and allow_slice is True or None, and False otherwise + (e.g., if object is mutable like a list or dictionary). See Also -------- @@ -427,6 +431,8 @@ def is_hashable(obj: object) -> TypeGuard[Hashable]: except TypeError: return False else: + if allow_slice is False and isinstance(obj, slice): + return False return True From 859f2f80bec2f5833a194efdbfe507aa46abca0c Mon Sep 17 00:00:00 2001 From: Lirong Date: Fri, 3 Oct 2025 21:01:43 +0000 Subject: [PATCH 03/20] removed zip strict --- pandas/io/formats/info.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/pandas/io/formats/info.py b/pandas/io/formats/info.py index 47792c6e6a8e0..83548dd8c0908 100644 --- a/pandas/io/formats/info.py +++ b/pandas/io/formats/info.py @@ -869,13 +869,13 @@ def _get_gross_column_widths(self) -> Sequence[int]: return [ max(*widths) for widths in zip( - self.header_column_widths, body_column_widths, strict=True + self.header_column_widths, body_column_widths ) ] def _get_body_column_widths(self) -> Sequence[int]: """Get widths of table content columns.""" - strcols: Sequence[Sequence[str]] = list(zip(*self.strrows, strict=True)) + strcols: Sequence[Sequence[str]] = list(zip(*self.strrows)) return [max(len(x) for x in col) for col in strcols] def _gen_rows(self) -> Iterator[Sequence[str]]: @@ -902,7 +902,7 @@ def add_header_line(self) -> None: [ _put_str(header, col_width) for header, col_width in zip( - self.headers, self.gross_column_widths, strict=True + self.headers, self.gross_column_widths ) ] ) @@ -925,7 +925,7 @@ def add_body_lines(self) -> None: [ _put_str(col, gross_colwidth) for col, gross_colwidth in zip( - row, self.gross_column_widths, strict=True + row, self.gross_column_widths ) ] ) @@ -985,8 +985,7 @@ def _gen_rows_without_counts(self) -> Iterator[Sequence[str]]: yield from zip( self._gen_line_numbers(), self._gen_columns(), - self._gen_dtypes(), - strict=True, + self._gen_dtypes(), ) def _gen_rows_with_counts(self) -> Iterator[Sequence[str]]: @@ -995,8 +994,7 @@ def _gen_rows_with_counts(self) -> Iterator[Sequence[str]]: self._gen_line_numbers(), self._gen_columns(), self._gen_non_null_counts(), - self._gen_dtypes(), - strict=True, + self._gen_dtypes(), ) def _gen_line_numbers(self) -> Iterator[str]: @@ -1100,7 +1098,7 @@ def _gen_rows_without_counts(self) -> Iterator[Sequence[str]]: def _gen_rows_with_counts(self) -> Iterator[Sequence[str]]: """Iterator with string representation of body data with counts.""" - yield from zip(self._gen_non_null_counts(), self._gen_dtypes(), strict=True) + yield from zip(self._gen_non_null_counts(), self._gen_dtypes()) def _get_dataframe_dtype_counts(df: DataFrame) -> Mapping[str, int]: From 90cb69e85a2b76137661a565ca7d495968af47e0 Mon Sep 17 00:00:00 2001 From: Lirong Date: Fri, 3 Oct 2025 21:04:59 +0000 Subject: [PATCH 04/20] lint --- pandas/io/formats/info.py | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/pandas/io/formats/info.py b/pandas/io/formats/info.py index 83548dd8c0908..16f60a83e8b79 100644 --- a/pandas/io/formats/info.py +++ b/pandas/io/formats/info.py @@ -868,9 +868,7 @@ def _get_gross_column_widths(self) -> Sequence[int]: body_column_widths = self._get_body_column_widths() return [ max(*widths) - for widths in zip( - self.header_column_widths, body_column_widths - ) + for widths in zip(self.header_column_widths, body_column_widths) ] def _get_body_column_widths(self) -> Sequence[int]: @@ -901,9 +899,7 @@ def add_header_line(self) -> None: header_line = self.SPACING.join( [ _put_str(header, col_width) - for header, col_width in zip( - self.headers, self.gross_column_widths - ) + for header, col_width in zip(self.headers, self.gross_column_widths) ] ) self._lines.append(header_line) @@ -912,9 +908,7 @@ def add_separator_line(self) -> None: separator_line = self.SPACING.join( [ _put_str("-" * header_colwidth, gross_colwidth) - for header_colwidth, gross_colwidth in zip( - self.header_column_widths, self.gross_column_widths - ) + for header_colwidth, gross_colwidth in zip(self.header_column_widths, self.gross_column_widths) ] ) self._lines.append(separator_line) @@ -924,9 +918,7 @@ def add_body_lines(self) -> None: body_line = self.SPACING.join( [ _put_str(col, gross_colwidth) - for col, gross_colwidth in zip( - row, self.gross_column_widths - ) + for col, gross_colwidth in zip(row, self.gross_column_widths) ] ) self._lines.append(body_line) @@ -985,7 +977,7 @@ def _gen_rows_without_counts(self) -> Iterator[Sequence[str]]: yield from zip( self._gen_line_numbers(), self._gen_columns(), - self._gen_dtypes(), + self._gen_dtypes(), ) def _gen_rows_with_counts(self) -> Iterator[Sequence[str]]: @@ -994,7 +986,7 @@ def _gen_rows_with_counts(self) -> Iterator[Sequence[str]]: self._gen_line_numbers(), self._gen_columns(), self._gen_non_null_counts(), - self._gen_dtypes(), + self._gen_dtypes(), ) def _gen_line_numbers(self) -> Iterator[str]: @@ -1098,7 +1090,10 @@ def _gen_rows_without_counts(self) -> Iterator[Sequence[str]]: def _gen_rows_with_counts(self) -> Iterator[Sequence[str]]: """Iterator with string representation of body data with counts.""" - yield from zip(self._gen_non_null_counts(), self._gen_dtypes()) + yield from zip( + self._gen_non_null_counts(), + self._gen_dtypes() + ) def _get_dataframe_dtype_counts(df: DataFrame) -> Mapping[str, int]: From 454484517a1881866a3373e66053f5a9efc2f7ee Mon Sep 17 00:00:00 2001 From: Lirong Date: Fri, 3 Oct 2025 21:06:25 +0000 Subject: [PATCH 05/20] format --- pandas/io/formats/info.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pandas/io/formats/info.py b/pandas/io/formats/info.py index 16f60a83e8b79..ca678107276a5 100644 --- a/pandas/io/formats/info.py +++ b/pandas/io/formats/info.py @@ -908,7 +908,9 @@ def add_separator_line(self) -> None: separator_line = self.SPACING.join( [ _put_str("-" * header_colwidth, gross_colwidth) - for header_colwidth, gross_colwidth in zip(self.header_column_widths, self.gross_column_widths) + for header_colwidth, gross_colwidth in zip( + self.header_column_widths, self.gross_column_widths + ) ] ) self._lines.append(separator_line) @@ -977,7 +979,7 @@ def _gen_rows_without_counts(self) -> Iterator[Sequence[str]]: yield from zip( self._gen_line_numbers(), self._gen_columns(), - self._gen_dtypes(), + self._gen_dtypes(), ) def _gen_rows_with_counts(self) -> Iterator[Sequence[str]]: @@ -986,7 +988,7 @@ def _gen_rows_with_counts(self) -> Iterator[Sequence[str]]: self._gen_line_numbers(), self._gen_columns(), self._gen_non_null_counts(), - self._gen_dtypes(), + self._gen_dtypes(), ) def _gen_line_numbers(self) -> Iterator[str]: @@ -1092,7 +1094,7 @@ def _gen_rows_with_counts(self) -> Iterator[Sequence[str]]: """Iterator with string representation of body data with counts.""" yield from zip( self._gen_non_null_counts(), - self._gen_dtypes() + self._gen_dtypes(), ) From 0e4d675ab95c99c93cf30d950b9adf8d2e36cdf1 Mon Sep 17 00:00:00 2001 From: Lirong Date: Fri, 3 Oct 2025 21:06:55 +0000 Subject: [PATCH 06/20] format --- pandas/io/formats/info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/io/formats/info.py b/pandas/io/formats/info.py index ca678107276a5..8b2bb3e8fb639 100644 --- a/pandas/io/formats/info.py +++ b/pandas/io/formats/info.py @@ -988,7 +988,7 @@ def _gen_rows_with_counts(self) -> Iterator[Sequence[str]]: self._gen_line_numbers(), self._gen_columns(), self._gen_non_null_counts(), - self._gen_dtypes(), + self._gen_dtypes() ) def _gen_line_numbers(self) -> Iterator[str]: From 03c1b22410701cc055089d8710ae5423402756e3 Mon Sep 17 00:00:00 2001 From: Lirong Date: Fri, 3 Oct 2025 21:07:35 +0000 Subject: [PATCH 07/20] format --- pandas/io/formats/info.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/io/formats/info.py b/pandas/io/formats/info.py index 8b2bb3e8fb639..eb579f7149d44 100644 --- a/pandas/io/formats/info.py +++ b/pandas/io/formats/info.py @@ -979,7 +979,7 @@ def _gen_rows_without_counts(self) -> Iterator[Sequence[str]]: yield from zip( self._gen_line_numbers(), self._gen_columns(), - self._gen_dtypes(), + self._gen_dtypes(), ) def _gen_rows_with_counts(self) -> Iterator[Sequence[str]]: @@ -988,7 +988,7 @@ def _gen_rows_with_counts(self) -> Iterator[Sequence[str]]: self._gen_line_numbers(), self._gen_columns(), self._gen_non_null_counts(), - self._gen_dtypes() + self._gen_dtypes(), ) def _gen_line_numbers(self) -> Iterator[str]: From 8ded7554e4c52f00b9ab5cef93950279482eddad Mon Sep 17 00:00:00 2001 From: Lirong Date: Sat, 4 Oct 2025 15:06:09 +0000 Subject: [PATCH 08/20] add allow_slice --- pandas/core/dtypes/inference.py | 14 +++++++++--- pandas/tests/dtypes/test_inference.py | 32 ++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index ba1039dacc2a5..c9d971cffe537 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -393,7 +393,7 @@ def is_hashable(obj: object, allow_slice: bool=None) -> TypeGuard[Hashable]: allow_slice: bool or None - If True: return True if the object is hashable (including slices). - If False: return True if the object is hashable and not a slice. - - If None: return True of the object is hashable. without checking for slice type. + - If None: return True if the object is hashable. without checking for slice type. Returns ------- @@ -426,12 +426,20 @@ def is_hashable(obj: object, allow_slice: bool=None) -> TypeGuard[Hashable]: # Reconsider this decision once this numpy bug is fixed: # https://github.com/numpy/numpy/issues/5562 + def _contains_slice(x: object) -> bool: + # Check if object is a slice or a tuple containing a slice + if isinstance(x, tuple): + return any(isinstance(v, slice) for v in x) + elif isinstance(x, slice): + return True + return False + try: hash(obj) except TypeError: return False - else: - if allow_slice is False and isinstance(obj, slice): + else: + if allow_slice is False and _contains_slice(obj): return False return True diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index d0955912e12c8..5a13809574868 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -451,17 +451,47 @@ class UnhashableClass1: class UnhashableClass2: def __hash__(self): raise TypeError("Not hashable") + + class HashableSlice: + def __init__(self, start, stop, step=None): + self.slice = slice(start, stop, step) + + def __eq__(self, other): + return isinstance(other, HashableSlice) and self.slice == other.slice + + def __hash__(self): + return hash((self.slice.start, self.slice.stop, self.slice.step)) + + def __repr__(self): + return f"HashableSlice({self.slice.start}, {self.slice.stop}, {self.slice.step})" + hashable = (1, 3.14, np.float64(3.14), "a", (), (1,), HashableClass()) - not_hashable = ([], UnhashableClass1()) + not_hashable = ([], UnhashableClass1(), slice(1, 2, 3)) abc_hashable_not_really_hashable = (([],), UnhashableClass2()) + hashable_slice = (HashableSlice(1, 2), HashableSlice(1, 2, 3)) + tuple_with_slice = ((slice(1, 2), 3), 1, "a") for i in hashable: assert inference.is_hashable(i) + assert inference.is_hashable(i, allow_slice=True) + assert inference.is_hashable(i, allow_slice=False) for i in not_hashable: assert not inference.is_hashable(i) + assert not inference.is_hashable(i, allow_slice=True) + assert not inference.is_hashable(i, allow_slice=False) for i in abc_hashable_not_really_hashable: assert not inference.is_hashable(i) + assert not inference.is_hashable(i, allow_slice=True) + assert not inference.is_hashable(i, allow_slice=False) + for i in hashable_slice: + assert inference.is_hashable(i) + assert inference.is_hashable(i, allow_slice=True) + assert inference.is_hashable(i, allow_slice=False) + + assert not inference.is_hashable(tuple_with_slice) + assert not inference.is_hashable(tuple_with_slice, allow_slice=True) + assert not inference.is_hashable(tuple_with_slice, allow_slice=False) # numpy.array is no longer collections.abc.Hashable as of # https://github.com/numpy/numpy/pull/5326, just test From 067b830d7739de217097c1438c1c137cb7e8b37e Mon Sep 17 00:00:00 2001 From: Lirong Date: Sat, 4 Oct 2025 16:44:01 +0000 Subject: [PATCH 09/20] ruff --- pandas/core/dtypes/inference.py | 13 +++++++------ pandas/tests/dtypes/test_inference.py | 12 +++++++----- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index c9d971cffe537..4cf8d0adf97aa 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -376,7 +376,7 @@ def is_named_tuple(obj: object) -> bool: return isinstance(obj, abc.Sequence) and hasattr(obj, "_fields") -def is_hashable(obj: object, allow_slice: bool=None) -> TypeGuard[Hashable]: +def is_hashable(obj: object, allow_slice: bool | None = None) -> TypeGuard[Hashable]: """ Return True if hash(obj) will succeed, False otherwise. @@ -391,9 +391,10 @@ def is_hashable(obj: object, allow_slice: bool=None) -> TypeGuard[Hashable]: obj : object The object to check for hashability. Any Python object can be passed here. allow_slice: bool or None - - If True: return True if the object is hashable (including slices). - - If False: return True if the object is hashable and not a slice. - - If None: return True if the object is hashable. without checking for slice type. + If True: return True if the object is hashable (including slices). + If False: return True if the object is hashable and not a slice. + If None: return True if the object is hashable. without checking + for slice type. Returns ------- @@ -429,7 +430,7 @@ def is_hashable(obj: object, allow_slice: bool=None) -> TypeGuard[Hashable]: def _contains_slice(x: object) -> bool: # Check if object is a slice or a tuple containing a slice if isinstance(x, tuple): - return any(isinstance(v, slice) for v in x) + return any(isinstance(v, slice) for v in x) elif isinstance(x, slice): return True return False @@ -438,7 +439,7 @@ def _contains_slice(x: object) -> bool: hash(obj) except TypeError: return False - else: + else: if allow_slice is False and _contains_slice(obj): return False return True diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index 5a13809574868..b96f655a0a51c 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -451,7 +451,7 @@ class UnhashableClass1: class UnhashableClass2: def __hash__(self): raise TypeError("Not hashable") - + class HashableSlice: def __init__(self, start, stop, step=None): self.slice = slice(start, stop, step) @@ -463,8 +463,10 @@ def __hash__(self): return hash((self.slice.start, self.slice.stop, self.slice.step)) def __repr__(self): - return f"HashableSlice({self.slice.start}, {self.slice.stop}, {self.slice.step})" - + return ( + f"HashableSlice({self.slice.start}, {self.slice.stop}, " + f"{self.slice.step})" + ) hashable = (1, 3.14, np.float64(3.14), "a", (), (1,), HashableClass()) not_hashable = ([], UnhashableClass1(), slice(1, 2, 3)) @@ -486,9 +488,9 @@ def __repr__(self): assert not inference.is_hashable(i, allow_slice=False) for i in hashable_slice: assert inference.is_hashable(i) - assert inference.is_hashable(i, allow_slice=True) + assert inference.is_hashable(i, allow_slice=True) assert inference.is_hashable(i, allow_slice=False) - + assert not inference.is_hashable(tuple_with_slice) assert not inference.is_hashable(tuple_with_slice, allow_slice=True) assert not inference.is_hashable(tuple_with_slice, allow_slice=False) From 8fcecee786373371d202d20e517ca61e7e5c3820 Mon Sep 17 00:00:00 2001 From: Lirong Date: Sat, 4 Oct 2025 16:59:58 +0000 Subject: [PATCH 10/20] fix ident --- pandas/core/dtypes/inference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index 4cf8d0adf97aa..59f1f587d04ef 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -394,7 +394,7 @@ def is_hashable(obj: object, allow_slice: bool | None = None) -> TypeGuard[Hasha If True: return True if the object is hashable (including slices). If False: return True if the object is hashable and not a slice. If None: return True if the object is hashable. without checking - for slice type. + for slice type. Returns ------- From 871d6214e842b385ab42798b758be815ae289411 Mon Sep 17 00:00:00 2001 From: Lirong Date: Sat, 4 Oct 2025 18:41:33 +0000 Subject: [PATCH 11/20] doc --- pandas/core/dtypes/inference.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index 59f1f587d04ef..1aae25599a4c8 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -390,11 +390,11 @@ def is_hashable(obj: object, allow_slice: bool | None = None) -> TypeGuard[Hasha ---------- obj : object The object to check for hashability. Any Python object can be passed here. - allow_slice: bool or None - If True: return True if the object is hashable (including slices). - If False: return True if the object is hashable and not a slice. - If None: return True if the object is hashable. without checking - for slice type. + allow_slice : bool or None + If True, return True if the object is hashable (including slices). + If False, return True if the object is hashable and not a slice. + If None, return True if the object is hashable without checking + for slice type. Returns ------- From d145d0b27826bcf88772f43ce057c47698f3fbbb Mon Sep 17 00:00:00 2001 From: Lirong Date: Sat, 4 Oct 2025 19:28:06 +0000 Subject: [PATCH 12/20] test --- pandas/tests/dtypes/test_inference.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index b96f655a0a51c..b2dce4f34fcfb 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -469,10 +469,10 @@ def __repr__(self): ) hashable = (1, 3.14, np.float64(3.14), "a", (), (1,), HashableClass()) - not_hashable = ([], UnhashableClass1(), slice(1, 2, 3)) + not_hashable = ([], UnhashableClass1()) abc_hashable_not_really_hashable = (([],), UnhashableClass2()) - hashable_slice = (HashableSlice(1, 2), HashableSlice(1, 2, 3)) - tuple_with_slice = ((slice(1, 2), 3), 1, "a") + hashable_slice = HashableSlice(1, 2) + tuple_with_slice = (slice(1, 2), 3) for i in hashable: assert inference.is_hashable(i) @@ -486,10 +486,10 @@ def __repr__(self): assert not inference.is_hashable(i) assert not inference.is_hashable(i, allow_slice=True) assert not inference.is_hashable(i, allow_slice=False) - for i in hashable_slice: - assert inference.is_hashable(i) - assert inference.is_hashable(i, allow_slice=True) - assert inference.is_hashable(i, allow_slice=False) + + assert inference.is_hashable(hashable_slice) + assert inference.is_hashable(hashable_slice, allow_slice=True) + assert inference.is_hashable(hashable_slice, allow_slice=False) assert not inference.is_hashable(tuple_with_slice) assert not inference.is_hashable(tuple_with_slice, allow_slice=True) From 2b0ba79ed3ef666f4748fbd027c6c93bbac36d2c Mon Sep 17 00:00:00 2001 From: Lirong Date: Sat, 4 Oct 2025 20:26:33 +0000 Subject: [PATCH 13/20] test --- pandas/tests/dtypes/test_inference.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index b2dce4f34fcfb..92e1b70905c55 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -491,8 +491,8 @@ def __repr__(self): assert inference.is_hashable(hashable_slice, allow_slice=True) assert inference.is_hashable(hashable_slice, allow_slice=False) - assert not inference.is_hashable(tuple_with_slice) - assert not inference.is_hashable(tuple_with_slice, allow_slice=True) + assert inference.is_hashable(tuple_with_slice) + assert inference.is_hashable(tuple_with_slice, allow_slice=True) assert not inference.is_hashable(tuple_with_slice, allow_slice=False) # numpy.array is no longer collections.abc.Hashable as of From 2a6e7a17249385d12f75e0704bf3c13206cbbaa0 Mon Sep 17 00:00:00 2001 From: Lirong Date: Sat, 4 Oct 2025 21:36:52 +0000 Subject: [PATCH 14/20] test --- pandas/tests/dtypes/test_inference.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index 92e1b70905c55..b2dce4f34fcfb 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -491,8 +491,8 @@ def __repr__(self): assert inference.is_hashable(hashable_slice, allow_slice=True) assert inference.is_hashable(hashable_slice, allow_slice=False) - assert inference.is_hashable(tuple_with_slice) - assert inference.is_hashable(tuple_with_slice, allow_slice=True) + assert not inference.is_hashable(tuple_with_slice) + assert not inference.is_hashable(tuple_with_slice, allow_slice=True) assert not inference.is_hashable(tuple_with_slice, allow_slice=False) # numpy.array is no longer collections.abc.Hashable as of From 7b83c1614d795b348d0097f6c32967d05bdaddf5 Mon Sep 17 00:00:00 2001 From: Lirong Date: Sat, 4 Oct 2025 22:09:37 +0000 Subject: [PATCH 15/20] test --- pandas/tests/dtypes/test_inference.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index b2dce4f34fcfb..bd51995351088 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -491,6 +491,10 @@ def __repr__(self): assert inference.is_hashable(hashable_slice, allow_slice=True) assert inference.is_hashable(hashable_slice, allow_slice=False) + assert not inference.is_hashable(slice(1, 2)) + assert not inference.is_hashable(slice(1, 2), allow_slice=True) + assert not inference.is_hashable(slice(1, 2), allow_slice=False) + assert not inference.is_hashable(tuple_with_slice) assert not inference.is_hashable(tuple_with_slice, allow_slice=True) assert not inference.is_hashable(tuple_with_slice, allow_slice=False) From 0a57d73fdef58b7c72dff54529c200960c4e51f0 Mon Sep 17 00:00:00 2001 From: Lirong Date: Sat, 4 Oct 2025 23:08:41 +0000 Subject: [PATCH 16/20] test --- pandas/tests/dtypes/test_inference.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index bd51995351088..f31dc1d888e4f 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -491,7 +491,9 @@ def __repr__(self): assert inference.is_hashable(hashable_slice, allow_slice=True) assert inference.is_hashable(hashable_slice, allow_slice=False) - assert not inference.is_hashable(slice(1, 2)) + assert not inference.is_hashable(slice(1, 2)), ( + f"result is {inference.is_hashable(slice(1, 2))}" + ) assert not inference.is_hashable(slice(1, 2), allow_slice=True) assert not inference.is_hashable(slice(1, 2), allow_slice=False) From c684c43844a9df86f78ec1e5af817c42ca0f223a Mon Sep 17 00:00:00 2001 From: Lirong Date: Sat, 4 Oct 2025 23:24:34 +0000 Subject: [PATCH 17/20] test --- pandas/tests/dtypes/test_inference.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index f31dc1d888e4f..02e9508967348 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -491,15 +491,25 @@ def __repr__(self): assert inference.is_hashable(hashable_slice, allow_slice=True) assert inference.is_hashable(hashable_slice, allow_slice=False) - assert not inference.is_hashable(slice(1, 2)), ( + assert inference.is_hashable(slice(1, 2)), ( f"result is {inference.is_hashable(slice(1, 2))}" ) - assert not inference.is_hashable(slice(1, 2), allow_slice=True) - assert not inference.is_hashable(slice(1, 2), allow_slice=False) + assert inference.is_hashable(slice(1, 2), allow_slice=True), ( + f"result is {inference.is_hashable(slice(1, 2), allow_slice=True)}" + ) + assert not inference.is_hashable(slice(1, 2), allow_slice=False), ( + f"result is {inference.is_hashable(slice(1, 2), allow_slice=False)}" + ) - assert not inference.is_hashable(tuple_with_slice) - assert not inference.is_hashable(tuple_with_slice, allow_slice=True) - assert not inference.is_hashable(tuple_with_slice, allow_slice=False) + assert not inference.is_hashable(tuple_with_slice), ( + f"result is {inference.is_hashable(tuple_with_slice)}" + ) + assert not inference.is_hashable(tuple_with_slice, allow_slice=True), ( + f"result is {inference.is_hashable(tuple_with_slice, allow_slice=True)}" + ) + assert not inference.is_hashable(tuple_with_slice, allow_slice=False), ( + f"result is {inference.is_hashable(tuple_with_slice, allow_slice=False)}" + ) # numpy.array is no longer collections.abc.Hashable as of # https://github.com/numpy/numpy/pull/5326, just test From 06af37caf0d3069e70b1a7d191bf2e417750d27d Mon Sep 17 00:00:00 2001 From: Lirong Date: Sat, 4 Oct 2025 23:44:55 +0000 Subject: [PATCH 18/20] test --- pandas/tests/dtypes/test_inference.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index 02e9508967348..2daf1bf735eeb 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -501,10 +501,10 @@ def __repr__(self): f"result is {inference.is_hashable(slice(1, 2), allow_slice=False)}" ) - assert not inference.is_hashable(tuple_with_slice), ( + assert inference.is_hashable(tuple_with_slice), ( f"result is {inference.is_hashable(tuple_with_slice)}" ) - assert not inference.is_hashable(tuple_with_slice, allow_slice=True), ( + assert inference.is_hashable(tuple_with_slice, allow_slice=True), ( f"result is {inference.is_hashable(tuple_with_slice, allow_slice=True)}" ) assert not inference.is_hashable(tuple_with_slice, allow_slice=False), ( From 272a17bc2d1498c9ea15c2ed827752e1f1c4352f Mon Sep 17 00:00:00 2001 From: Lirong Date: Sun, 5 Oct 2025 16:02:04 +0000 Subject: [PATCH 19/20] removed example from code --- pandas/core/dtypes/inference.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index 4621b88618378..a3d8456b516b1 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -423,14 +423,6 @@ def is_hashable(obj: object, allow_slice: bool | None = None) -> TypeGuard[Hasha True >>> is_hashable(a) False - >>> is_hashable(slice(1, 2, 3)) - True - >>> is_hashable(slice(1, 2, 3), allow_slice=False) - False - >>> is_hashable((slice(1, 2, 3),), allow_slice=False) - False - >>> is_hashable((slice(1, 2, 3),), allow_slice=True) - True """ # Unfortunately, we can't use isinstance(obj, collections.abc.Hashable), # which can be faster than calling hash. That is because numpy scalars From 4440d7065aa1cf858c4ae66f0d2250396629b627 Mon Sep 17 00:00:00 2001 From: Lirong Date: Sun, 5 Oct 2025 16:40:56 +0000 Subject: [PATCH 20/20] var name --- pandas/tests/dtypes/test_inference.py | 33 ++++++++------------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index faefa9dd4296c..116adcb883326 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -473,8 +473,7 @@ def __repr__(self): not_hashable = ([], UnhashableClass1()) abc_hashable_not_really_hashable = (([],), UnhashableClass2()) hashable_slice = HashableSlice(1, 2) - tuple_with_slice_1 = (slice(1, 2), 3) - tuple_with_slice_2 = ((1, slice(1, 2)), "a") + tuple_with_slice = (slice(1, 2), 3) for i in hashable: assert inference.is_hashable(i) @@ -494,29 +493,15 @@ def __repr__(self): assert inference.is_hashable(hashable_slice, allow_slice=False) if PY312: - assert inference.is_hashable(slice(1, 2)) - assert inference.is_hashable(slice(1, 2), allow_slice=True) - assert not inference.is_hashable(slice(1, 2), allow_slice=False) - - assert inference.is_hashable(tuple_with_slice_1) - assert inference.is_hashable(tuple_with_slice_1, allow_slice=True) - assert not inference.is_hashable(tuple_with_slice_1, allow_slice=False) - - assert inference.is_hashable(tuple_with_slice_2) - assert inference.is_hashable(tuple_with_slice_2, allow_slice=True) - assert not inference.is_hashable(tuple_with_slice_2, allow_slice=False) + for obj in [slice(1, 2), tuple_with_slice]: + assert inference.is_hashable(obj) + assert inference.is_hashable(obj, allow_slice=True) + assert not inference.is_hashable(obj, allow_slice=False) else: - assert not inference.is_hashable(slice(1, 2)) - assert not inference.is_hashable(slice(1, 2), allow_slice=True) - assert not inference.is_hashable(slice(1, 2), allow_slice=False) - - assert not inference.is_hashable(tuple_with_slice_1) - assert not inference.is_hashable(tuple_with_slice_1, allow_slice=True) - assert not inference.is_hashable(tuple_with_slice_1, allow_slice=False) - - assert not inference.is_hashable(tuple_with_slice_2) - assert not inference.is_hashable(tuple_with_slice_2, allow_slice=True) - assert not inference.is_hashable(tuple_with_slice_2, allow_slice=False) + for obj in [slice(1, 2), tuple_with_slice]: + assert not inference.is_hashable(obj) + assert not inference.is_hashable(obj, allow_slice=True) + assert not inference.is_hashable(obj, allow_slice=False) # numpy.array is no longer collections.abc.Hashable as of # https://github.com/numpy/numpy/pull/5326, just test