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

ferruzzi 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 f511c0d1246 Add a document_classifier_deletable waiter with unit tests 
(#74125)
f511c0d1246 is described below

commit f511c0d124650cda5179c1e798d8afb70f553137
Author: D. Ferruzzi <[email protected]>
AuthorDate: Tue Oct 13 10:04:40 2026 -0700

    Add a document_classifier_deletable waiter with unit tests (#74125)
    
    * Add a document_classifier_deletable waiter with unit tests, and implement 
it in the system test
---
 .../providers/amazon/aws/waiters/comprehend.json   | 55 ++++++++++++++++++++++
 .../aws/example_comprehend_document_classifier.py  | 21 +++++++--
 .../unit/amazon/aws/waiters/test_comprehend.py     | 54 +++++++++++++++++++++
 3 files changed, 127 insertions(+), 3 deletions(-)

diff --git 
a/providers/amazon/src/airflow/providers/amazon/aws/waiters/comprehend.json 
b/providers/amazon/src/airflow/providers/amazon/aws/waiters/comprehend.json
index 0d2d7259598..020cf189369 100644
--- a/providers/amazon/src/airflow/providers/amazon/aws/waiters/comprehend.json
+++ b/providers/amazon/src/airflow/providers/amazon/aws/waiters/comprehend.json
@@ -99,6 +99,61 @@
                     "state": "success"
                 }
             ]
+        },
+        "document_classifier_deletable": {
+            "delay": 10,
+            "maxAttempts": 30,
+            "operation": "DescribeDocumentClassifier",
+            "acceptors": [
+                {
+                    "matcher": "path",
+                    "argument": "DocumentClassifierProperties.Status",
+                    "expected": "DELETING",
+                    "state": "failure"
+                },
+                {
+                    "matcher": "path",
+                    "argument": "DocumentClassifierProperties.Status",
+                    "expected": "SUBMITTED",
+                    "state": "retry"
+                },
+                {
+                    "matcher": "path",
+                    "argument": "DocumentClassifierProperties.Status",
+                    "expected": "TRAINING",
+                    "state": "retry"
+                },
+                {
+                    "matcher": "path",
+                    "argument": "DocumentClassifierProperties.Status",
+                    "expected": "STOP_REQUESTED",
+                    "state": "retry"
+                },
+                {
+                    "matcher": "path",
+                    "argument": "DocumentClassifierProperties.Status",
+                    "expected": "TRAINED",
+                    "state": "success"
+                },
+                {
+                    "matcher": "path",
+                    "argument": "DocumentClassifierProperties.Status",
+                    "expected": "TRAINED_WITH_WARNING",
+                    "state": "success"
+                },
+                {
+                    "matcher": "path",
+                    "argument": "DocumentClassifierProperties.Status",
+                    "expected": "IN_ERROR",
+                    "state": "success"
+                },
+                {
+                    "matcher": "path",
+                    "argument": "DocumentClassifierProperties.Status",
+                    "expected": "STOPPED",
+                    "state": "success"
+                }
+            ]
         }
     }
 }
diff --git 
a/providers/amazon/tests/system/amazon/aws/example_comprehend_document_classifier.py
 
b/providers/amazon/tests/system/amazon/aws/example_comprehend_document_classifier.py
index 7b32eae9615..d15796165af 100644
--- 
a/providers/amazon/tests/system/amazon/aws/example_comprehend_document_classifier.py
+++ 
b/providers/amazon/tests/system/amazon/aws/example_comprehend_document_classifier.py
@@ -114,13 +114,28 @@ def document_classifier_workflow():
     # [END howto_sensor_create_document_classifier]
 
     @task(trigger_rule=TriggerRule.ALL_DONE)
-    def delete_classifier(document_classifier_arn: str):
-        
ComprehendHook().conn.delete_document_classifier(DocumentClassifierArn=document_classifier_arn)
+    def delete_classifier(classifier_name: str):
+        hook = ComprehendHook()
+        classifiers = hook.conn.list_document_classifiers(
+            Filter={"DocumentClassifierName": classifier_name},
+        )["DocumentClassifierPropertiesList"]
+
+        for classifier in classifiers:
+            classifier_arn = classifier["DocumentClassifierArn"]
+
+            # A classifier can only be deleted from a terminal state.
+            if classifier["Status"] == "DELETING":
+                continue
+            if classifier["Status"] in ("SUBMITTED", "TRAINING"):
+                
hook.conn.stop_training_document_classifier(DocumentClassifierArn=classifier_arn)
+
+            
hook.get_waiter("document_classifier_deletable").wait(DocumentClassifierArn=classifier_arn)
+            
hook.conn.delete_document_classifier(DocumentClassifierArn=classifier_arn)
 
     chain(
         create_document_classifier,
         await_create_document_classifier,
-        delete_classifier(create_document_classifier.output),
+        delete_classifier(classifier_name),
     )
 
 
