Skip to content

REF: de-duplicate test_direct_arith_with_ndframe_returns_not_implemented #54410

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 4, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions pandas/_testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,26 @@
EMPTY_STRING_PATTERN = re.compile("^$")


arithmetic_dunder_methods = [
"__add__",
"__radd__",
"__sub__",
"__rsub__",
"__mul__",
"__rmul__",
"__floordiv__",
"__rfloordiv__",
"__truediv__",
"__rtruediv__",
"__pow__",
"__rpow__",
"__mod__",
"__rmod__",
]

comparison_dunder_methods = ["__eq__", "__ne__", "__le__", "__lt__", "__ge__", "__gt__"]


def reset_display_options() -> None:
"""
Reset the display options for printing and representing objects.
Expand Down
18 changes: 1 addition & 17 deletions pandas/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -974,25 +974,9 @@ def ea_scalar_and_dtype(request):
# ----------------------------------------------------------------
# Operators & Operations
# ----------------------------------------------------------------
_all_arithmetic_operators = [
"__add__",
"__radd__",
"__sub__",
"__rsub__",
"__mul__",
"__rmul__",
"__floordiv__",
"__rfloordiv__",
"__truediv__",
"__rtruediv__",
"__pow__",
"__rpow__",
"__mod__",
"__rmod__",
]


@pytest.fixture(params=_all_arithmetic_operators)
@pytest.fixture(params=tm.arithmetic_dunder_methods)
def all_arithmetic_operators(request):
"""
Fixture for dunder names for common arithmetic operations.
Expand Down
29 changes: 17 additions & 12 deletions pandas/core/arrays/datetimelike.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@
DTScalarOrNaT = Union[DatetimeLikeScalar, NaTType]


def _make_unpacked_invalid_op(op_name: str):
op = make_invalid_op(op_name)
return unpack_zerodim_and_defer(op_name)(op)


def _period_dispatch(meth: F) -> F:
"""
For PeriodArray methods, dispatch to DatetimeArray and re-wrap the results
Expand Down Expand Up @@ -979,18 +984,18 @@ def _cmp_method(self, other, op):

# pow is invalid for all three subclasses; TimedeltaArray will override
# the multiplication and division ops
__pow__ = make_invalid_op("__pow__")
__rpow__ = make_invalid_op("__rpow__")
__mul__ = make_invalid_op("__mul__")
__rmul__ = make_invalid_op("__rmul__")
__truediv__ = make_invalid_op("__truediv__")
__rtruediv__ = make_invalid_op("__rtruediv__")
__floordiv__ = make_invalid_op("__floordiv__")
__rfloordiv__ = make_invalid_op("__rfloordiv__")
__mod__ = make_invalid_op("__mod__")
__rmod__ = make_invalid_op("__rmod__")
__divmod__ = make_invalid_op("__divmod__")
__rdivmod__ = make_invalid_op("__rdivmod__")
__pow__ = _make_unpacked_invalid_op("__pow__")
__rpow__ = _make_unpacked_invalid_op("__rpow__")
__mul__ = _make_unpacked_invalid_op("__mul__")
__rmul__ = _make_unpacked_invalid_op("__rmul__")
__truediv__ = _make_unpacked_invalid_op("__truediv__")
__rtruediv__ = _make_unpacked_invalid_op("__rtruediv__")
__floordiv__ = _make_unpacked_invalid_op("__floordiv__")
__rfloordiv__ = _make_unpacked_invalid_op("__rfloordiv__")
__mod__ = _make_unpacked_invalid_op("__mod__")
__rmod__ = _make_unpacked_invalid_op("__rmod__")
__divmod__ = _make_unpacked_invalid_op("__divmod__")
__rdivmod__ = _make_unpacked_invalid_op("__rdivmod__")

@final
def _get_i8_values_and_mask(
Expand Down
49 changes: 13 additions & 36 deletions pandas/tests/extension/base/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,23 +166,20 @@ def test_add_series_with_extension_array(self, data):
expected = pd.Series(data + data)
tm.assert_series_equal(result, expected)

@pytest.mark.parametrize("box", [pd.Series, pd.DataFrame])
def test_direct_arith_with_ndframe_returns_not_implemented(
self, request, data, box
):
# EAs should return NotImplemented for ops with Series/DataFrame
@pytest.mark.parametrize("box", [pd.Series, pd.DataFrame, pd.Index])
def test_direct_arith_with_ndframe_returns_not_implemented(self, data, box):
# EAs should return NotImplemented for ops with Series/DataFrame/Index
# Pandas takes care of unboxing the series and calling the EA's op.
other = pd.Series(data)
if box is pd.DataFrame:
other = other.to_frame()
if not hasattr(data, "__add__"):
request.node.add_marker(
pytest.mark.xfail(
reason=f"{type(data).__name__} does not implement add"
)
)
result = data.__add__(other)
assert result is NotImplemented
other = box(data)

op_names = tm.arithmetic_dunder_methods + tm.comparison_dunder_methods
op_names = [x for x in op_names if not x.startswith("__r")]
for op_name in op_names:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the ergonomics parameterizing this so that the test doesn't stop for a failure for just one op

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok will update

# We use a loop here instead of fixture to avoid overhead from
# re-creating 'data' many times.
if hasattr(data, op_name):
result = getattr(data, op_name)(other)
assert result is NotImplemented


class BaseComparisonOpsTests(BaseOpsUtil):
Expand Down Expand Up @@ -219,26 +216,6 @@ def test_compare_array(self, data, comparison_op):
other = pd.Series([data[0]] * len(data))
self._compare_other(ser, data, comparison_op, other)

@pytest.mark.parametrize("box", [pd.Series, pd.DataFrame])
def test_direct_arith_with_ndframe_returns_not_implemented(self, data, box):
# EAs should return NotImplemented for ops with Series/DataFrame
# Pandas takes care of unboxing the series and calling the EA's op.
other = pd.Series(data)
if box is pd.DataFrame:
other = other.to_frame()

if hasattr(data, "__eq__"):
result = data.__eq__(other)
assert result is NotImplemented
else:
pytest.skip(f"{type(data).__name__} does not implement __eq__")

if hasattr(data, "__ne__"):
result = data.__ne__(other)
assert result is NotImplemented
else:
pytest.skip(f"{type(data).__name__} does not implement __ne__")


class BaseUnaryOpsTests(BaseOpsUtil):
def test_invert(self, data):
Expand Down
11 changes: 0 additions & 11 deletions pandas/tests/extension/test_period.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,17 +131,6 @@ def test_add_series_with_extension_array(self, data):
with pytest.raises(TypeError, match=msg):
s + data

def test_direct_arith_with_ndframe_returns_not_implemented(
self, data, frame_or_series
):
# Override to use __sub__ instead of __add__
other = pd.Series(data)
if frame_or_series is pd.DataFrame:
other = other.to_frame()

result = data.__sub__(other)
assert result is NotImplemented


class TestCasting(BasePeriodTests, base.BaseCastingTests):
pass
Expand Down