Skip to content

Commit b209ac3

Browse files
authored
TST/REF: fixturize TestEval (#44663)
1 parent f42fff8 commit b209ac3

File tree

1 file changed

+52
-101
lines changed

1 file changed

+52
-101
lines changed

pandas/tests/computation/test_eval.py

+52-101
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,14 @@
5353
params=(
5454
pytest.param(
5555
engine,
56-
marks=pytest.mark.skipif(
57-
engine == "numexpr" and not USE_NUMEXPR,
58-
reason=f"numexpr enabled->{USE_NUMEXPR}, "
59-
f"installed->{NUMEXPR_INSTALLED}",
60-
),
56+
marks=[
57+
pytest.mark.skipif(
58+
engine == "numexpr" and not USE_NUMEXPR,
59+
reason=f"numexpr enabled->{USE_NUMEXPR}, "
60+
f"installed->{NUMEXPR_INSTALLED}",
61+
),
62+
td.skip_if_no_ne,
63+
],
6164
)
6265
for engine in ENGINES
6366
)
@@ -145,10 +148,12 @@ def lhs(request):
145148
midhs = lhs
146149

147150

148-
@td.skip_if_no_ne
149-
class TestEvalNumexprPandas:
150-
engine = "numexpr"
151-
parser = "pandas"
151+
class TestEval:
152+
@pytest.fixture(autouse=True)
153+
def set_engine_parser_attrs(self, engine, parser):
154+
# Older tests look for these as attributes, so we set them here.
155+
self.engine = engine
156+
self.parser = parser
152157

153158
@classmethod
154159
def setup_class(cls):
@@ -184,47 +189,18 @@ def test_complex_cmp_ops(self, cmp1, cmp2, binop, lhs, rhs):
184189
self.check_equal(result, expected)
185190

186191
@pytest.mark.parametrize("cmp_op", expr.CMP_OPS_SYMS)
187-
def test_simple_cmp_ops(self, cmp_op):
188-
bool_lhses = (
189-
DataFrame(tm.randbool(size=(10, 5))),
190-
Series(tm.randbool((5,))),
191-
tm.randbool(),
192-
)
193-
bool_rhses = (
194-
DataFrame(tm.randbool(size=(10, 5))),
195-
Series(tm.randbool((5,))),
196-
tm.randbool(),
197-
)
192+
def test_simple_cmp_ops(self, cmp_op, lhs, rhs):
193+
lhs = lhs < 0
194+
rhs = rhs < 0
198195

199196
if self.parser == "python" and cmp_op in ["in", "not in"]:
200197
msg = "'(In|NotIn)' nodes are not implemented"
201-
for lhs, rhs in product(bool_lhses, bool_rhses):
202198

203-
with pytest.raises(NotImplementedError, match=msg):
204-
self.check_simple_cmp_op(lhs, cmp_op, rhs)
199+
with pytest.raises(NotImplementedError, match=msg):
200+
self.check_simple_cmp_op(lhs, cmp_op, rhs)
205201
return
206202

207-
for lhs, rhs in product(bool_lhses, bool_rhses):
208-
self.check_simple_cmp_op(lhs, cmp_op, rhs)
209-
210-
@pytest.mark.parametrize("op", _good_arith_ops)
211-
def test_binary_arith_ops(self, op, lhs, rhs, request):
212-
self.check_binary_arith_op(lhs, op, rhs)
213-
214-
def test_modulus(self, lhs, rhs):
215-
self.check_modulus(lhs, "%", rhs)
216-
217-
def test_floor_division(self, lhs, rhs):
218-
self.check_floor_division(lhs, "//", rhs)
219-
220-
@td.skip_if_windows
221-
def test_pow(self, lhs, rhs):
222-
# odd failure on win32 platform, so skip
223-
self.check_pow(lhs, "**", rhs)
224-
225-
@pytest.mark.parametrize("op", expr.CMP_OPS_SYMS)
226-
def test_single_invert_op(self, op, lhs):
227-
self.check_single_invert_op(lhs, op)
203+
self.check_simple_cmp_op(lhs, cmp_op, rhs)
228204

229205
@pytest.mark.parametrize("op", expr.CMP_OPS_SYMS)
230206
def test_compound_invert_op(self, op, lhs, rhs, request):
@@ -266,11 +242,16 @@ def check_equal(self, result, expected):
266242
assert result == expected
267243

268244
def check_chained_cmp_op(self, lhs, cmp1, mid, cmp2, rhs):
269-
def check_operands(left, right, cmp_op):
270-
return _eval_single_bin(left, cmp_op, right, self.engine)
271245

272-
lhs_new = check_operands(lhs, mid, cmp1)
273-
rhs_new = check_operands(mid, rhs, cmp2)
246+
if self.parser == "python":
247+
ex1 = f"lhs {cmp1} mid {cmp2} rhs"
248+
msg = "'BoolOp' nodes are not implemented"
249+
with pytest.raises(NotImplementedError, match=msg):
250+
pd.eval(ex1, engine=self.engine, parser=self.parser)
251+
return
252+
253+
lhs_new = _eval_single_bin(lhs, cmp1, mid, self.engine)
254+
rhs_new = _eval_single_bin(mid, cmp2, rhs, self.engine)
274255

275256
if lhs_new is not None and rhs_new is not None:
276257
ex1 = f"lhs {cmp1} mid {cmp2} rhs"
@@ -306,7 +287,8 @@ def check_simple_cmp_op(self, lhs, cmp1, rhs):
306287
result = pd.eval(ex, engine=self.engine, parser=self.parser)
307288
self.check_equal(result, expected)
308289

309-
def check_binary_arith_op(self, lhs, arith1, rhs):
290+
@pytest.mark.parametrize("arith1", _good_arith_ops)
291+
def test_binary_arith_ops(self, arith1, lhs, rhs):
310292
ex = f"lhs {arith1} rhs"
311293
result = pd.eval(ex, engine=self.engine, parser=self.parser)
312294
expected = _eval_single_bin(lhs, arith1, rhs, self.engine)
@@ -339,20 +321,24 @@ def check_alignment(self, result, nlhs, ghs, op):
339321

340322
# modulus, pow, and floor division require special casing
341323

342-
def check_modulus(self, lhs, arith1, rhs):
343-
ex = f"lhs {arith1} rhs"
324+
def test_modulus(self, lhs, rhs):
325+
ex = r"lhs % rhs"
344326
result = pd.eval(ex, engine=self.engine, parser=self.parser)
345327
expected = lhs % rhs
346-
347328
tm.assert_almost_equal(result, expected)
348-
expected = self.ne.evaluate(f"expected {arith1} rhs")
349-
if isinstance(result, (DataFrame, Series)):
350-
tm.assert_almost_equal(result.values, expected)
329+
330+
if self.engine == "numexpr":
331+
expected = self.ne.evaluate(r"expected % rhs")
332+
if isinstance(result, (DataFrame, Series)):
333+
tm.assert_almost_equal(result.values, expected)
334+
else:
335+
tm.assert_almost_equal(result, expected.item())
351336
else:
352-
tm.assert_almost_equal(result, expected.item())
337+
expected = _eval_single_bin(expected, "%", rhs, self.engine)
338+
tm.assert_almost_equal(result, expected)
353339

354-
def check_floor_division(self, lhs, arith1, rhs):
355-
ex = f"lhs {arith1} rhs"
340+
def test_floor_division(self, lhs, rhs):
341+
ex = "lhs // rhs"
356342

357343
if self.engine == "python":
358344
res = pd.eval(ex, engine=self.engine, parser=self.parser)
@@ -371,8 +357,10 @@ def check_floor_division(self, lhs, arith1, rhs):
371357
parser=self.parser,
372358
)
373359

