Skip to content

Commit

Permalink
CLN: Refactor pandas/tests/base - part3 (#30147)
Browse files Browse the repository at this point in the history
  • Loading branch information
SaturnFromTitan committed Feb 1, 2020
1 parent 3ae1710 commit 01623f8
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 145 deletions.
10 changes: 10 additions & 0 deletions pandas/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,16 @@ def tick_classes(request):
)


@pytest.fixture
def datetime_series():
"""
Fixture for Series of floats with DatetimeIndex
"""
s = tm.makeTimeSeries()
s.name = "ts"
return s


@pytest.fixture
def float_frame():
"""
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/ops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ def _get_opstr(op):
rtruediv: "/",
operator.floordiv: "//",
rfloordiv: "//",
operator.mod: None, # TODO: Why None for mod but '%' for rmod?
operator.mod: "%",
rmod: "%",
operator.pow: "**",
rpow: "**",
Expand Down
112 changes: 34 additions & 78 deletions pandas/tests/base/test_ops.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import datetime, timedelta
from io import StringIO
import sys
from typing import Any

import numpy as np
import pytest
Expand Down Expand Up @@ -30,17 +31,15 @@
Timestamp,
)
import pandas._testing as tm
from pandas.core.indexes.datetimelike import DatetimeIndexOpsMixin


class Ops:
def _allow_na_ops(self, obj):
"""Whether to skip test cases including NaN"""
if (isinstance(obj, Index) and obj.is_boolean()) or not obj._can_hold_na:
# don't test boolean / integer dtypes
return False
return True
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


class Ops:
def setup_method(self, method):
self.bool_index = tm.makeBoolIndex(10, name="a")
self.int_index = tm.makeIntIndex(10, name="a")
Expand Down Expand Up @@ -83,74 +82,31 @@ def setup_method(self, method):

self.objs = self.indexes + self.series + self.narrow_series

def check_ops_properties(self, props, filter=None, ignore_failures=False):
for op in props:
for o in self.is_valid_objs:

# if a filter, skip if it doesn't match
if filter is not None:
filt = o.index if isinstance(o, Series) else o
if not filter(filt):
continue

try:
if isinstance(o, Series):
expected = Series(getattr(o.index, op), index=o.index, name="a")
else:
expected = getattr(o, op)
except (AttributeError):
if ignore_failures:
continue

result = getattr(o, 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

# freq raises AttributeError on an Int64Index because its not
# defined we mostly care about Series here anyhow
if not ignore_failures:
for o in self.not_valid_objs:

# an object that is datetimelike will raise a TypeError,
# otherwise an AttributeError
msg = "no attribute"
err = AttributeError
if issubclass(type(o), DatetimeIndexOpsMixin):
err = TypeError
with pytest.raises(err, match=msg):
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(
"op_name, op",
[
("add", "+"),
("sub", "-"),
("mul", "*"),
("mod", "%"),
("pow", "**"),
("truediv", "/"),
("floordiv", "//"),
],
)
@pytest.mark.parametrize("klass", [Series, DataFrame])
def test_binary_ops(klass, op_name, op):
# not using the all_arithmetic_functions fixture with _get_opstr
# as _get_opstr is used internally in the dynamic implementation of the docstring
operand1 = klass.__name__.lower()
operand2 = "other"
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):
Expand Down Expand Up @@ -313,7 +269,7 @@ def test_value_counts_unique_nunique_null(self, null_obj):
klass = type(o)
values = o._ndarray_values

if not self._allow_na_ops(o):
if not allow_na_ops(o):
continue

# special assign to the numpy array
Expand Down Expand Up @@ -794,7 +750,7 @@ def test_fillna(self):
o = orig.copy()
klass = type(o)

if not self._allow_na_ops(o):
if not allow_na_ops(o):
continue

if needs_i8_conversion(o):
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/indexes/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"uint": tm.makeUIntIndex(100),
"range": tm.makeRangeIndex(100),
"float": tm.makeFloatIndex(100),
"bool": Index([True, False]),
"bool": tm.makeBoolIndex(2),
"categorical": tm.makeCategoricalIndex(100),
"interval": tm.makeIntervalIndex(100),
"empty": Index([]),
Expand Down
29 changes: 4 additions & 25 deletions pandas/tests/indexes/datetimes/test_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,44 +7,23 @@
from pandas.core.dtypes.generic import ABCDateOffset

import pandas as pd
from pandas import (
DatetimeIndex,
Index,
PeriodIndex,
Series,
Timestamp,
bdate_range,
date_range,
)
from pandas import DatetimeIndex, Index, Series, Timestamp, bdate_range, date_range
import pandas._testing as tm
from pandas.tests.base.test_ops import Ops

from pandas.tseries.offsets import BDay, BMonthEnd, CDay, Day, Hour

START, END = datetime(2009, 1, 1), datetime(2010, 1, 1)


class TestDatetimeIndexOps(Ops):
def setup_method(self, method):
super().setup_method(method)
mask = lambda x: (isinstance(x, DatetimeIndex) or isinstance(x, PeriodIndex))
self.is_valid_objs = [o for o in self.objs if mask(o)]
self.not_valid_objs = [o for o in self.objs if not mask(o)]

def test_ops_properties(self):
f = lambda x: isinstance(x, DatetimeIndex)
self.check_ops_properties(DatetimeIndex._field_ops, f)
self.check_ops_properties(DatetimeIndex._object_ops, f)
self.check_ops_properties(DatetimeIndex._bool_ops, f)

def test_ops_properties_basic(self):
class TestDatetimeIndexOps:
def test_ops_properties_basic(self, datetime_series):

# sanity check that the behavior didn't change
# GH#7206
for op in ["year", "day", "second", "weekday"]:
msg = f"'Series' object has no attribute '{op}'"
with pytest.raises(AttributeError, match=msg):
getattr(self.dt_series, op)
getattr(datetime_series, op)

# attribute access should still work!
s = Series(dict(year=2000, month=1, day=10))
Expand Down
18 changes: 2 additions & 16 deletions pandas/tests/indexes/period/test_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,11 @@
import pytest

import pandas as pd
from pandas import DatetimeIndex, Index, NaT, PeriodIndex, Series
from pandas import Index, NaT, PeriodIndex, Series
import pandas._testing as tm
from pandas.core.arrays import PeriodArray
from pandas.tests.base.test_ops import Ops


class TestPeriodIndexOps(Ops):
def setup_method(self, method):
super().setup_method(method)
mask = lambda x: (isinstance(x, DatetimeIndex) or isinstance(x, PeriodIndex))
self.is_valid_objs = [o for o in self.objs if mask(o)]
self.not_valid_objs = [o for o in self.objs if not mask(o)]

def test_ops_properties(self):
f = lambda x: isinstance(x, PeriodIndex)
self.check_ops_properties(PeriodArray._field_ops, f)
self.check_ops_properties(PeriodArray._object_ops, f)
self.check_ops_properties(PeriodArray._bool_ops, f)

class TestPeriodIndexOps:
def test_resolution(self):
for freq, expected in zip(
["A", "Q", "M", "D", "H", "T", "S", "L", "U"],
Expand Down
15 changes: 1 addition & 14 deletions pandas/tests/indexes/timedeltas/test_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,13 @@
import pandas as pd
from pandas import Series, TimedeltaIndex, timedelta_range
import pandas._testing as tm
from pandas.tests.base.test_ops import Ops

from pandas.tseries.offsets import Day, Hour


class TestTimedeltaIndexOps(Ops):
def setup_method(self, method):
super().setup_method(method)
mask = lambda x: isinstance(x, TimedeltaIndex)
self.is_valid_objs = [o for o in self.objs if mask(o)]
self.not_valid_objs = []

def test_ops_properties(self):
f = lambda x: isinstance(x, TimedeltaIndex)
self.check_ops_properties(TimedeltaIndex._field_ops, f)
self.check_ops_properties(TimedeltaIndex._object_ops, f)

class TestTimedeltaIndexOps:
def test_value_counts_unique(self):
# GH 7735

idx = timedelta_range("1 days 09:00:00", freq="H", periods=10)
# create repeated values, 'n'th element is repeated by n+1 times
idx = TimedeltaIndex(np.repeat(idx.values, range(1, len(idx) + 1)))
Expand Down
10 changes: 0 additions & 10 deletions pandas/tests/series/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,6 @@
import pandas._testing as tm


@pytest.fixture
def datetime_series():
"""
Fixture for Series of floats with DatetimeIndex
"""
s = tm.makeTimeSeries()
s.name = "ts"
return s


@pytest.fixture
def string_series():
"""
Expand Down

0 comments on commit 01623f8

Please sign in to comment.