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

eladkal 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 f87240f7031 Handling `NotFound` when file has already been deleted 
from GCS (#72275)
f87240f7031 is described below

commit f87240f703181c3807d2cddc7f39cedbe3712a3a
Author: Jake McGrath <[email protected]>
AuthorDate: Sat Sep 12 02:56:42 2026 -0400

    Handling `NotFound` when file has already been deleted from GCS (#72275)
    
    * fix/issue-72264: Handling NotFound when file has already been deleted
    
    * fix/issue-72264: Fixing failing tests for Python 3.10
    
    * Apply batched suggestions from code review
    
    Co-authored-by: Ryan Hatter <[email protected]>
    
    * fix/issue-72264: Fixing variable mis-match
    
    ---------
    
    Co-authored-by: Ryan Hatter <[email protected]>
---
 .../providers/google/cloud/transfers/gcs_to_gcs.py | 13 +++++++--
 .../unit/google/cloud/transfers/test_gcs_to_gcs.py | 34 ++++++++++++++++++++++
 2 files changed, 45 insertions(+), 2 deletions(-)

diff --git 
a/providers/google/src/airflow/providers/google/cloud/transfers/gcs_to_gcs.py 
b/providers/google/src/airflow/providers/google/cloud/transfers/gcs_to_gcs.py
index b6f7e4f3729..8d10763754e 100644
--- 
a/providers/google/src/airflow/providers/google/cloud/transfers/gcs_to_gcs.py
+++ 
b/providers/google/src/airflow/providers/google/cloud/transfers/gcs_to_gcs.py
@@ -23,6 +23,8 @@ import warnings
 from collections.abc import Sequence
 from typing import TYPE_CHECKING
 
+from google.api_core.exceptions import NotFound
+
 from airflow.exceptions import AirflowProviderDeprecationWarning
 from airflow.providers.common.compat.sdk import AirflowException
 from airflow.providers.google.cloud.hooks.gcs import GCSHook
@@ -607,8 +609,15 @@ class GCSToGCSOperator(BaseOperator):
             **rewrite_kwargs,
         )
 
-        if self.move_object:
-            hook.delete(self.source_bucket, source_object)
+        try:
+            if self.move_object:
+                hook.delete(self.source_bucket, source_object)
+
+        # Handle case where the file has already been deleted and a NotFound 
exception is raised
+        except NotFound:
+            self.log.warning(
+                "Object %s does not exist in the source bucket %s", 
source_object, self.source_bucket
+            )
 
         return f"gs://{dest_bucket}/{destination_object}"
 
diff --git 
a/providers/google/tests/unit/google/cloud/transfers/test_gcs_to_gcs.py 
b/providers/google/tests/unit/google/cloud/transfers/test_gcs_to_gcs.py
index bfd2c8f586f..73f185adead 100644
--- a/providers/google/tests/unit/google/cloud/transfers/test_gcs_to_gcs.py
+++ b/providers/google/tests/unit/google/cloud/transfers/test_gcs_to_gcs.py
@@ -21,6 +21,7 @@ from datetime import datetime
 from unittest import mock
 
 import pytest
+from google.api_core.exceptions import NotFound
 
 from airflow.exceptions import AirflowProviderDeprecationWarning
 from airflow.providers.common.compat.openlineage.facet import Dataset
@@ -547,6 +548,39 @@ class TestGoogleCloudStorageToCloudStorageOperator:
             any_order=True,
         )
 
+    @mock.patch("airflow.providers.google.cloud.transfers.gcs_to_gcs.GCSHook")
+    def test_executes_with_source_objects_not_found(self, mock_hook, caplog):
+        """Handle a file NotFound exception when attempting to delete source 
object."""
+        operator = GCSToGCSOperator(
+            task_id=TASK_ID,
+            source_bucket=TEST_BUCKET,
+            source_objects=SOURCE_OBJECTS_SINGLE_FILE,
+            move_object=True,
+        )
+
+        mock_hook.return_value.delete.side_effect = NotFound("Object not 
found")
+
+        operator.execute(None)
+
+        expected_object = SOURCE_OBJECTS_SINGLE_FILE[0]
+        expected_message = f"Object {expected_object} does not exist in the 
source bucket {TEST_BUCKET}"
+        assert any(expected_message in record.getMessage() for record in 
caplog.records)
+
+    @mock.patch("airflow.providers.google.cloud.transfers.gcs_to_gcs.GCSHook")
+    def test_executes_with_source_objects_failed_delete(self, mock_hook):
+        """Handle a non-NotFound exception when attempting to delete a 
non-existent object."""
+        operator = GCSToGCSOperator(
+            task_id=TASK_ID,
+            source_bucket=TEST_BUCKET,
+            source_objects=SOURCE_OBJECTS_SINGLE_FILE,
+            move_object=True,
+        )
+
+        mock_hook.return_value.delete.side_effect = RuntimeError
+
+        with pytest.raises(RuntimeError):
+            operator.execute(None)
+
     @mock.patch("airflow.providers.google.cloud.transfers.gcs_to_gcs.GCSHook")
     def test_executes_with_a_delimiter(self, mock_hook):
         operator = GCSToGCSOperator(

Reply via email to