374-
def check_pow(self, lhs, arith1, rhs):
375-
ex = f"lhs {arith1} rhs"
360+
@td.skip_if_windows
361+
def test_pow(self, lhs, rhs):
362+
# odd failure on win32 platform, so skip
363+
ex = "lhs ** rhs"
376364
expected = _eval_single_bin(lhs, "**", rhs, self.engine)
377365
result = pd.eval(ex, engine=self.engine, parser=self.parser)
378366

@@ -387,19 +375,19 @@ def check_pow(self, lhs, arith1, rhs):
387375
else:
388376
tm.assert_almost_equal(result, expected)
389377

390-
ex = f"(lhs {arith1} rhs) {arith1} rhs"
378+
ex = "(lhs ** rhs) ** rhs"
391379
result = pd.eval(ex, engine=self.engine, parser=self.parser)
392380

393381
middle = _eval_single_bin(lhs, "**", rhs, self.engine)
394382
expected = _eval_single_bin(middle, "**", rhs, self.engine)
395383
tm.assert_almost_equal(result, expected)
396384

397-
def check_single_invert_op(self, elem, cmp1):
385+
def check_single_invert_op(self, lhs):
398386
# simple
399387
try:
400-
elb = elem.astype(bool)
388+
elb = lhs.astype(bool)
401389
except AttributeError:
402-
elb = np.array([bool(elem)])
390+
elb = np.array([bool(lhs)])
403391
expected = ~elb
404392
result = pd.eval("~elb", engine=self.engine, parser=self.parser)
405393
tm.assert_almost_equal(expected, result)
@@ -766,43 +754,6 @@ def test_disallow_python_keywords(self):
766754
df.query("lambda == 0")
767755

768756

769-
@td.skip_if_no_ne
770-
class TestEvalNumexprPython(TestEvalNumexprPandas):
771-
engine = "numexpr"
772-
parser = "python"
773-
774-
def check_chained_cmp_op(self, lhs, cmp1, mid, cmp2, rhs):
775-
ex1 = f"lhs {cmp1} mid {cmp2} rhs"
776-
msg = "'BoolOp' nodes are not implemented"
777-
with pytest.raises(NotImplementedError, match=msg):
778-
pd.eval(ex1, engine=self.engine, parser=self.parser)
779-
780-
781-
class TestEvalPythonPython(TestEvalNumexprPython):
782-
engine = "python"
783-
parser = "python"
784-
785-
def check_modulus(self, lhs, arith1, rhs):
786-
ex = f"lhs {arith1} rhs"
787-
result = pd.eval(ex, engine=self.engine, parser=self.parser)
788-
789-
expected = lhs % rhs
790-
tm.assert_almost_equal(result, expected)
791-
792-
expected = _eval_single_bin(expected, arith1, rhs, self.engine)
793-
tm.assert_almost_equal(result, expected)
794-
795-
796-
class TestEvalPythonPandas(TestEvalPythonPython):
797-
engine = "python"
798-
parser = "pandas"
799-
800-
def check_chained_cmp_op(self, lhs, cmp1, mid, cmp2, rhs):
801-
# FIXME: by calling this parent class method, we are using the parent
802-
# class's "engine" and "parser", which I don't think is what we want.
803-
TestEvalNumexprPandas.check_chained_cmp_op(self, lhs, cmp1, mid, cmp2, rhs)
804-
805-
806757
f = lambda *args, **kwargs: np.random.randn()
807758

808759

0 commit comments

Comments
 (0)