Skip to content

Commit 01623f8

Browse files
CLN: Refactor pandas/tests/base - part3 (#30147)
1 parent 3ae1710 commit 01623f8

File tree

8 files changed

+53
-145
lines changed

8 files changed

+53
-145
lines changed

pandas/conftest.py

+10
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,16 @@ def tick_classes(request):
868868
)
869869

870870

871+
@pytest.fixture
872+
def datetime_series():
873+
"""
874+
Fixture for Series of floats with DatetimeIndex
875+
"""
876+
s = tm.makeTimeSeries()
877+
s.name = "ts"
878+
return s
879+
880+
871881
@pytest.fixture
872882
def float_frame():
873883
"""

pandas/core/ops/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ def _get_opstr(op):
266266
rtruediv: "/",
267267
operator.floordiv: "//",
268268
rfloordiv: "//",
269-
operator.mod: None, # TODO: Why None for mod but '%' for rmod?
269+
operator.mod: "%",
270270
rmod: "%",
271271
operator.pow: "**",
272272
rpow: "**",

pandas/tests/base/test_ops.py

+34-78
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from datetime import datetime, timedelta
22
from io import StringIO
33
import sys
4+
from typing import Any
45

56
import numpy as np
67
import pytest
@@ -30,17 +31,15 @@
3031
Timestamp,
3132
)
3233
import pandas._testing as tm
33-
from pandas.core.indexes.datetimelike import DatetimeIndexOpsMixin
3434

3535

36-
class Ops:
37-
def _allow_na_ops(self, obj):
38-
"""Whether to skip test cases including NaN"""
39-
if (isinstance(obj, Index) and obj.is_boolean()) or not obj._can_hold_na:
40-
# don't test boolean / integer dtypes
41-
return False
42-
return True
36+
def allow_na_ops(obj: Any) -> bool:
37+
"""Whether to skip test cases including NaN"""
38+
is_bool_index = isinstance(obj, Index) and obj.is_boolean()
39+
return not is_bool_index and obj._can_hold_na
40+
4341

42+
class Ops:
4443
def setup_method(self, method):
4544
self.bool_index = tm.makeBoolIndex(10, name="a")
4645
self.int_index = tm.makeIntIndex(10, name="a")
@@ -83,74 +82,31 @@ def setup_method(self, method):
8382

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

86-
def check_ops_properties(self, props, filter=None, ignore_failures=False):
87-
for op in props:
88-
for o in self.is_valid_objs:
89-
90-
# if a filter, skip if it doesn't match
91-
if filter is not None:
92-
filt = o.index if isinstance(o, Series) else o
93-
if not filter(filt):
94-
continue
95-
96-
try:
97-
if isinstance(o, Series):
98-
expected = Series(getattr(o.index, op), index=o.index, name="a")
99-
else:
100-
expected = getattr(o, op)
101-
except (AttributeError):
102-
if ignore_failures:
103-
continue
104-
105-
result = getattr(o, op)
106-
107-
# these could be series, arrays or scalars
108-
if isinstance(result, Series) and isinstance(expected, Series):
109-
tm.assert_series_equal(result, expected)
110-
elif isinstance(result, Index) and isinstance(expected, Index):
111-
tm.assert_index_equal(result, expected)
112-
elif isinstance(result, np.ndarray) and isinstance(
113-
expected, np.ndarray
114-
):
115-
tm.assert_numpy_array_equal(result, expected)
116-
else:
117-
assert result == expected
118-
119-
# freq raises AttributeError on an Int64Index because its not
120-
# defined we mostly care about Series here anyhow
121-
if not ignore_failures:
122-
for o in self.not_valid_objs:
123-
124-
# an object that is datetimelike will raise a TypeError,
125-
# otherwise an AttributeError
126-
msg = "no attribute"
127-
err = AttributeError
128-
if issubclass(type(o), DatetimeIndexOpsMixin):
129-
err = TypeError
130-
with pytest.raises(err, match=msg):
131-
getattr(o, op)
132-
133-
@pytest.mark.parametrize("klass", [Series, DataFrame])
134-
def test_binary_ops_docs(self, klass):
135-
op_map = {
136-
"add": "+",
137-
"sub": "-",
138-
"mul": "*",
139-
"mod": "%",
140-
"pow": "**",
141-
"truediv": "/",
142-
"floordiv": "//",
143-
}
144-
for op_name in op_map:
145-
operand1 = klass.__name__.lower()
146-
operand2 = "other"
147-
op = op_map[op_name]
148-
expected_str = " ".join([operand1, op, operand2])
149-
assert expected_str in getattr(klass, op_name).__doc__
150-
151-
# reverse version of the binary ops
152-
expected_str = " ".join([operand2, op, operand1])
153-
assert expected_str in getattr(klass, "r" + op_name).__doc__
85+
86+
@pytest.mark.parametrize(
87+
"op_name, op",
88+
[
89+
("add", "+"),
90+
("sub", "-"),
91+
("mul", "*"),
92+
("mod", "%"),
93+
("pow", "**"),
94+
("truediv", "/"),
95+
("floordiv", "//"),
96+
],
97+
)
98+
@pytest.mark.parametrize("klass", [Series, DataFrame])
99+
def test_binary_ops(klass, op_name, op):
100+
# not using the all_arithmetic_functions fixture with _get_opstr
101+
# as _get_opstr is used internally in the dynamic implementation of the docstring
102+
operand1 = klass.__name__.lower()
103+
operand2 = "other"
104+
expected_str = " ".join([operand1, op, operand2])
105+
assert expected_str in getattr(klass, op_name).__doc__
106+
107+
# reverse version of the binary ops
108+
expected_str = " ".join([operand2, op, operand1])
109+
assert expected_str in getattr(klass, "r" + op_name).__doc__
154110

155111

156112
class TestTranspose(Ops):
@@ -313,7 +269,7 @@ def test_value_counts_unique_nunique_null(self, null_obj):
313269
klass = type(o)
314270
values = o._ndarray_values
315271

316-
if not self._allow_na_ops(o):
272+
if not allow_na_ops(o):
317273
continue
318274

319275
# special assign to the numpy array
@@ -794,7 +750,7 @@ def test_fillna(self):
794750
o = orig.copy()
795751
klass = type(o)
796752

797-
if not self._allow_na_ops(o):
753+
if not allow_na_ops(o):
798754
continue
799755

800756
if needs_i8_conversion(o):

pandas/tests/indexes/conftest.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"uint": tm.makeUIntIndex(100),
1515
"range": tm.makeRangeIndex(100),
1616
"float": tm.makeFloatIndex(100),
17-
"bool": Index([True, False]),
17+
"bool": tm.makeBoolIndex(2),
1818
"categorical": tm.makeCategoricalIndex(100),
1919
"interval": tm.makeIntervalIndex(100),
2020
"empty": Index([]),

pandas/tests/indexes/datetimes/test_ops.py

+4-25
Original file line numberDiff line numberDiff line change
@@ -7,44 +7,23 @@
77
from pandas.core.dtypes.generic import ABCDateOffset
88

99
import pandas as pd
10-
from pandas import (
11-
DatetimeIndex,
12-
Index,
13-
PeriodIndex,
14-
Series,
15-
Timestamp,
16-
bdate_range,
17-
date_range,
18-
)
10+
from pandas import DatetimeIndex, Index, Series, Timestamp, bdate_range, date_range
1911
import pandas._testing as tm
20-
from pandas.tests.base.test_ops import Ops
2112

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

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

2617

27-
class TestDatetimeIndexOps(Ops):
28-
def setup_method(self, method):
29-
super().setup_method(method)
30-
mask = lambda x: (isinstance(x, DatetimeIndex) or isinstance(x, PeriodIndex))
31-
self.is_valid_objs = [o for o in self.objs if mask(o)]
32-
self.not_valid_objs = [o for o in self.objs if not mask(o)]
33-
34-
def test_ops_properties(self):
35-
f = lambda x: isinstance(x, DatetimeIndex)
36-
self.check_ops_properties(DatetimeIndex._field_ops, f)
37-
self.check_ops_properties(DatetimeIndex._object_ops, f)
38-
self.check_ops_properties(DatetimeIndex._bool_ops, f)
39-
40-
def test_ops_properties_basic(self):
18+
class TestDatetimeIndexOps:
19+
def test_ops_properties_basic(self, datetime_series):
4120

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

4928
# attribute access should still work!
5029
s = Series(dict(year=2000, month=1, day=10))

pandas/tests/indexes/period/test_ops.py

+2-16
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,11 @@
22
import pytest
33

44
import pandas as pd
5-
from pandas import DatetimeIndex, Index, NaT, PeriodIndex, Series
5+
from pandas import Index, NaT, PeriodIndex, Series
66
import pandas._testing as tm
7-
from pandas.core.arrays import PeriodArray
8-
from pandas.tests.base.test_ops import Ops
97

108

11-
class TestPeriodIndexOps(Ops):
12-
def setup_method(self, method):
13-
super().setup_method(method)
14-
mask = lambda x: (isinstance(x, DatetimeIndex) or isinstance(x, PeriodIndex))
15-
self.is_valid_objs = [o for o in self.objs if mask(o)]
16-
self.not_valid_objs = [o for o in self.objs if not mask(o)]
17-
18-
def test_ops_properties(self):
19-
f = lambda x: isinstance(x, PeriodIndex)
20-
self.check_ops_properties(PeriodArray._field_ops, f)
21-
self.check_ops_properties(PeriodArray._object_ops, f)
22-
self.check_ops_properties(PeriodArray._bool_ops, f)
23-
9+
class TestPeriodIndexOps:
2410
def test_resolution(self):
2511
for freq, expected in zip(
2612
["A", "Q", "M", "D", "H", "T", "S", "L", "U"],

pandas/tests/indexes/timedeltas/test_ops.py

+1-14
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,13 @@
88
import pandas as pd
99
from pandas import Series, TimedeltaIndex, timedelta_range
1010
import pandas._testing as tm
11-
from pandas.tests.base.test_ops import Ops
1211

1312
from pandas.tseries.offsets import Day, Hour
1413

1514

16-
class TestTimedeltaIndexOps(Ops):
17-
def setup_method(self, method):
18-
super().setup_method(method)
19-
mask = lambda x: isinstance(x, TimedeltaIndex)
20-
self.is_valid_objs = [o for o in self.objs if mask(o)]
21-
self.not_valid_objs = []
22-
23-
def test_ops_properties(self):
24-
f = lambda x: isinstance(x, TimedeltaIndex)
25-
self.check_ops_properties(TimedeltaIndex._field_ops, f)
26-
self.check_ops_properties(TimedeltaIndex._object_ops, f)
27-
15+
class TestTimedeltaIndexOps:
2816
def test_value_counts_unique(self):
2917
# GH 7735
30-
3118
idx = timedelta_range("1 days 09:00:00", freq="H", periods=10)
3219
# create repeated values, 'n'th element is repeated by n+1 times
3320
idx = TimedeltaIndex(np.repeat(idx.values, range(1, len(idx) + 1)))

pandas/tests/series/conftest.py

-10
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,6 @@
33
import pandas._testing as tm
44

55

6-
@pytest.fixture
7-
def datetime_series():
8-
"""
9-
Fixture for Series of floats with DatetimeIndex
10-
"""
11-
s = tm.makeTimeSeries()
12-
s.name = "ts"
13-
return s
14-
15-
166
@pytest.fixture
177
def string_series():
188
"""

0 commit comments

Comments
 (0)