diff --git a/pandas/conftest.py b/pandas/conftest.py index 3553a411a27f8..b0b56899e674c 100644 --- a/pandas/conftest.py +++ b/pandas/conftest.py @@ -881,3 +881,75 @@ def index_or_series(request): See GH#29725 """ return request.param + + +_int_index = tm.makeIntIndex(10, name="a") +_indexes = [ + tm.makeBoolIndex(10, name="a"), + _int_index, + tm.makeFloatIndex(10, name="a"), + tm.makeDateIndex(10, name="a"), + tm.makeDateIndex(10, name="a", tz="US/Eastern"), + tm.makePeriodIndex(10, name="a"), + tm.makeStringIndex(10, name="a"), + tm.makeUnicodeIndex(10, name="a"), +] +_index_ids = [ + "bool-index", + "int-index", + "float-index", + "date-index", + "localized-date-index", + "period-index", + "string-index", + "unicode-index", +] + + +@pytest.fixture(params=_indexes, ids=_index_ids) +def index(request): + """ Fixture for tests on indexes """ + return request.param.copy(deep=True) + + +_arr = arr = np.random.randn(10) + + +@pytest.fixture +def int_series(): + """ Fixture for Series with random numbers and integer index """ + index = _int_index.copy(deep=True) + return pd.Series(_arr, index=index, name=index.name) + + +_series = [pd.Series(_arr, index=index, name=index.name) for index in _indexes] +_series_ids = [f"series-with-{index_id}" for index_id in _index_ids] + +_arr_int = np.random.choice(10, size=10, replace=False) +_narrow_series = [ + pd.Series(_arr.astype(np.float32), index=_int_index, name="a"), + pd.Series(_arr_int.astype(np.int8), index=_int_index, name="a"), + pd.Series(_arr_int.astype(np.int16), index=_int_index, name="a"), + pd.Series(_arr_int.astype(np.int32), index=_int_index, name="a"), + pd.Series(_arr_int.astype(np.uint8), index=_int_index, name="a"), + pd.Series(_arr_int.astype(np.uint16), index=_int_index, name="a"), + pd.Series(_arr_int.astype(np.uint32), index=_int_index, name="a"), +] +_narrow_series_ids = [ + "float32-series", + "int8-series", + "int16-series", + "int32-series", + "uint8-series", + "uint16-series", + "uint32-series", +] + +_all_objs = _indexes + _series + _narrow_series +_all_obj_ids = _index_ids + _series_ids + _narrow_series_ids + + +@pytest.fixture(params=_all_objs, ids=_all_obj_ids) +def index_or_series_obj(request): + """ Fixture for tests on indexes, series and series with a narrow dtype """ + return request.param.copy(deep=True) diff --git a/pandas/tests/base/test_ops.py b/pandas/tests/base/test_ops.py index bcd6b931a0f85..14a3975e94f3d 100644 --- a/pandas/tests/base/test_ops.py +++ b/pandas/tests/base/test_ops.py @@ -130,27 +130,28 @@ def check_ops_properties(self, props, filter=None, ignore_failures=False): with pytest.raises(err): getattr(o, op) - @pytest.mark.parametrize("klass", [Series, DataFrame]) - def test_binary_ops_docs(self, klass): - op_map = { - "add": "+", - "sub": "-", - "mul": "*", - "mod": "%", - "pow": "**", - "truediv": "/", - "floordiv": "//", - } - for op_name in op_map: - operand1 = klass.__name__.lower() - operand2 = "other" - op = op_map[op_name] - expected_str = " ".join([operand1, op, operand2]) - assert expected_str in getattr(klass, op_name).__doc__ - - # reverse version of the binary ops - expected_str = " ".join([operand2, op, operand1]) - assert expected_str in getattr(klass, "r" + op_name).__doc__ + +@pytest.mark.parametrize("klass", [Series, DataFrame]) +def test_binary_ops_docs(klass): + op_map = { + "add": "+", + "sub": "-", + "mul": "*", + "mod": "%", + "pow": "**", + "truediv": "/", + "floordiv": "//", + } + for op_name in op_map: + operand1 = klass.__name__.lower() + operand2 = "other" + op = op_map[op_name] + expected_str = " ".join([operand1, op, operand2]) + assert expected_str in getattr(klass, op_name).__doc__ + + # reverse version of the binary ops + expected_str = " ".join([operand2, op, operand1]) + assert expected_str in getattr(klass, "r" + op_name).__doc__ class TestTranspose(Ops): diff --git a/pandas/tests/base/utils.py b/pandas/tests/base/utils.py new file mode 100644 index 0000000000000..02dbbe14335a6 --- /dev/null +++ b/pandas/tests/base/utils.py @@ -0,0 +1,59 @@ +from typing import Any, Callable, List + +import numpy as np +import pytest + +from pandas import Index, Series +from pandas.core.indexes.datetimelike import DatetimeIndexOpsMixin +import pandas.util.testing as tm + + +def allow_na_ops(obj: Any) -> bool: + """Whether to skip test cases including NaN""" + is_bool_index = isinstance(obj, Index) and obj.is_boolean() + return not is_bool_index and obj._can_hold_na + + +def check_ops_properties_valid(obj: Any, props: List[str], filter: Callable) -> None: + """ Validates that certain properties are available """ + for op in props: + # if a filter, skip if it doesn't match + index = obj.index if isinstance(obj, Series) else obj + if not filter(index): + continue + + try: + if isinstance(obj, Series): + expected = Series(getattr(obj.index, op), index=obj.index, name="a") + else: + expected = getattr(obj, op) + except AttributeError: + continue + + result = getattr(obj, op) + + # these could be series, arrays or scalars + if isinstance(result, Series) and isinstance(expected, Series): + tm.assert_series_equal(result, expected) + elif isinstance(result, Index) and isinstance(expected, Index): + tm.assert_index_equal(result, expected) + elif isinstance(result, np.ndarray) and isinstance(expected, np.ndarray): + tm.assert_numpy_array_equal(result, expected) + else: + assert result == expected + + +def check_ops_properties_invalid(obj: Any, props: List[str]) -> None: + """ Validates that certain properties are not available """ + for op in props: + # freq raises AttributeError on an Int64Index because its not + # defined we mostly care about Series here anyhow + + # an object that is datetimelike will raise a TypeError, + # otherwise an AttributeError + err = AttributeError + if issubclass(type(obj), DatetimeIndexOpsMixin): + err = TypeError + + with pytest.raises(err): + getattr(obj, op)