Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/source/whatsnew/v1.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ Deprecations
value in ``idx`` of ``idx_val`` and a new value of ``val``, ``idx.set_value(arr, idx_val, val)``
is equivalent to ``arr[idx.get_loc(idx_val)] = val``, which should be used instead (:issue:`28621`).
- :func:`is_extension_type` is deprecated, :func:`is_extension_array_dtype` should be used instead (:issue:`29457`)

- :func:`eval` keyword argument "truediv" is deprecated and will be removed in a future version (:issue:`29812`)

.. _whatsnew_1000.prior_deprecations:

Expand Down
19 changes: 5 additions & 14 deletions pandas/core/computation/engines.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import abc

from pandas.core.computation.align import align_terms, reconstruct_object
from pandas.core.computation.ops import UndefinedVariableError, _mathops, _reductions
from pandas.core.computation.ops import _mathops, _reductions

import pandas.io.formats.printing as printing

Expand Down Expand Up @@ -114,19 +114,10 @@ def _evaluate(self):
# convert the expression to a valid numexpr expression
s = self.convert()

try:
env = self.expr.env
scope = env.full_scope
truediv = scope["truediv"]
_check_ne_builtin_clash(self.expr)
return ne.evaluate(s, local_dict=scope, truediv=truediv)
except KeyError as e:
# python 3 compat kludge
try:
msg = e.message
except AttributeError:
msg = str(e)
raise UndefinedVariableError(msg)
env = self.expr.env
scope = env.full_scope
_check_ne_builtin_clash(self.expr)
return ne.evaluate(s, local_dict=scope)


class PythonEngine(AbstractEngine):
Expand Down
15 changes: 13 additions & 2 deletions pandas/core/computation/eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import tokenize
import warnings

from pandas._libs.lib import _no_default
from pandas.util._validators import validate_bool_kwarg

from pandas.core.computation.engines import _engines
Expand Down Expand Up @@ -169,7 +170,7 @@ def eval(
expr,
parser="pandas",
engine=None,
truediv=True,
truediv=_no_default,
local_dict=None,
global_dict=None,
resolvers=(),
Expand Down Expand Up @@ -219,6 +220,8 @@ def eval(

truediv : bool, optional
Whether to use true division, like in Python >= 3.
deprecated:: 1.0.0

local_dict : dict or None, optional
A dictionary of local variables, taken from locals() by default.
global_dict : dict or None, optional
Expand Down Expand Up @@ -284,6 +287,14 @@ def eval(

inplace = validate_bool_kwarg(inplace, "inplace")

if truediv is not _no_default:
warnings.warn(
"The `truediv` parameter in pd.eval is deprecated and will be "
"removed in a future version.",
FutureWarning,
stacklevel=2,
)

if isinstance(expr, str):
_check_expression(expr)
exprs = [e.strip() for e in expr.splitlines() if e.strip() != ""]
Expand Down Expand Up @@ -317,7 +328,7 @@ def eval(
target=target,
)

parsed_expr = Expr(expr, engine=engine, parser=parser, env=env, truediv=truediv)
parsed_expr = Expr(expr, engine=engine, parser=parser, env=env)

# construct the engine and evaluate the parsed expression
eng = _engines[engine]
Expand Down
18 changes: 12 additions & 6 deletions pandas/core/computation/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import itertools as it
import operator
import tokenize
from typing import Type
from typing import Optional, Type

import numpy as np

Expand Down Expand Up @@ -564,8 +564,7 @@ def visit_BinOp(self, node, **kwargs):
return self._maybe_evaluate_binop(op, op_class, left, right)

def visit_Div(self, node, **kwargs):
truediv = self.env.scope["truediv"]
return lambda lhs, rhs: Div(lhs, rhs, truediv)
return lambda lhs, rhs: Div(lhs, rhs)

def visit_UnaryOp(self, node, **kwargs):
op = self.visit(node.op)
Expand Down Expand Up @@ -813,18 +812,25 @@ class Expr:
engine : str, optional, default 'numexpr'
parser : str, optional, default 'pandas'
env : Scope, optional, default None
truediv : bool, optional, default True
level : int, optional, default 2
"""

env: Scope
engine: str
parser: str

def __init__(
self, expr, engine="numexpr", parser="pandas", env=None, truediv=True, level=0
self,
expr,
engine: str = "numexpr",
parser: str = "pandas",
env: Optional[Scope] = None,
level: int = 0,
):
self.expr = expr
self.env = env or Scope(level=level + 1)
self.engine = engine
self.parser = parser
self.env.scope["truediv"] = truediv
self._visitor = _parsers[parser](self.env, self.engine, self.parser)
self.terms = self.parse()

Expand Down
8 changes: 1 addition & 7 deletions pandas/core/computation/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,9 +391,6 @@ def __call__(self, env):
object
The result of an evaluated expression.
"""
# handle truediv
if self.op == "/" and env.scope["truediv"]:
self.func = operator.truediv

# recurse over the left/right nodes
left = self.lhs(env)
Expand Down Expand Up @@ -505,12 +502,9 @@ class Div(BinOp):
----------
lhs, rhs : Term or Op
The Terms or Ops in the ``/`` expression.
truediv : bool
Whether or not to use true division. With Python 3 this happens
regardless of the value of ``truediv``.
"""

def __init__(self, lhs, rhs, truediv: bool, **kwargs):
def __init__(self, lhs, rhs, **kwargs):
super().__init__("/", lhs, rhs, **kwargs)

if not isnumeric(lhs.return_type) or not isnumeric(rhs.return_type):
Expand Down
16 changes: 16 additions & 0 deletions pandas/tests/computation/test_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -2006,6 +2006,22 @@ def test_inf(engine, parser):
assert result == expected


def test_truediv_deprecated(engine, parser):
match = "The `truediv` parameter in pd.eval is deprecated"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reference issue number above.


with tm.assert_produces_warning(FutureWarning) as m:
pd.eval("1+1", engine=engine, parser=parser, truediv=True)

assert len(m) == 1
assert match in str(m[0].message)

with tm.assert_produces_warning(FutureWarning) as m:
pd.eval("1+1", engine=engine, parser=parser, truediv=False)

assert len(m) == 1
assert match in str(m[0].message)


def test_negate_lt_eq_le(engine, parser):
df = pd.DataFrame([[0, 10], [1, 20]], columns=["cat", "count"])
expected = df[~(df.cat > 0)]
Expand Down