diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 84ad478226175..58aab96aa95eb 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -784,6 +784,7 @@ Reshaping - Bug in :func:`concat` was not allowing for concatenation of ``DataFrame`` and ``Series`` with duplicate keys (:issue:`33654`) - Bug in :func:`cut` raised an error when non-unique labels (:issue:`33141`) - Bug in :meth:`DataFrame.replace` casts columns to ``object`` dtype if items in ``to_replace`` not in values (:issue:`32988`) +- Ensure only named functions can be used in :func:`eval()` (:issue:`32460`) Sparse diff --git a/pandas/core/computation/expr.py b/pandas/core/computation/expr.py index 6cd9a15b70d39..fcccc24ed7615 100644 --- a/pandas/core/computation/expr.py +++ b/pandas/core/computation/expr.py @@ -641,7 +641,7 @@ def visit_Attribute(self, node, **kwargs): def visit_Call(self, node, side=None, **kwargs): - if isinstance(node.func, ast.Attribute): + if isinstance(node.func, ast.Attribute) and node.func.attr != "__call__": res = self.visit_Attribute(node.func) elif not isinstance(node.func, ast.Name): raise TypeError("Only named functions are supported") diff --git a/pandas/tests/frame/test_query_eval.py b/pandas/tests/frame/test_query_eval.py index 1a07780462ea3..8612c05e6a996 100644 --- a/pandas/tests/frame/test_query_eval.py +++ b/pandas/tests/frame/test_query_eval.py @@ -1181,3 +1181,24 @@ def test_failing_character_outside_range(self, df): def test_failing_hashtag(self, df): with pytest.raises(SyntaxError): df.query("`foo#bar` > 4") + + def test_call_non_named_expression(self, df): + """ + Only attributes and variables ('named functions') can be called. + .__call__() is not an allowed attribute because that would allow + calling anything. + https://github.com/pandas-dev/pandas/pull/32460 + """ + + def func(*_): + return 1 + + funcs = [func] # noqa + + df.eval("@func()") + + with pytest.raises(TypeError, match="Only named functions are supported"): + df.eval("@funcs[0]()") + + with pytest.raises(TypeError, match="Only named functions are supported"): + df.eval("@funcs[0].__call__()")