Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DEPR: deprecate truediv param in pd.eval #29812

Merged
merged 5 commits into from
Nov 25, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
17 changes: 17 additions & 0 deletions pandas/tests/computation/test_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -2006,6 +2006,23 @@ def test_inf(engine, parser):
assert result == expected


def test_truediv_deprecated(engine, parser):
# GH#29182
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