|
1 | 1 | from datetime import datetime, timedelta |
2 | 2 | from io import StringIO |
3 | 3 | import sys |
| 4 | +from typing import Any |
4 | 5 |
|
5 | 6 | import numpy as np |
6 | 7 | import pytest |
|
30 | 31 | Timestamp, |
31 | 32 | ) |
32 | 33 | import pandas._testing as tm |
33 | | -from pandas.core.indexes.datetimelike import DatetimeIndexOpsMixin |
34 | 34 |
|
35 | 35 |
|
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 | + |
43 | 41 |
|
| 42 | +class Ops: |
44 | 43 | def setup_method(self, method): |
45 | 44 | self.bool_index = tm.makeBoolIndex(10, name="a") |
46 | 45 | self.int_index = tm.makeIntIndex(10, name="a") |
@@ -83,74 +82,31 @@ def setup_method(self, method): |
83 | 82 |
|
84 | 83 | self.objs = self.indexes + self.series + self.narrow_series |
85 | 84 |
|
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__ |
154 | 110 |
|
155 | 111 |
|
156 | 112 | class TestTranspose(Ops): |
@@ -313,7 +269,7 @@ def test_value_counts_unique_nunique_null(self, null_obj): |
313 | 269 | klass = type(o) |
314 | 270 | values = o._ndarray_values |
315 | 271 |
|
316 | | - if not self._allow_na_ops(o): |
| 272 | + if not allow_na_ops(o): |
317 | 273 | continue |
318 | 274 |
|
319 | 275 | # special assign to the numpy array |
@@ -794,7 +750,7 @@ def test_fillna(self): |
794 | 750 | o = orig.copy() |
795 | 751 | klass = type(o) |
796 | 752 |
|
797 | | - if not self._allow_na_ops(o): |
| 753 | + if not allow_na_ops(o): |
798 | 754 | continue |
799 | 755 |
|
800 | 756 | if needs_i8_conversion(o): |
|
0 commit comments