|
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