From 2895b9bb6af8a36be860f2e4fe9bce6d89e42a1f Mon Sep 17 00:00:00 2001 From: root Date: Wed, 15 May 2024 09:48:27 +0000 Subject: [PATCH] Fix for issue #28283: Ensure DataFrame.eval calls __finalize__ --- pandas/core/frame.py | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index a4decab6e8a2b2..d9a3471b3237f4 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4628,27 +4628,36 @@ def query(self, expr: str, *, inplace: bool = False, **kwargs) -> DataFrame | No return result @overload - def eval(self, expr: str, *, inplace: Literal[False] = ..., **kwargs) -> Any: ... - @overload - def eval(self, expr: str, *, inplace: Literal[True], **kwargs) -> None: ... +def eval(self, expr: str, *, inplace: bool = False, **kwargs) -> Any | None: + """ + Evaluate a string describing operations on DataFrame columns. - def eval(self, expr: str, *, inplace: bool = False, **kwargs) -> Any | None: - """ - Evaluate a string describing operations on DataFrame columns. + Operates on columns only, not specific rows or elements. This allows + `eval` to run arbitrary code, which can make you vulnerable to code + injection if you pass user input to this function. - Operates on columns only, not specific rows or elements. This allows - `eval` to run arbitrary code, which can make you vulnerable to code - injection if you pass user input to this function. + Parameters + ---------- + expr : str + The expression string to evaluate. + inplace : bool, default False + If the expression contains an assignment, whether to perform the + operation inplace and mutate the existing DataFrame. Otherwise, + a new DataFrame is returned. + """ + from pandas.core.computation.eval import eval as _eval + + if not isinstance(expr, str): + msg = f"expr must be a string to be evaluated, {type(expr)} given" + raise ValueError(msg) + + # Ensure __finalize__ is called to propagate metadata + result = super().eval(expr, inplace=inplace, **kwargs) + if not inplace: + result = result.__finalize__(self, method="eval") + return result - Parameters - ---------- - expr : str - The expression string to evaluate. - inplace : bool, default False - If the expression contains an assignment, whether to perform the - operation inplace and mutate the existing DataFrame. Otherwise, - a new DataFrame is returned. **kwargs See the documentation for :func:`eval` for complete details on the keyword arguments accepted by