From edd68dd39599c25812d8ef424e902a57fe4d3e62 Mon Sep 17 00:00:00 2001 From: debnathshoham Date: Sat, 22 Oct 2022 13:15:43 +0530 Subject: [PATCH 01/15] initial commit --- doc/source/whatsnew/v1.5.2.rst | 2 +- pandas/core/computation/ops.py | 3 ++- pandas/tests/frame/test_query_eval.py | 9 +++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v1.5.2.rst b/doc/source/whatsnew/v1.5.2.rst index aaf00804262bb..634d967f817b6 100644 --- a/doc/source/whatsnew/v1.5.2.rst +++ b/doc/source/whatsnew/v1.5.2.rst @@ -13,7 +13,7 @@ including other versions of pandas. Fixed regressions ~~~~~~~~~~~~~~~~~ -- +- Fixed regression in :meth:`DataFrame.query` raising ``UndefinedVariableError`` when querying it with class as a name (:issue:`48694`) - .. --------------------------------------------------------------------------- diff --git a/pandas/core/computation/ops.py b/pandas/core/computation/ops.py index f629538281d65..bded09ab2bf66 100644 --- a/pandas/core/computation/ops.py +++ b/pandas/core/computation/ops.py @@ -4,6 +4,7 @@ from __future__ import annotations +import inspect from datetime import datetime from functools import partial import operator @@ -104,7 +105,7 @@ def _resolve_name(self): is_local = self.is_local if local_name in self.env.scope and isinstance( self.env.scope[local_name], type - ): + ) and not inspect.isclass(self.env.scope[local_name]): is_local = False res = self.env.resolve(local_name, is_local=is_local) diff --git a/pandas/tests/frame/test_query_eval.py b/pandas/tests/frame/test_query_eval.py index 4da57fc177712..efff0c97d42c3 100644 --- a/pandas/tests/frame/test_query_eval.py +++ b/pandas/tests/frame/test_query_eval.py @@ -1271,3 +1271,12 @@ def func(*_): with pytest.raises(TypeError, match="Only named functions are supported"): df.eval("@funcs[0].__call__()") + + def test_classname_frame_query(self): + class A: + a = 1 + + df1 = pd.DataFrame({'x': [1, 2, 3], 'y': [4, 5, 6]}) + df_test = DataFrame({'x': [1], 'y': [4]}) + tm.assert_frame_equal(df_test, df1.query(f'x=={A.a}')) + tm.assert_frame_equal(df_test, df1.query('x==@A.a')) From 8f57a6ae89dfde7570bceee8ffdcb7742c609240 Mon Sep 17 00:00:00 2001 From: debnathshoham Date: Sat, 22 Oct 2022 14:11:18 +0530 Subject: [PATCH 02/15] cleanup --- pandas/tests/frame/test_query_eval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/frame/test_query_eval.py b/pandas/tests/frame/test_query_eval.py index efff0c97d42c3..1f355c5e1fe85 100644 --- a/pandas/tests/frame/test_query_eval.py +++ b/pandas/tests/frame/test_query_eval.py @@ -1276,7 +1276,7 @@ def test_classname_frame_query(self): class A: a = 1 - df1 = pd.DataFrame({'x': [1, 2, 3], 'y': [4, 5, 6]}) + df1 = DataFrame({'x': [1, 2, 3], 'y': [4, 5, 6]}) df_test = DataFrame({'x': [1], 'y': [4]}) tm.assert_frame_equal(df_test, df1.query(f'x=={A.a}')) tm.assert_frame_equal(df_test, df1.query('x==@A.a')) From 17ec88004b76352f41d031140ebd172793535bf5 Mon Sep 17 00:00:00 2001 From: debnathshoham Date: Sat, 22 Oct 2022 15:50:13 +0530 Subject: [PATCH 03/15] looking for column in scope --- pandas/core/computation/ops.py | 6 ++---- pandas/tests/computation/test_eval.py | 11 ++++++++++- pandas/tests/frame/test_query_eval.py | 9 --------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/pandas/core/computation/ops.py b/pandas/core/computation/ops.py index bded09ab2bf66..212327bfe0da2 100644 --- a/pandas/core/computation/ops.py +++ b/pandas/core/computation/ops.py @@ -103,10 +103,8 @@ def evaluate(self, *args, **kwargs) -> Term: def _resolve_name(self): local_name = str(self.local_name) is_local = self.is_local - if local_name in self.env.scope and isinstance( - self.env.scope[local_name], type - ) and not inspect.isclass(self.env.scope[local_name]): - is_local = False + if 'column' in self.env.scope and self.env.scope['column'] == local_name: + is_local = not is_local res = self.env.resolve(local_name, is_local=is_local) self.update(res) diff --git a/pandas/tests/computation/test_eval.py b/pandas/tests/computation/test_eval.py index 6548422c5d2b7..4bc41a411386c 100644 --- a/pandas/tests/computation/test_eval.py +++ b/pandas/tests/computation/test_eval.py @@ -1908,7 +1908,7 @@ def test_negate_lt_eq_le(engine, parser): ) def test_eval_no_support_column_name(request, column): # GH 44603 - if column in ["True", "False", "inf", "Inf"]: + if column in ["True", "False"]: request.node.add_marker( pytest.mark.xfail( raises=KeyError, @@ -1922,6 +1922,15 @@ def test_eval_no_support_column_name(request, column): tm.assert_frame_equal(result, expected) +def test_classname_frame_query(): + # GH 48694 + class A: + a = 1 + + df1 = DataFrame({'x': [1, 2, 3], 'y': [4, 5, 6]}) + df_test = DataFrame({'x': [1], 'y': [4]}) + tm.assert_frame_equal(df_test, df1.query(f'x=={A.a}')) + tm.assert_frame_equal(df_test, df1.query('x==@A.a')) @td.skip_array_manager_not_yet_implemented def test_set_inplace(using_copy_on_write): diff --git a/pandas/tests/frame/test_query_eval.py b/pandas/tests/frame/test_query_eval.py index 1f355c5e1fe85..4da57fc177712 100644 --- a/pandas/tests/frame/test_query_eval.py +++ b/pandas/tests/frame/test_query_eval.py @@ -1271,12 +1271,3 @@ def func(*_): with pytest.raises(TypeError, match="Only named functions are supported"): df.eval("@funcs[0].__call__()") - - def test_classname_frame_query(self): - class A: - a = 1 - - df1 = DataFrame({'x': [1, 2, 3], 'y': [4, 5, 6]}) - df_test = DataFrame({'x': [1], 'y': [4]}) - tm.assert_frame_equal(df_test, df1.query(f'x=={A.a}')) - tm.assert_frame_equal(df_test, df1.query('x==@A.a')) From 7a66a0608fa0f7e46a64f20797696cd3cc84dda1 Mon Sep 17 00:00:00 2001 From: debnathshoham Date: Sat, 22 Oct 2022 15:57:11 +0530 Subject: [PATCH 04/15] looking for column in scope --- pandas/core/computation/ops.py | 4 ++-- pandas/tests/computation/test_eval.py | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pandas/core/computation/ops.py b/pandas/core/computation/ops.py index 212327bfe0da2..887f3b26d55a6 100644 --- a/pandas/core/computation/ops.py +++ b/pandas/core/computation/ops.py @@ -4,9 +4,9 @@ from __future__ import annotations -import inspect from datetime import datetime from functools import partial +import inspect import operator from typing import ( Callable, @@ -103,7 +103,7 @@ def evaluate(self, *args, **kwargs) -> Term: def _resolve_name(self): local_name = str(self.local_name) is_local = self.is_local - if 'column' in self.env.scope and self.env.scope['column'] == local_name: + if "column" in self.env.scope and self.env.scope["column"] == local_name: is_local = not is_local res = self.env.resolve(local_name, is_local=is_local) diff --git a/pandas/tests/computation/test_eval.py b/pandas/tests/computation/test_eval.py index 4bc41a411386c..7d677f197008b 100644 --- a/pandas/tests/computation/test_eval.py +++ b/pandas/tests/computation/test_eval.py @@ -1922,15 +1922,17 @@ def test_eval_no_support_column_name(request, column): tm.assert_frame_equal(result, expected) + def test_classname_frame_query(): # GH 48694 class A: a = 1 - df1 = DataFrame({'x': [1, 2, 3], 'y': [4, 5, 6]}) - df_test = DataFrame({'x': [1], 'y': [4]}) - tm.assert_frame_equal(df_test, df1.query(f'x=={A.a}')) - tm.assert_frame_equal(df_test, df1.query('x==@A.a')) + df1 = DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]}) + df_test = DataFrame({"x": [1], "y": [4]}) + tm.assert_frame_equal(df_test, df1.query(f"x=={A.a}")) + tm.assert_frame_equal(df_test, df1.query("x==@A.a")) + @td.skip_array_manager_not_yet_implemented def test_set_inplace(using_copy_on_write): From 2f7ab28d8a82cd472b40ad10720e49e7f624f9c3 Mon Sep 17 00:00:00 2001 From: debnathshoham Date: Sat, 22 Oct 2022 18:05:10 +0530 Subject: [PATCH 05/15] looking for column in scope --- pandas/core/computation/ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/computation/ops.py b/pandas/core/computation/ops.py index 887f3b26d55a6..99c920292009a 100644 --- a/pandas/core/computation/ops.py +++ b/pandas/core/computation/ops.py @@ -6,7 +6,7 @@ from datetime import datetime from functools import partial -import inspect + import operator from typing import ( Callable, From c3a9116db49f66f4d74bf49f885a8d4c890afa0d Mon Sep 17 00:00:00 2001 From: debnathshoham Date: Sat, 22 Oct 2022 13:15:43 +0530 Subject: [PATCH 06/15] initial commit --- doc/source/whatsnew/v1.5.2.rst | 2 +- pandas/core/computation/ops.py | 3 ++- pandas/tests/frame/test_query_eval.py | 9 +++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v1.5.2.rst b/doc/source/whatsnew/v1.5.2.rst index aaf00804262bb..634d967f817b6 100644 --- a/doc/source/whatsnew/v1.5.2.rst +++ b/doc/source/whatsnew/v1.5.2.rst @@ -13,7 +13,7 @@ including other versions of pandas. Fixed regressions ~~~~~~~~~~~~~~~~~ -- +- Fixed regression in :meth:`DataFrame.query` raising ``UndefinedVariableError`` when querying it with class as a name (:issue:`48694`) - .. --------------------------------------------------------------------------- diff --git a/pandas/core/computation/ops.py b/pandas/core/computation/ops.py index f629538281d65..bded09ab2bf66 100644 --- a/pandas/core/computation/ops.py +++ b/pandas/core/computation/ops.py @@ -4,6 +4,7 @@ from __future__ import annotations +import inspect from datetime import datetime from functools import partial import operator @@ -104,7 +105,7 @@ def _resolve_name(self): is_local = self.is_local if local_name in self.env.scope and isinstance( self.env.scope[local_name], type - ): + ) and not inspect.isclass(self.env.scope[local_name]): is_local = False res = self.env.resolve(local_name, is_local=is_local) diff --git a/pandas/tests/frame/test_query_eval.py b/pandas/tests/frame/test_query_eval.py index 4da57fc177712..efff0c97d42c3 100644 --- a/pandas/tests/frame/test_query_eval.py +++ b/pandas/tests/frame/test_query_eval.py @@ -1271,3 +1271,12 @@ def func(*_): with pytest.raises(TypeError, match="Only named functions are supported"): df.eval("@funcs[0].__call__()") + + def test_classname_frame_query(self): + class A: + a = 1 + + df1 = pd.DataFrame({'x': [1, 2, 3], 'y': [4, 5, 6]}) + df_test = DataFrame({'x': [1], 'y': [4]}) + tm.assert_frame_equal(df_test, df1.query(f'x=={A.a}')) + tm.assert_frame_equal(df_test, df1.query('x==@A.a')) From 45f68f85ffa342757a715ced1109f035f6c3932d Mon Sep 17 00:00:00 2001 From: debnathshoham Date: Sat, 22 Oct 2022 14:11:18 +0530 Subject: [PATCH 07/15] cleanup --- pandas/tests/frame/test_query_eval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/frame/test_query_eval.py b/pandas/tests/frame/test_query_eval.py index efff0c97d42c3..1f355c5e1fe85 100644 --- a/pandas/tests/frame/test_query_eval.py +++ b/pandas/tests/frame/test_query_eval.py @@ -1276,7 +1276,7 @@ def test_classname_frame_query(self): class A: a = 1 - df1 = pd.DataFrame({'x': [1, 2, 3], 'y': [4, 5, 6]}) + df1 = DataFrame({'x': [1, 2, 3], 'y': [4, 5, 6]}) df_test = DataFrame({'x': [1], 'y': [4]}) tm.assert_frame_equal(df_test, df1.query(f'x=={A.a}')) tm.assert_frame_equal(df_test, df1.query('x==@A.a')) From 847beed232224f8ce16d66e45c7373e5a1812ef5 Mon Sep 17 00:00:00 2001 From: debnathshoham Date: Sat, 22 Oct 2022 15:50:13 +0530 Subject: [PATCH 08/15] looking for column in scope --- pandas/core/computation/ops.py | 6 ++---- pandas/tests/computation/test_eval.py | 11 ++++++++++- pandas/tests/frame/test_query_eval.py | 9 --------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/pandas/core/computation/ops.py b/pandas/core/computation/ops.py index bded09ab2bf66..212327bfe0da2 100644 --- a/pandas/core/computation/ops.py +++ b/pandas/core/computation/ops.py @@ -103,10 +103,8 @@ def evaluate(self, *args, **kwargs) -> Term: def _resolve_name(self): local_name = str(self.local_name) is_local = self.is_local - if local_name in self.env.scope and isinstance( - self.env.scope[local_name], type - ) and not inspect.isclass(self.env.scope[local_name]): - is_local = False + if 'column' in self.env.scope and self.env.scope['column'] == local_name: + is_local = not is_local res = self.env.resolve(local_name, is_local=is_local) self.update(res) diff --git a/pandas/tests/computation/test_eval.py b/pandas/tests/computation/test_eval.py index 6548422c5d2b7..4bc41a411386c 100644 --- a/pandas/tests/computation/test_eval.py +++ b/pandas/tests/computation/test_eval.py @@ -1908,7 +1908,7 @@ def test_negate_lt_eq_le(engine, parser): ) def test_eval_no_support_column_name(request, column): # GH 44603 - if column in ["True", "False", "inf", "Inf"]: + if column in ["True", "False"]: request.node.add_marker( pytest.mark.xfail( raises=KeyError, @@ -1922,6 +1922,15 @@ def test_eval_no_support_column_name(request, column): tm.assert_frame_equal(result, expected) +def test_classname_frame_query(): + # GH 48694 + class A: + a = 1 + + df1 = DataFrame({'x': [1, 2, 3], 'y': [4, 5, 6]}) + df_test = DataFrame({'x': [1], 'y': [4]}) + tm.assert_frame_equal(df_test, df1.query(f'x=={A.a}')) + tm.assert_frame_equal(df_test, df1.query('x==@A.a')) @td.skip_array_manager_not_yet_implemented def test_set_inplace(using_copy_on_write): diff --git a/pandas/tests/frame/test_query_eval.py b/pandas/tests/frame/test_query_eval.py index 1f355c5e1fe85..4da57fc177712 100644 --- a/pandas/tests/frame/test_query_eval.py +++ b/pandas/tests/frame/test_query_eval.py @@ -1271,12 +1271,3 @@ def func(*_): with pytest.raises(TypeError, match="Only named functions are supported"): df.eval("@funcs[0].__call__()") - - def test_classname_frame_query(self): - class A: - a = 1 - - df1 = DataFrame({'x': [1, 2, 3], 'y': [4, 5, 6]}) - df_test = DataFrame({'x': [1], 'y': [4]}) - tm.assert_frame_equal(df_test, df1.query(f'x=={A.a}')) - tm.assert_frame_equal(df_test, df1.query('x==@A.a')) From 7b0ed783b188d5150844e4ecd2e190306ef11055 Mon Sep 17 00:00:00 2001 From: debnathshoham Date: Sat, 22 Oct 2022 15:57:11 +0530 Subject: [PATCH 09/15] looking for column in scope --- pandas/core/computation/ops.py | 4 ++-- pandas/tests/computation/test_eval.py | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pandas/core/computation/ops.py b/pandas/core/computation/ops.py index 212327bfe0da2..887f3b26d55a6 100644 --- a/pandas/core/computation/ops.py +++ b/pandas/core/computation/ops.py @@ -4,9 +4,9 @@ from __future__ import annotations -import inspect from datetime import datetime from functools import partial +import inspect import operator from typing import ( Callable, @@ -103,7 +103,7 @@ def evaluate(self, *args, **kwargs) -> Term: def _resolve_name(self): local_name = str(self.local_name) is_local = self.is_local - if 'column' in self.env.scope and self.env.scope['column'] == local_name: + if "column" in self.env.scope and self.env.scope["column"] == local_name: is_local = not is_local res = self.env.resolve(local_name, is_local=is_local) diff --git a/pandas/tests/computation/test_eval.py b/pandas/tests/computation/test_eval.py index 4bc41a411386c..7d677f197008b 100644 --- a/pandas/tests/computation/test_eval.py +++ b/pandas/tests/computation/test_eval.py @@ -1922,15 +1922,17 @@ def test_eval_no_support_column_name(request, column): tm.assert_frame_equal(result, expected) + def test_classname_frame_query(): # GH 48694 class A: a = 1 - df1 = DataFrame({'x': [1, 2, 3], 'y': [4, 5, 6]}) - df_test = DataFrame({'x': [1], 'y': [4]}) - tm.assert_frame_equal(df_test, df1.query(f'x=={A.a}')) - tm.assert_frame_equal(df_test, df1.query('x==@A.a')) + df1 = DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]}) + df_test = DataFrame({"x": [1], "y": [4]}) + tm.assert_frame_equal(df_test, df1.query(f"x=={A.a}")) + tm.assert_frame_equal(df_test, df1.query("x==@A.a")) + @td.skip_array_manager_not_yet_implemented def test_set_inplace(using_copy_on_write): From 52ff08da809033d2828c5451f986783dd387ca96 Mon Sep 17 00:00:00 2001 From: debnathshoham Date: Sat, 22 Oct 2022 18:05:10 +0530 Subject: [PATCH 10/15] looking for column in scope --- pandas/core/computation/ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/computation/ops.py b/pandas/core/computation/ops.py index 887f3b26d55a6..99c920292009a 100644 --- a/pandas/core/computation/ops.py +++ b/pandas/core/computation/ops.py @@ -6,7 +6,7 @@ from datetime import datetime from functools import partial -import inspect + import operator from typing import ( Callable, From f16c40410c11868dc204dafa6f98cd0ae3ee78b0 Mon Sep 17 00:00:00 2001 From: debnathshoham Date: Sat, 22 Oct 2022 20:26:49 +0530 Subject: [PATCH 11/15] fixed lint --- doc/source/whatsnew/v1.5.2.rst | 2 +- pandas/core/computation/ops.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v1.5.2.rst b/doc/source/whatsnew/v1.5.2.rst index 634d967f817b6..acb9f8e9b960f 100644 --- a/doc/source/whatsnew/v1.5.2.rst +++ b/doc/source/whatsnew/v1.5.2.rst @@ -13,7 +13,7 @@ including other versions of pandas. Fixed regressions ~~~~~~~~~~~~~~~~~ -- Fixed regression in :meth:`DataFrame.query` raising ``UndefinedVariableError`` when querying it with class as a name (:issue:`48694`) +- Fixed regression in :meth:`DataFrame.query` raising ``UndefinedVariableError`` when calling it with class as a name (:issue:`48694`) - .. --------------------------------------------------------------------------- diff --git a/pandas/core/computation/ops.py b/pandas/core/computation/ops.py index 99c920292009a..0a8876babe8c6 100644 --- a/pandas/core/computation/ops.py +++ b/pandas/core/computation/ops.py @@ -6,7 +6,6 @@ from datetime import datetime from functools import partial - import operator from typing import ( Callable, From 9881ca6a4097cf2597d2455f6654b58d3b5b660f Mon Sep 17 00:00:00 2001 From: Shoham Debnath Date: Sat, 22 Oct 2022 20:29:35 +0530 Subject: [PATCH 12/15] Update ops.py --- pandas/core/computation/ops.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/core/computation/ops.py b/pandas/core/computation/ops.py index 99c920292009a..0a8876babe8c6 100644 --- a/pandas/core/computation/ops.py +++ b/pandas/core/computation/ops.py @@ -6,7 +6,6 @@ from datetime import datetime from functools import partial - import operator from typing import ( Callable, From 6b07d7f116f41495c759dc88c8b330e90c6273fe Mon Sep 17 00:00:00 2001 From: debnathshoham Date: Sun, 30 Oct 2022 23:35:50 +0530 Subject: [PATCH 13/15] minor change --- pandas/core/computation/ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/computation/ops.py b/pandas/core/computation/ops.py index 0a8876babe8c6..132c7aeea8159 100644 --- a/pandas/core/computation/ops.py +++ b/pandas/core/computation/ops.py @@ -102,7 +102,7 @@ def evaluate(self, *args, **kwargs) -> Term: def _resolve_name(self): local_name = str(self.local_name) is_local = self.is_local - if "column" in self.env.scope and self.env.scope["column"] == local_name: + if self.env.scope.get("column") == local_name: is_local = not is_local res = self.env.resolve(local_name, is_local=is_local) From a674f49c8759e8de8959591baa6f7cf6a8ded7c4 Mon Sep 17 00:00:00 2001 From: debnathshoham Date: Mon, 31 Oct 2022 11:27:04 +0530 Subject: [PATCH 14/15] check if Series --- pandas/core/computation/ops.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandas/core/computation/ops.py b/pandas/core/computation/ops.py index 132c7aeea8159..96e3b1e8f20b1 100644 --- a/pandas/core/computation/ops.py +++ b/pandas/core/computation/ops.py @@ -29,6 +29,7 @@ result_type_many, ) from pandas.core.computation.scope import DEFAULT_GLOBALS +from pandas.core.series import Series from pandas.io.formats.printing import ( pprint_thing, @@ -102,7 +103,8 @@ def evaluate(self, *args, **kwargs) -> Term: def _resolve_name(self): local_name = str(self.local_name) is_local = self.is_local - if self.env.scope.get("column") == local_name: + check = self.env.scope.get("column") == local_name + if not isinstance(check, Series) and check: is_local = not is_local res = self.env.resolve(local_name, is_local=is_local) From 82617c381001a133a2ad561000ed904762783dd9 Mon Sep 17 00:00:00 2001 From: debnathshoham Date: Mon, 31 Oct 2022 15:07:16 +0530 Subject: [PATCH 15/15] mypy happy --- pandas/core/computation/pytables.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/pandas/core/computation/pytables.py b/pandas/core/computation/pytables.py index ea7b8b654efa1..592d71aea81b9 100644 --- a/pandas/core/computation/pytables.py +++ b/pandas/core/computation/pytables.py @@ -606,20 +606,21 @@ def __repr__(self) -> str: def evaluate(self): """create and return the numexpr condition and filter""" - try: - self.condition = self.terms.prune(ConditionBinOp) - except AttributeError as err: - raise ValueError( - f"cannot process expression [{self.expr}], [{self}] " - "is not a valid condition" - ) from err - try: - self.filter = self.terms.prune(FilterBinOp) - except AttributeError as err: - raise ValueError( - f"cannot process expression [{self.expr}], [{self}] " - "is not a valid filter" - ) from err + if self.terms is not None: + try: + self.condition = self.terms.prune(ConditionBinOp) + except AttributeError as err: + raise ValueError( + f"cannot process expression [{self.expr}], [{self}] " + "is not a valid condition" + ) from err + try: + self.filter = self.terms.prune(FilterBinOp) + except AttributeError as err: + raise ValueError( + f"cannot process expression [{self.expr}], [{self}] " + "is not a valid filter" + ) from err return self.condition, self.filter