Skip to content

Commit 4efc97c

Browse files
authored
TST: annotations, fix test_invert (#54576)
* TST: annotations, fix test_invert * mypy fixup * trim runtime * use TYPE_CHECKING * xfail fixed
1 parent 3b34c3b commit 4efc97c

File tree

9 files changed

+87
-87
lines changed

9 files changed

+87
-87
lines changed

pandas/tests/extension/base/ops.py

+17-3
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,23 @@ def test_compare_array(self, data, comparison_op):
239239
class BaseUnaryOpsTests(BaseOpsUtil):
240240
def test_invert(self, data):
241241
ser = pd.Series(data, name="name")
242-
result = ~ser
243-
expected = pd.Series(~data, name="name")
244-
tm.assert_series_equal(result, expected)
242+
try:
243+
# 10 is an arbitrary choice here, just avoid iterating over
244+
# the whole array to trim test runtime
245+
[~x for x in data[:10]]
246+
except TypeError:
247+
# scalars don't support invert -> we don't expect the vectorized
248+
# operation to succeed
249+
with pytest.raises(TypeError):
250+
~ser
251+
with pytest.raises(TypeError):
252+
~data
253+
else:
254+
# Note we do not re-use the pointwise result to construct expected
255+
# because python semantics for negating bools are weird see GH#54569
256+
result = ~ser
257+
expected = pd.Series(~data, name="name")
258+
tm.assert_series_equal(result, expected)
245259

246260
@pytest.mark.parametrize("ufunc", [np.positive, np.negative, np.abs])
247261
def test_unary_ufunc_dunder_equivalence(self, data, ufunc):

pandas/tests/extension/base/reduce.py

+22-21
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,23 @@ class BaseReduceTests:
1313
make sense for numeric/boolean operations.
1414
"""
1515

16-
def _supports_reduction(self, obj, op_name: str) -> bool:
16+
def _supports_reduction(self, ser: pd.Series, op_name: str) -> bool:
1717
# Specify if we expect this reduction to succeed.
1818
return False
1919

20-
def check_reduce(self, s, op_name, skipna):
20+
def check_reduce(self, ser: pd.Series, op_name: str, skipna: bool):
2121
# We perform the same operation on the np.float64 data and check
2222
# that the results match. Override if you need to cast to something
2323
# other than float64.
24-
res_op = getattr(s, op_name)
24+
res_op = getattr(ser, op_name)
2525

2626
try:
27-
alt = s.astype("float64")
28-
except TypeError:
29-
# e.g. Interval can't cast, so let's cast to object and do
27+
alt = ser.astype("float64")
28+
except (TypeError, ValueError):
29+
# e.g. Interval can't cast (TypeError), StringArray can't cast
30+
# (ValueError), so let's cast to object and do
3031
# the reduction pointwise
31-
alt = s.astype(object)
32+
alt = ser.astype(object)
3233

3334
exp_op = getattr(alt, op_name)
3435
if op_name == "count":
@@ -79,53 +80,53 @@ def check_reduce_frame(self, ser: pd.Series, op_name: str, skipna: bool):
7980
@pytest.mark.parametrize("skipna", [True, False])
8081
def test_reduce_series_boolean(self, data, all_boolean_reductions, skipna):
8182
op_name = all_boolean_reductions
82-
s = pd.Series(data)
83+
ser = pd.Series(data)
8384

84-
if not self._supports_reduction(s, op_name):
85+
if not self._supports_reduction(ser, op_name):
8586
msg = (
8687
"[Cc]annot perform|Categorical is not ordered for operation|"
8788
"does not support reduction|"
8889
)
8990

9091
with pytest.raises(TypeError, match=msg):
91-
getattr(s, op_name)(skipna=skipna)
92+
getattr(ser, op_name)(skipna=skipna)
9293

9394
else:
94-
self.check_reduce(s, op_name, skipna)
95+
self.check_reduce(ser, op_name, skipna)
9596

9697
@pytest.mark.filterwarnings("ignore::RuntimeWarning")
9798
@pytest.mark.parametrize("skipna", [True, False])
9899
def test_reduce_series_numeric(self, data, all_numeric_reductions, skipna):
99100
op_name = all_numeric_reductions
100-
s = pd.Series(data)
101+
ser = pd.Series(data)
101102

102-
if not self._supports_reduction(s, op_name):
103+
if not self._supports_reduction(ser, op_name):
103104
msg = (
104105
"[Cc]annot perform|Categorical is not ordered for operation|"
105106
"does not support reduction|"
106107
)
107108

108109
with pytest.raises(TypeError, match=msg):
109-
getattr(s, op_name)(skipna=skipna)
110+
getattr(ser, op_name)(skipna=skipna)
110111

111112
else:
112113
# min/max with empty produce numpy warnings
113-
self.check_reduce(s, op_name, skipna)
114+
self.check_reduce(ser, op_name, skipna)
114115

115116
@pytest.mark.parametrize("skipna", [True, False])
116117
def test_reduce_frame(self, data, all_numeric_reductions, skipna):
117118
op_name = all_numeric_reductions
118-
s = pd.Series(data)
119-
if not is_numeric_dtype(s.dtype):
119+
ser = pd.Series(data)
120+
if not is_numeric_dtype(ser.dtype):
120121
pytest.skip("not numeric dtype")
121122

122123
if op_name in ["count", "kurt", "sem"]:
123124
pytest.skip(f"{op_name} not an array method")
124125

125-
if not self._supports_reduction(s, op_name):
126+
if not self._supports_reduction(ser, op_name):
126127
pytest.skip(f"Reduction {op_name} not supported for this dtype")
127128

128-
self.check_reduce_frame(s, op_name, skipna)
129+
self.check_reduce_frame(ser, op_name, skipna)
129130

130131

131132
# TODO: deprecate BaseNoReduceTests, BaseNumericReduceTests, BaseBooleanReduceTests
@@ -135,15 +136,15 @@ class BaseNoReduceTests(BaseReduceTests):
135136

136137
class BaseNumericReduceTests(BaseReduceTests):
137138
# For backward compatibility only, this only runs the numeric reductions
138-
def _supports_reduction(self, obj, op_name: str) -> bool:
139+
def _supports_reduction(self, ser: pd.Series, op_name: str) -> bool:
139140
if op_name in ["any", "all"]:
140141
pytest.skip("These are tested in BaseBooleanReduceTests")
141142
return True
142143

143144

144145
class BaseBooleanReduceTests(BaseReduceTests):
145146
# For backward compatibility only, this only runs the numeric reductions
146-
def _supports_reduction(self, obj, op_name: str) -> bool:
147+
def _supports_reduction(self, ser: pd.Series, op_name: str) -> bool:
147148
if op_name not in ["any", "all"]:
148149
pytest.skip("These are tested in BaseNumericReduceTests")
149150
return True

pandas/tests/extension/decimal/test_decimal.py

+5-11
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,15 @@ def _get_expected_exception(
7171
) -> type[Exception] | None:
7272
return None
7373

74-
def _supports_reduction(self, obj, op_name: str) -> bool:
74+
def _supports_reduction(self, ser: pd.Series, op_name: str) -> bool:
7575
return True
7676

77-
def check_reduce(self, s, op_name, skipna):
77+
def check_reduce(self, ser: pd.Series, op_name: str, skipna: bool):
7878
if op_name == "count":
79-
return super().check_reduce(s, op_name, skipna)
79+
return super().check_reduce(ser, op_name, skipna)
8080
else:
81-
result = getattr(s, op_name)(skipna=skipna)
82-
expected = getattr(np.asarray(s), op_name)()
81+
result = getattr(ser, op_name)(skipna=skipna)
82+
expected = getattr(np.asarray(ser), op_name)()
8383
tm.assert_almost_equal(result, expected)
8484

8585
def test_reduce_series_numeric(self, data, all_numeric_reductions, skipna, request):
@@ -216,12 +216,6 @@ def test_series_repr(self, data):
216216
assert data.dtype.name in repr(ser)
217217
assert "Decimal: " in repr(ser)
218218

219-
@pytest.mark.xfail(
220-
reason="Looks like the test (incorrectly) implicitly assumes int/bool dtype"
221-
)
222-
def test_invert(self, data):
223-
super().test_invert(data)
224-
225219
@pytest.mark.xfail(reason="Inconsistent array-vs-scalar behavior")
226220
@pytest.mark.parametrize("ufunc", [np.positive, np.negative, np.abs])
227221
def test_unary_ufunc_dunder_equivalence(self, data, ufunc):

pandas/tests/extension/test_arrow.py

+16-11
Original file line numberDiff line numberDiff line change
@@ -401,8 +401,8 @@ def test_accumulate_series(self, data, all_numeric_accumulations, skipna, reques
401401

402402
self.check_accumulate(ser, op_name, skipna)
403403

404-
def _supports_reduction(self, obj, op_name: str) -> bool:
405-
dtype = tm.get_dtype(obj)
404+
def _supports_reduction(self, ser: pd.Series, op_name: str) -> bool:
405+
dtype = ser.dtype
406406
# error: Item "dtype[Any]" of "dtype[Any] | ExtensionDtype" has
407407
# no attribute "pyarrow_dtype"
408408
pa_dtype = dtype.pyarrow_dtype # type: ignore[union-attr]
@@ -445,20 +445,25 @@ def _supports_reduction(self, obj, op_name: str) -> bool:
445445

446446
return True
447447

448-
def check_reduce(self, ser, op_name, skipna):
449-
pa_dtype = ser.dtype.pyarrow_dtype
450-
if op_name == "count":
451-
result = getattr(ser, op_name)()
448+
def check_reduce(self, ser: pd.Series, op_name: str, skipna: bool):
449+
# error: Item "dtype[Any]" of "dtype[Any] | ExtensionDtype" has no
450+
# attribute "pyarrow_dtype"
451+
pa_dtype = ser.dtype.pyarrow_dtype # type: ignore[union-attr]
452+
if pa.types.is_integer(pa_dtype) or pa.types.is_floating(pa_dtype):
453+
alt = ser.astype("Float64")
452454
else:
453-
result = getattr(ser, op_name)(skipna=skipna)
455+
# TODO: in the opposite case, aren't we testing... nothing? For
456+
# e.g. date/time dtypes trying to calculate 'expected' by converting
457+
# to object will raise for mean, std etc
458+
alt = ser
454459

455-
if pa.types.is_integer(pa_dtype) or pa.types.is_floating(pa_dtype):
456-
ser = ser.astype("Float64")
457460
# TODO: in the opposite case, aren't we testing... nothing?
458461
if op_name == "count":
459-
expected = getattr(ser, op_name)()
462+
result = getattr(ser, op_name)()
463+
expected = getattr(alt, op_name)()
460464
else:
461-
expected = getattr(ser, op_name)(skipna=skipna)
465+
result = getattr(ser, op_name)(skipna=skipna)
466+
expected = getattr(alt, op_name)(skipna=skipna)
462467
tm.assert_almost_equal(result, expected)
463468

464469
@pytest.mark.parametrize("skipna", [True, False])

pandas/tests/extension/test_categorical.py

-6
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,6 @@ def _compare_other(self, s, data, op, other):
179179
def test_array_repr(self, data, size):
180180
super().test_array_repr(data, size)
181181

182-
@pytest.mark.xfail(
183-
reason="Looks like the test (incorrectly) implicitly assumes int/bool dtype"
184-
)
185-
def test_invert(self, data):
186-
super().test_invert(data)
187-
188182
@pytest.mark.xfail(reason="TBD")
189183
@pytest.mark.parametrize("as_index", [True, False])
190184
def test_groupby_extension_agg(self, as_index, data_for_grouping):

pandas/tests/extension/test_interval.py

+8-7
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
be added to the array-specific tests in `pandas/tests/arrays/`.
1414
1515
"""
16+
from __future__ import annotations
17+
18+
from typing import TYPE_CHECKING
19+
1620
import numpy as np
1721
import pytest
1822

@@ -22,6 +26,9 @@
2226
from pandas.core.arrays import IntervalArray
2327
from pandas.tests.extension import base
2428

29+
if TYPE_CHECKING:
30+
import pandas as pd
31+
2532

2633
def make_data():
2734
N = 100
@@ -73,7 +80,7 @@ def data_for_grouping():
7380
class TestIntervalArray(base.ExtensionTests):
7481
divmod_exc = TypeError
7582

76-
def _supports_reduction(self, obj, op_name: str) -> bool:
83+
def _supports_reduction(self, ser: pd.Series, op_name: str) -> bool:
7784
return op_name in ["min", "max"]
7885

7986
@pytest.mark.xfail(
@@ -89,12 +96,6 @@ def test_EA_types(self, engine, data):
8996
with pytest.raises(NotImplementedError, match=expected_msg):
9097
super().test_EA_types(engine, data)
9198

92-
@pytest.mark.xfail(
93-
reason="Looks like the test (incorrectly) implicitly assumes int/bool dtype"
94-
)
95-
def test_invert(self, data):
96-
super().test_invert(data)
97-
9899

99100
# TODO: either belongs in tests.arrays.interval or move into base tests.
100101
def test_fillna_non_scalar_raises(data_missing):

pandas/tests/extension/test_masked.py

+8-13
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,8 @@ def test_combine_le(self, data_repeated):
238238
self._combine_le_expected_dtype = object
239239
super().test_combine_le(data_repeated)
240240

241-
def _supports_reduction(self, obj, op_name: str) -> bool:
242-
if op_name in ["any", "all"] and tm.get_dtype(obj).kind != "b":
241+
def _supports_reduction(self, ser: pd.Series, op_name: str) -> bool:
242+
if op_name in ["any", "all"] and ser.dtype.kind != "b":
243243
pytest.skip(reason="Tested in tests/reductions/test_reductions.py")
244244
return True
245245

@@ -256,12 +256,16 @@ def check_reduce(self, ser: pd.Series, op_name: str, skipna: bool):
256256
if op_name in ["min", "max"]:
257257
cmp_dtype = "bool"
258258

259+
# TODO: prod with integer dtypes does *not* match the result we would
260+
# get if we used object for cmp_dtype. In that cae the object result
261+
# is a large integer while the non-object case overflows and returns 0
262+
alt = ser.dropna().astype(cmp_dtype)
259263
if op_name == "count":
260264
result = getattr(ser, op_name)()
261-
expected = getattr(ser.dropna().astype(cmp_dtype), op_name)()
265+
expected = getattr(alt, op_name)()
262266
else:
263267
result = getattr(ser, op_name)(skipna=skipna)
264-
expected = getattr(ser.dropna().astype(cmp_dtype), op_name)(skipna=skipna)
268+
expected = getattr(alt, op_name)(skipna=skipna)
265269
if not skipna and ser.isna().any() and op_name not in ["any", "all"]:
266270
expected = pd.NA
267271
tm.assert_almost_equal(result, expected)
@@ -350,15 +354,6 @@ def check_accumulate(self, ser: pd.Series, op_name: str, skipna: bool):
350354
else:
351355
raise NotImplementedError(f"{op_name} not supported")
352356

353-
def test_invert(self, data, request):
354-
if data.dtype.kind == "f":
355-
mark = pytest.mark.xfail(
356-
reason="Looks like the base class test implicitly assumes "
357-
"boolean/integer dtypes"
358-
)
359-
request.node.add_marker(mark)
360-
super().test_invert(data)
361-
362357

363358
class Test2DCompat(base.Dim2CompatTests):
364359
pass

pandas/tests/extension/test_numpy.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -302,15 +302,19 @@ class TestPrinting(BaseNumPyTests, base.BasePrintingTests):
302302

303303

304304
class TestReduce(BaseNumPyTests, base.BaseReduceTests):
305-
def _supports_reduction(self, obj, op_name: str) -> bool:
306-
if tm.get_dtype(obj).kind == "O":
305+
def _supports_reduction(self, ser: pd.Series, op_name: str) -> bool:
306+
if ser.dtype.kind == "O":
307307
return op_name in ["sum", "min", "max", "any", "all"]
308308
return True
309309

310-
def check_reduce(self, s, op_name, skipna):
311-
res_op = getattr(s, op_name)
310+
def check_reduce(self, ser: pd.Series, op_name: str, skipna: bool):
311+
res_op = getattr(ser, op_name)
312312
# avoid coercing int -> float. Just cast to the actual numpy type.
313-
exp_op = getattr(s.astype(s.dtype._dtype), op_name)
313+
# error: Item "ExtensionDtype" of "dtype[Any] | ExtensionDtype" has
314+
# no attribute "numpy_dtype"
315+
cmp_dtype = ser.dtype.numpy_dtype # type: ignore[union-attr]
316+
alt = ser.astype(cmp_dtype)
317+
exp_op = getattr(alt, op_name)
314318
if op_name == "count":
315319
result = res_op()
316320
expected = exp_op()

pandas/tests/extension/test_string.py

+2-10
Original file line numberDiff line numberDiff line change
@@ -157,16 +157,8 @@ def test_fillna_no_op_returns_copy(self, data):
157157

158158

159159
class TestReduce(base.BaseReduceTests):
160-
@pytest.mark.parametrize("skipna", [True, False])
161-
def test_reduce_series_numeric(self, data, all_numeric_reductions, skipna):
162-
op_name = all_numeric_reductions
163-
164-
if op_name in ["min", "max"]:
165-
return None
166-
167-
ser = pd.Series(data)
168-
with pytest.raises(TypeError):
169-
getattr(ser, op_name)(skipna=skipna)
160+
def _supports_reduction(self, ser: pd.Series, op_name: str) -> bool:
161+
return op_name in ["min", "max"]
170162

171163

172164
class TestMethods(base.BaseMethodsTests):

0 commit comments

Comments
 (0)