diff --git a/pandas/core/algorithms.py b/pandas/core/algorithms.py index 7fc391d3ffb51..f5570dbbfc2b1 100644 --- a/pandas/core/algorithms.py +++ b/pandas/core/algorithms.py @@ -1334,9 +1334,15 @@ def diff(arr, n: int, axis: AxisInt = 0): # added a check on the integer value of period # see https://github.com/pandas-dev/pandas/issues/56607 if not lib.is_integer(n): - if not (is_float(n) and n.is_integer()): - raise ValueError("periods must be an integer") - n = int(n) + try: + if is_float(n) and n.is_integer(): + n = int(n) + else: + raise ValueError("periods must be an integer") + except (AttributeError, TypeError) as err: + # Handle cases where n doesn't have is_integer method + # or other type-related errors + raise ValueError("periods must be an integer") from err na = np.nan dtype = arr.dtype diff --git a/pandas/tests/test_algos.py b/pandas/tests/test_algos.py index 7fb421e27bb40..055cb244ee44b 100644 --- a/pandas/tests/test_algos.py +++ b/pandas/tests/test_algos.py @@ -2033,6 +2033,62 @@ def test_diff_low_precision_int(self, dtype): expected = np.array([np.nan, 1, 0, -1, 0], dtype="float32") tm.assert_numpy_array_equal(result, expected) + def test_diff_invalid_type_handling(self): + """Test that diff function properly handles invalid input types""" + # Test for the bug fix where non-numeric types would raise AttributeError + # instead of ValueError + + # Create a simple array for testing + arr = np.array([1, 2, 3, 4, 5]) + + # Test cases that should raise ValueError (not AttributeError) + invalid_inputs = [ + "hello", # string + None, # None + [1, 2], # list + {"key": "value"}, # dict + object(), # generic object + ] + + for invalid_input in invalid_inputs: + with pytest.raises(ValueError, match="periods must be an integer"): + algos.diff(arr, invalid_input) + + def test_diff_valid_float_handling(self): + """Test that diff function properly handles valid float inputs""" + + # Create a simple array for testing + arr = np.array([1, 2, 3, 4, 5]) + + # Test cases that should work (float values that are integers) + valid_inputs = [ + 1.0, # float that is an integer + 2.0, # another float that is an integer + -1.0, # negative float that is an integer + ] + + for valid_input in valid_inputs: + # Should not raise an exception + result = algos.diff(arr, valid_input) + assert result.shape == arr.shape + + def test_diff_invalid_float_handling(self): + """Test that diff function properly handles invalid float inputs""" + + # Create a simple array for testing + arr = np.array([1, 2, 3, 4, 5]) + + # Test cases that should raise ValueError (float values that are not integers) + invalid_float_inputs = [ + 1.5, # float that is not an integer + 2.7, # another float that is not an integer + -1.3, # negative float that is not an integer + ] + + for invalid_input in invalid_float_inputs: + with pytest.raises(ValueError, match="periods must be an integer"): + algos.diff(arr, invalid_input) + @pytest.mark.parametrize("op", [np.array, pd.array]) def test_union_with_duplicates(op):