Skip to content

Commit 5246d13

Browse files
authored
BUG: Eval scopes ignoring empty dictionaries (#47084) (#47085)
1 parent 16a4e59 commit 5246d13

File tree

4 files changed

+21
-3
lines changed

4 files changed

+21
-3
lines changed

doc/source/whatsnew/v1.4.3.rst

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Fixed regressions
3030

3131
Bug fixes
3232
~~~~~~~~~
33+
- Bug in :meth:`pd.eval`, :meth:`DataFrame.eval` and :meth:`DataFrame.query` where passing empty ``local_dict`` or ``global_dict`` was treated as passing ``None`` (:issue:`47084`)
3334
- Most I/O methods do no longer suppress ``OSError`` and ``ValueError`` when closing file handles (:issue:`47136`)
3435
-
3536

pandas/core/computation/pytables.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,7 @@ def __init__(
563563
self._visitor = None
564564

565565
# capture the environment if needed
566-
local_dict: DeepChainMap[Any, Any] = DeepChainMap()
566+
local_dict: DeepChainMap[Any, Any] | None = None
567567

568568
if isinstance(where, PyTablesExpr):
569569
local_dict = where.env.scope

pandas/core/computation/scope.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,13 @@ def __init__(
133133
# shallow copy here because we don't want to replace what's in
134134
# scope when we align terms (alignment accesses the underlying
135135
# numpy array of pandas objects)
136-
scope_global = self.scope.new_child((global_dict or frame.f_globals).copy())
136+
scope_global = self.scope.new_child(
137+
(global_dict if global_dict is not None else frame.f_globals).copy()
138+
)
137139
self.scope = DeepChainMap(scope_global)
138140
if not isinstance(local_dict, Scope):
139141
scope_local = self.scope.new_child(
140-
(local_dict or frame.f_locals).copy()
142+
(local_dict if local_dict is not None else frame.f_locals).copy()
141143
)
142144
self.scope = DeepChainMap(scope_local)
143145
finally:

pandas/tests/computation/test_eval.py

+15
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
from pandas.core.computation.ops import (
4545
ARITH_OPS_SYMS,
4646
SPECIAL_CASE_ARITH_OPS_SYMS,
47+
UndefinedVariableError,
4748
_binary_math_ops,
4849
_binary_ops_dict,
4950
_unary_math_ops,
@@ -1671,6 +1672,20 @@ def test_no_new_globals(self, engine, parser):
16711672
gbls2 = globals().copy()
16721673
assert gbls == gbls2
16731674

1675+
def test_empty_locals(self, engine, parser):
1676+
# GH 47084
1677+
x = 1 # noqa: F841
1678+
msg = "name 'x' is not defined"
1679+
with pytest.raises(UndefinedVariableError, match=msg):
1680+
pd.eval("x + 1", engine=engine, parser=parser, local_dict={})
1681+
1682+
def test_empty_globals(self, engine, parser):
1683+
# GH 47084
1684+
msg = "name '_var_s' is not defined"
1685+
e = "_var_s * 2"
1686+
with pytest.raises(UndefinedVariableError, match=msg):
1687+
pd.eval(e, engine=engine, parser=parser, global_dict={})
1688+
16741689

16751690
@td.skip_if_no_ne
16761691
def test_invalid_engine():

0 commit comments

Comments
 (0)