From 0e817b7bde0ebf1364de002732d91f61ff1bce99 Mon Sep 17 00:00:00 2001 From: Alex Hall Date: Tue, 3 Mar 2020 13:30:09 +0200 Subject: [PATCH 1/6] Properly handle missing attributes in query/eval strings --- pandas/core/computation/expr.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/core/computation/expr.py b/pandas/core/computation/expr.py index c59952bea8dc0..f821a3fc11a64 100644 --- a/pandas/core/computation/expr.py +++ b/pandas/core/computation/expr.py @@ -635,8 +635,9 @@ def visit_Attribute(self, node, **kwargs): # something like datetime.datetime where scope is overridden if isinstance(value, ast.Name) and value.id == attr: return resolved + raise - raise ValueError(f"Invalid Attribute context {ctx.__name__}") + raise ValueError(f"Invalid Attribute context {ctx.__class__.__name__}") def visit_Call(self, node, side=None, **kwargs): From f392c60f8a83c26d2cb3e4d854b9a3ba3b44511d Mon Sep 17 00:00:00 2001 From: Alex Hall Date: Thu, 5 Mar 2020 13:54:34 +0200 Subject: [PATCH 2/6] Use type(ctx) instead of ctx.__class__ --- pandas/core/computation/expr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/computation/expr.py b/pandas/core/computation/expr.py index f821a3fc11a64..6cd9a15b70d39 100644 --- a/pandas/core/computation/expr.py +++ b/pandas/core/computation/expr.py @@ -637,7 +637,7 @@ def visit_Attribute(self, node, **kwargs): return resolved raise - raise ValueError(f"Invalid Attribute context {ctx.__class__.__name__}") + raise ValueError(f"Invalid Attribute context {type(ctx).__name__}") def visit_Call(self, node, side=None, **kwargs): From 8f1ae2bbbc88a321707d5d0335217dd6a5bc009c Mon Sep 17 00:00:00 2001 From: Alex Hall Date: Tue, 3 Mar 2020 13:30:09 +0200 Subject: [PATCH 3/6] Properly handle missing attributes in query/eval strings --- pandas/core/computation/expr.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/core/computation/expr.py b/pandas/core/computation/expr.py index c59952bea8dc0..f821a3fc11a64 100644 --- a/pandas/core/computation/expr.py +++ b/pandas/core/computation/expr.py @@ -635,8 +635,9 @@ def visit_Attribute(self, node, **kwargs): # something like datetime.datetime where scope is overridden if isinstance(value, ast.Name) and value.id == attr: return resolved + raise - raise ValueError(f"Invalid Attribute context {ctx.__name__}") + raise ValueError(f"Invalid Attribute context {ctx.__class__.__name__}") def visit_Call(self, node, side=None, **kwargs): From ef046fb1747c17c9654fcd823f7ce62cb73102b9 Mon Sep 17 00:00:00 2001 From: Alex Hall Date: Thu, 5 Mar 2020 13:54:34 +0200 Subject: [PATCH 4/6] Use type(ctx) instead of ctx.__class__ --- pandas/core/computation/expr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/computation/expr.py b/pandas/core/computation/expr.py index f821a3fc11a64..6cd9a15b70d39 100644 --- a/pandas/core/computation/expr.py +++ b/pandas/core/computation/expr.py @@ -637,7 +637,7 @@ def visit_Attribute(self, node, **kwargs): return resolved raise - raise ValueError(f"Invalid Attribute context {ctx.__class__.__name__}") + raise ValueError(f"Invalid Attribute context {type(ctx).__name__}") def visit_Call(self, node, side=None, **kwargs): From 9e0ba6c745f3998a80848ae1c71ea091574e7138 Mon Sep 17 00:00:00 2001 From: Alex Hall Date: Sun, 15 Mar 2020 10:19:49 +0200 Subject: [PATCH 5/6] Add test_missing_attribute --- pandas/tests/frame/test_query_eval.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pandas/tests/frame/test_query_eval.py b/pandas/tests/frame/test_query_eval.py index bf9eeb532b43b..1a07780462ea3 100644 --- a/pandas/tests/frame/test_query_eval.py +++ b/pandas/tests/frame/test_query_eval.py @@ -1165,6 +1165,11 @@ def test_lots_of_operators_string(self, df): expect = df[df[" &^ :!€$?(} > <++*'' "] > 4] tm.assert_frame_equal(res, expect) + def test_missing_attribute(self, df): + message = "module 'pandas' has no attribute 'thing'" + with pytest.raises(AttributeError, match=message): + df.eval("@pd.thing") + def test_failing_quote(self, df): with pytest.raises(SyntaxError): df.query("`it's` > `that's`") From 49ac9b602e4bd885c92867d08a01af58c5a3bdfd Mon Sep 17 00:00:00 2001 From: Alex Hall Date: Tue, 17 Mar 2020 08:43:16 +0200 Subject: [PATCH 6/6] Add whatsnew note about missing attributes --- doc/source/whatsnew/v1.1.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index a667d0663aa31..aaa8a0a56b49d 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -401,6 +401,7 @@ Other - Bug in :meth:`DataFrame.to_records` incorrectly losing timezone information in timezone-aware ``datetime64`` columns (:issue:`32535`) - Fixed :func:`pandas.testing.assert_series_equal` to correctly raise if left object is a different subclass with ``check_series_type=True`` (:issue:`32670`). - :meth:`IntegerArray.astype` now supports ``datetime64`` dtype (:issue:32538`) +- Getting a missing attribute in a query/eval string raises the correct ``AttributeError`` (:issue:`32408`) .. ---------------------------------------------------------------------------