diff --git a/providers/amazon/tests/unit/amazon/aws/waiters/test_comprehend.py 
b/providers/amazon/tests/unit/amazon/aws/waiters/test_comprehend.py
index 650632a366c..05faecfc9b3 100644
--- a/providers/amazon/tests/unit/amazon/aws/waiters/test_comprehend.py
+++ b/providers/amazon/tests/unit/amazon/aws/waiters/test_comprehend.py
@@ -33,6 +33,7 @@ class TestComprehendCustomWaiters:
     def test_service_waiters(self):
         assert "pii_entities_detection_job_complete" in 
ComprehendHook().list_waiters()
         assert "create_document_classifier_complete" in 
ComprehendHook().list_waiters()
+        assert "document_classifier_deletable" in 
ComprehendHook().list_waiters()
 
 
 class TestComprehendCustomWaitersBase:
@@ -102,3 +103,56 @@ class 
TestComprehendDocumentClassifierCompleteWaiter(TestComprehendCustomWaiters
         ComprehendHook().get_waiter(self.WAITER_NAME).wait(
             DocumentClassifierArn="arn", WaiterConfig={"Delay": 0.01, 
"MaxAttempts": 3}
         )
+
+
+class 
TestComprehendDocumentClassifierDeletableWaiter(TestComprehendCustomWaitersBase):
+    WAITER_NAME = "document_classifier_deletable"
+
+    @pytest.fixture
+    def mock_describe_document_classifier(self):
+        with mock.patch.object(self.client, "describe_document_classifier") as 
mock_getter:
+            yield mock_getter
+
+    @pytest.mark.parametrize("state", ["TRAINED", "TRAINED_WITH_WARNING", 
"IN_ERROR", "STOPPED"])
+    def test_document_classifier_deletable(self, state, 
mock_describe_document_classifier):
+        mock_describe_document_classifier.return_value = 
{"DocumentClassifierProperties": {"Status": state}}
+
+        
ComprehendHook().get_waiter(self.WAITER_NAME).wait(DocumentClassifierArn="arn")
+
+        
mock_describe_document_classifier.assert_called_once_with(DocumentClassifierArn="arn")
+
+    @pytest.mark.parametrize("state", ["SUBMITTED", "TRAINING", 
"STOP_REQUESTED"])
+    def test_document_classifier_deletable_retries_while_in_progress(
+        self, state, mock_describe_document_classifier
+    ):
+        mock_describe_document_classifier.return_value = 
{"DocumentClassifierProperties": {"Status": state}}
+
+        with pytest.raises(botocore.exceptions.WaiterError):
+            ComprehendHook().get_waiter(self.WAITER_NAME).wait(
+                DocumentClassifierArn="arn", WaiterConfig={"Delay": 0.01, 
"MaxAttempts": 2}
+            )
+
+        assert mock_describe_document_classifier.call_count == 2
+
+    def test_document_classifier_deletable_fails_while_already_deleting(
+        self, mock_describe_document_classifier
+    ):
+        mock_describe_document_classifier.return_value = {
+            "DocumentClassifierProperties": {"Status": "DELETING"}
+        }
+
+        with pytest.raises(botocore.exceptions.WaiterError):
+            ComprehendHook().get_waiter(self.WAITER_NAME).wait(
+                DocumentClassifierArn="arn", WaiterConfig={"Delay": 0.01, 
"MaxAttempts": 2}
+            )
+
+        
mock_describe_document_classifier.assert_called_once_with(DocumentClassifierArn="arn")
+
+    def test_document_classifier_deletable_wait(self, 
mock_describe_document_classifier):
+        wait = {"DocumentClassifierProperties": {"Status": "STOP_REQUESTED"}}
+        success = {"DocumentClassifierProperties": {"Status": "STOPPED"}}
+        mock_describe_document_classifier.side_effect = [wait, wait, success]
+
+        ComprehendHook().get_waiter(self.WAITER_NAME).wait(
+            DocumentClassifierArn="arn", WaiterConfig={"Delay": 0.01, 
"MaxAttempts": 3}
+        )

Reply via email to