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 82a2c91d08a Validate Databricks Repos operators' template fields after 
rendering (#70341)
82a2c91d08a is described below

commit 82a2c91d08a5d7e686cf453552575db774f6b999
Author: Stefan Wang <[email protected]>
AuthorDate: Fri Jul 24 13:11:52 2026 -0700

    Validate Databricks Repos operators' template fields after rendering 
(#70341)
    
    Validate Databricks Repos operators' template fields after rendering 
branch, tag and repo_path are template fields, rendered after __init__ runs. 
The Create, Update and Delete Repos operators enforced their mutual-exclusivity 
and
    presence combinations in the constructor, acting on the un-rendered Jinja
    expressions. Move those checks into execute(), preserving order; 
git_url/git_provider
    detection reads no template field and stays in __init__.
    
    related: #70296
---
 .../databricks/operators/databricks_repos.py       | 28 ++++++++++-----------
 .../databricks/operators/test_databricks_repos.py  | 29 ++++++++++++----------
 .../ci/prek/validate_operators_init_exemptions.txt |  3 ---
 3 files changed, 30 insertions(+), 30 deletions(-)

diff --git 
a/providers/databricks/src/airflow/providers/databricks/operators/databricks_repos.py
 
b/providers/databricks/src/airflow/providers/databricks/operators/databricks_repos.py
index cb513ea8873..5614fd403dd 100644
--- 
a/providers/databricks/src/airflow/providers/databricks/operators/databricks_repos.py
+++ 
b/providers/databricks/src/airflow/providers/databricks/operators/databricks_repos.py
@@ -97,8 +97,6 @@ class DatabricksReposCreateOperator(BaseOperator):
         else:
             self.git_provider = git_provider
         self.repo_path = repo_path
-        if branch is not None and tag is not None:
-            raise AirflowException("Only one of branch or tag should be 
provided, but not both")
         self.branch = branch
         self.tag = tag
 
@@ -131,6 +129,8 @@ class DatabricksReposCreateOperator(BaseOperator):
         :param context: context
         :return: Repo ID
         """
+        if self.branch is not None and self.tag is not None:
+            raise AirflowException("Only one of branch or tag should be 
provided, but not both")
         payload = {
             "url": self.git_url,
             "provider": self.git_provider,
@@ -225,14 +225,6 @@ class DatabricksReposUpdateOperator(BaseOperator):
         self.databricks_conn_id = databricks_conn_id
         self.databricks_retry_limit = databricks_retry_limit
         self.databricks_retry_delay = databricks_retry_delay
-        if branch is not None and tag is not None:
-            raise AirflowException("Only one of branch or tag should be 
provided, but not both")
-        if branch is None and tag is None:
-            raise AirflowException("One of branch or tag should be provided")
-        if repo_id is not None and repo_path is not None:
-            raise AirflowException("Only one of repo_id or repo_path should be 
provided, but not both")
-        if repo_id is None and repo_path is None:
-            raise AirflowException("One of repo_id or repo_path should be 
provided")
         self.repo_path = repo_path
         self.repo_id = repo_id
         self.branch = branch
@@ -248,6 +240,14 @@ class DatabricksReposUpdateOperator(BaseOperator):
         )
 
     def execute(self, context: Context):
+        if self.branch is not None and self.tag is not None:
+            raise AirflowException("Only one of branch or tag should be 
provided, but not both")
+        if self.branch is None and self.tag is None:
+            raise AirflowException("One of branch or tag should be provided")
+        if self.repo_id is not None and self.repo_path is not None:
+            raise AirflowException("Only one of repo_id or repo_path should be 
provided, but not both")
+        if self.repo_id is None and self.repo_path is None:
+            raise AirflowException("One of repo_id or repo_path should be 
provided")
         if self.repo_path is not None:
             self.repo_id = self._hook.get_repo_by_path(self.repo_path)
             if self.repo_id is None:
@@ -297,10 +297,6 @@ class DatabricksReposDeleteOperator(BaseOperator):
         self.databricks_conn_id = databricks_conn_id
         self.databricks_retry_limit = databricks_retry_limit
         self.databricks_retry_delay = databricks_retry_delay
-        if repo_id is not None and repo_path is not None:
-            raise AirflowException("Only one of repo_id or repo_path should be 
provided, but not both")
-        if repo_id is None and repo_path is None:
-            raise AirflowException("One of repo_id repo_path tag should be 
provided")
         self.repo_path = repo_path
         self.repo_id = repo_id
 
@@ -314,6 +310,10 @@ class DatabricksReposDeleteOperator(BaseOperator):
         )
 
     def execute(self, context: Context):
+        if self.repo_id is not None and self.repo_path is not None:
+            raise AirflowException("Only one of repo_id or repo_path should be 
provided, but not both")
+        if self.repo_id is None and self.repo_path is None:
+            raise AirflowException("One of repo_id repo_path tag should be 
provided")
         if self.repo_path is not None:
             self.repo_id = self._hook.get_repo_by_path(self.repo_path)
             if self.repo_id is None:
diff --git 
a/providers/databricks/tests/unit/databricks/operators/test_databricks_repos.py 
b/providers/databricks/tests/unit/databricks/operators/test_databricks_repos.py
index 398050cf1ff..19d0adccf55 100644
--- 
a/providers/databricks/tests/unit/databricks/operators/test_databricks_repos.py
+++ 
b/providers/databricks/tests/unit/databricks/operators/test_databricks_repos.py
@@ -77,24 +77,26 @@ class TestDatabricksReposUpdateOperator:
         db_mock.update_repo.assert_called_once_with("123", {"tag": "v1.0.0"})
 
     def test_init_exception(self):
-        """
-        Tests handling of incorrect parameters passed to ``__init__``
-        """
+        """Incorrect parameter combinations are rejected at execute, after 
rendering."""
+        op = DatabricksReposUpdateOperator(task_id=TASK_ID, repo_id="abc", 
repo_path="path", branch="abc")
         with pytest.raises(
             AirflowException, match="Only one of repo_id or repo_path should 
be provided, but not both"
         ):
-            DatabricksReposUpdateOperator(task_id=TASK_ID, repo_id="abc", 
repo_path="path", branch="abc")
+            op.execute(None)
 
+        op = DatabricksReposUpdateOperator(task_id=TASK_ID, branch="abc")
         with pytest.raises(AirflowException, match="One of repo_id or 
repo_path should be provided"):
-            DatabricksReposUpdateOperator(task_id=TASK_ID, branch="abc")
+            op.execute(None)
 
+        op = DatabricksReposUpdateOperator(task_id=TASK_ID, repo_id="123", 
branch="123", tag="123")
         with pytest.raises(
             AirflowException, match="Only one of branch or tag should be 
provided, but not both"
         ):
-            DatabricksReposUpdateOperator(task_id=TASK_ID, repo_id="123", 
branch="123", tag="123")
+            op.execute(None)
 
+        op = DatabricksReposUpdateOperator(task_id=TASK_ID, repo_id="123")
         with pytest.raises(AirflowException, match="One of branch or tag 
should be provided"):
-            DatabricksReposUpdateOperator(task_id=TASK_ID, repo_id="123")
+            op.execute(None)
 
 
 class TestDatabricksReposDeleteOperator:
@@ -140,16 +142,16 @@ class TestDatabricksReposDeleteOperator:
         db_mock.delete_repo.assert_called_once_with("123")
 
     def test_init_exception(self):
-        """
-        Tests handling of incorrect parameters passed to ``__init__``
-        """
+        """Incorrect parameter combinations are rejected at execute, after 
rendering."""
+        op = DatabricksReposDeleteOperator(task_id=TASK_ID, repo_id="abc", 
repo_path="path")
         with pytest.raises(
             AirflowException, match="Only one of repo_id or repo_path should 
be provided, but not both"
         ):
-            DatabricksReposDeleteOperator(task_id=TASK_ID, repo_id="abc", 
repo_path="path")
+            op.execute(None)
 
+        op = DatabricksReposDeleteOperator(task_id=TASK_ID)
         with pytest.raises(AirflowException, match="One of repo_id repo_path 
tag should be provided"):
-            DatabricksReposDeleteOperator(task_id=TASK_ID)
+            op.execute(None)
 
 
 class TestDatabricksReposCreateOperator:
@@ -286,7 +288,8 @@ class TestDatabricksReposCreateOperator:
         with pytest.raises(AirflowException, match=exception_message):
             op.execute(None)
 
+        op = DatabricksReposCreateOperator(task_id=TASK_ID, git_url=git_url, 
branch="123", tag="123")
         with pytest.raises(
             AirflowException, match="Only one of branch or tag should be 
provided, but not both"
         ):
-            DatabricksReposCreateOperator(task_id=TASK_ID, git_url=git_url, 
branch="123", tag="123")
+            op.execute(None)
diff --git a/scripts/ci/prek/validate_operators_init_exemptions.txt 
b/scripts/ci/prek/validate_operators_init_exemptions.txt
index bfb3791cc10..ec735e38dde 100644
--- a/scripts/ci/prek/validate_operators_init_exemptions.txt
+++ b/scripts/ci/prek/validate_operators_init_exemptions.txt
@@ -23,9 +23,6 @@ 
providers/amazon/src/airflow/providers/amazon/aws/transfers/s3_to_redshift.py::S
 
providers/anthropic/src/airflow/providers/anthropic/operators/agent.py::AnthropicAgentSessionOperator
 
providers/apache/kafka/src/airflow/providers/apache/kafka/operators/produce.py::ProduceToTopicOperator
 
providers/cncf/kubernetes/src/airflow/providers/cncf/kubernetes/operators/pod.py::KubernetesPodOperator
-providers/databricks/src/airflow/providers/databricks/operators/databricks_repos.py::DatabricksReposCreateOperator
-providers/databricks/src/airflow/providers/databricks/operators/databricks_repos.py::DatabricksReposDeleteOperator
-providers/databricks/src/airflow/providers/databricks/operators/databricks_repos.py::DatabricksReposUpdateOperator
 
providers/docker/src/airflow/providers/docker/operators/docker.py::DockerOperator
 
providers/google/src/airflow/providers/google/cloud/operators/bigquery.py::BigQueryInsertJobOperator
 
providers/google/src/airflow/providers/google/cloud/operators/cloud_batch.py::CloudBatchSubmitJobOperator

Reply via email to