This is an automated email from the ASF dual-hosted git repository.

shahar1 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new dbb5c7ffff9 Fail Neo4jOperator argument mistakes at Dag parse time 
(#70508)
dbb5c7ffff9 is described below

commit dbb5c7ffff9cd5a83a5b24191dd5027b67957d7a
Author: Jyun-An Chen <[email protected]>
AuthorDate: Thu Jul 30 14:22:05 2026 +0800

    Fail Neo4jOperator argument mistakes at Dag parse time (#70508)
    
    Whether `cypher` or `sql` was passed is a property of how the Dag was
    written, not of the rendered value, and the constructor is the only place
    that can read it: under render_template_as_native_obj a supplied field can
    render to None, so the same check in execute reports an argument the author
    did provide as missing. Checking at construction also surfaces the mistake
    as a Dag import error instead of once per task instance.
    
    The execute guard stays: a passed field can still render to None and the
    hook needs a query string.
    
    related: #70503
---
 .../neo4j/src/airflow/providers/neo4j/operators/neo4j.py |  8 ++++++--
 providers/neo4j/tests/unit/neo4j/operators/test_neo4j.py | 16 +++++++++-------
 2 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/providers/neo4j/src/airflow/providers/neo4j/operators/neo4j.py 
b/providers/neo4j/src/airflow/providers/neo4j/operators/neo4j.py
index e0d1e1b1538..7bc55df65c6 100644
--- a/providers/neo4j/src/airflow/providers/neo4j/operators/neo4j.py
+++ b/providers/neo4j/src/airflow/providers/neo4j/operators/neo4j.py
@@ -57,6 +57,10 @@ class Neo4jOperator(BaseOperator):
         **kwargs,
     ) -> None:
         super().__init__(**kwargs)
+        if sql is not None and cypher is not None:
+            raise ValueError("Cannot provide both `sql` and `cypher`. Use 
`cypher` only.")
+        if cypher is None and sql is None:
+            raise ValueError("Parameter `cypher` is required.")
         self.neo4j_conn_id = neo4j_conn_id
         self.cypher = cypher
         self.sql = sql
@@ -70,10 +74,10 @@ class Neo4jOperator(BaseOperator):
                 AirflowProviderDeprecationWarning,
                 stacklevel=2,
             )
-            if cypher is not None:
-                raise ValueError("Cannot provide both `sql` and `cypher`. Use 
`cypher` only.")
             cypher = self.sql
         if cypher is None:
+            # The constructor only sees whether the argument was passed; a 
passed field can still
+            # render to None, and the hook needs a query string.
             raise ValueError("Parameter `cypher` is required.")
 
         self.log.info("Executing: %s", cypher)
diff --git a/providers/neo4j/tests/unit/neo4j/operators/test_neo4j.py 
b/providers/neo4j/tests/unit/neo4j/operators/test_neo4j.py
index 3399cdb6cce..bd1c6e77509 100644
--- a/providers/neo4j/tests/unit/neo4j/operators/test_neo4j.py
+++ b/providers/neo4j/tests/unit/neo4j/operators/test_neo4j.py
@@ -66,15 +66,17 @@ class TestNeo4jOperator:
             op.execute(mock.MagicMock())
         mock_hook.return_value.run.assert_called_once_with(cypher, None)
 
-    def test_neo4j_operator_both_sql_and_cypher_raises_on_execute(self):
-        op = Neo4jOperator(task_id="basic_neo4j", sql="a", cypher="b")
+    def test_neo4j_operator_both_sql_and_cypher_raises(self):
+        with pytest.raises(ValueError, match="Cannot provide both `sql` and 
`cypher`"):
+            Neo4jOperator(task_id="basic_neo4j", sql="a", cypher="b")
 
-        with pytest.warns(AirflowProviderDeprecationWarning):
-            with pytest.raises(ValueError, match="Cannot provide both `sql` 
and `cypher`"):
-                op.execute(mock.MagicMock())
+    def test_neo4j_operator_missing_cypher_raises(self):
+        with pytest.raises(ValueError, match="Parameter `cypher` is 
required."):
+            Neo4jOperator(task_id="basic_neo4j")
 
-    def test_neo4j_operator_missing_cypher_raises_on_execute(self):
-        op = Neo4jOperator(task_id="basic_neo4j")
+    def test_neo4j_operator_cypher_rendering_to_none_raises_on_execute(self):
+        op = Neo4jOperator(task_id="basic_neo4j", cypher="{{ missing }}")
+        op.cypher = None
 
         with pytest.raises(ValueError, match="Parameter `cypher` is 
required."):
             op.execute(mock.MagicMock())

Reply via email to