diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index da712f84eb1b5..9e2e3faa8386d 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -412,6 +412,7 @@ Other - Improved :class:`Timestamp` type checking in various datetime functions to prevent exceptions when using a subclassed `datetime` (:issue:`25851`) - Bug in :class:`Series` and :class:`DataFrame` repr where ``np.datetime64('NaT')`` and ``np.timedelta64('NaT')`` with ``dtype=object`` would be represented as ``NaN`` (:issue:`25445`) +- Bug in :class:`BaseExprVisitor` which caused :func:`eval` expressions to fail when named keyword arguments were included within the expression string (:issue:`25813`) - - diff --git a/pandas/core/computation/expr.py b/pandas/core/computation/expr.py index e61dbd07dac5d..3b58d7ab201ac 100644 --- a/pandas/core/computation/expr.py +++ b/pandas/core/computation/expr.py @@ -632,11 +632,8 @@ def visit_Call(self, node, side=None, **kwargs): if not isinstance(key, ast.keyword): raise ValueError("keyword error in function call " "'{func}'".format(func=node.func.id)) - if key.arg: - # TODO: bug? - kwargs.append(ast.keyword( - keyword.arg, self.visit(keyword.value))) # noqa + kwargs[key.arg] = self.visit(key.value)() return self.const_type(res(*new_args, **kwargs), self.env) diff --git a/pandas/tests/computation/test_eval.py b/pandas/tests/computation/test_eval.py index 3908a7f1f99aa..b11a5e975149d 100644 --- a/pandas/tests/computation/test_eval.py +++ b/pandas/tests/computation/test_eval.py @@ -1757,6 +1757,29 @@ def test_no_new_globals(self, engine, parser): assert gbls == gbls2 +class TestEvalNamedKWargs(object): + # xref https://github.com/pandas-dev/pandas/issues/25813 + + def test_eval_with_kwargs(self, engine, parser): + def fn(x): + return 2 * x + result = pd.eval("fn(x=2)", engine=engine, parser=parser) + assert result == 4 + + def test_query_str_contains(self, parser): + df = pd.DataFrame([["I", "XYZ"], ["IJ", None]], columns=['A', 'B']) + + expected = df[df["A"].str.contains("J")] + result = df.query("A.str.contains('J')", engine="python", + parser=parser) + tm.assert_frame_equal(result, expected) + + expected = df[df["B"].str.contains("Z", na=False)] + result = df.query("B.str.contains('Z', na=False)", engine="python", + parser=parser) + tm.assert_frame_equal(result, expected) + + @td.skip_if_no_ne def test_invalid_engine(): msg = 'Invalid engine \'asdf\' passed'