SameerMesiah97 commented on code in PR #72081:
URL: https://github.com/apache/airflow/pull/72081#discussion_r4053507558


##########
providers/amazon/src/airflow/providers/amazon/aws/hooks/athena.py:
##########
@@ -344,3 +351,178 @@ def stop_query(self, query_execution_id: str) -> dict:
         """
         self.log.info("Stopping Query with executionId - %s", 
query_execution_id)
         return 
self.get_conn().stop_query_execution(QueryExecutionId=query_execution_id)
+
+    def _get_spark_calculation_status(self, response: dict[str, Any] | None) 
-> dict[str, Any]:
+        return (response or {}).get("Status") or {}
+
+    def start_spark_calculation(
+        self,
+        *,
+        session_id: str,
+        code_block: str,
+        description: str | None = None,
+        calculation_configuration: dict[str, Any] | None = None,
+        client_request_token: str | None = None,
+    ) -> str:
+        """
+        Start an Athena Spark calculation execution.
+
+        .. seealso::
+            - 
:external+boto3:py:meth:`Athena.Client.start_calculation_execution`
+
+        :param session_id: The Athena session ID.
+        :param code_block: Spark code to execute, typically notebook-like code.
+        :param description: Optional description of the calculation. Defaults 
to None.
+        :param calculation_configuration: Contains configuration information 
for the calculation. Defaults to None.
+        :param client_request_token: Optional idempotency token. Defaults to 
None.
+        :return: CalculationExecutionId
+        """
+        params: dict[str, Any] = {
+            "SessionId": session_id,
+            "CodeBlock": code_block,
+        }
+        if description:
+            params["Description"] = description
+
+        if calculation_configuration:
+            params["CalculationConfiguration"] = calculation_configuration
+
+        if client_request_token:
+            params["ClientRequestToken"] = client_request_token
+
+        if self.log_query:
+            self.log.info("Starting CalculationExecution with params:\n%s", 
query_params_to_string(params))
+
+        response = self.get_conn().start_calculation_execution(**params)
+        calculation_execution_id = response["CalculationExecutionId"]
+        self.log.info("Calculation execution id: %s", calculation_execution_id)
+        return calculation_execution_id
+
+    def get_spark_calculation_info(
+        self,
+        calculation_execution_id: str,
+        use_cache: bool = False,
+    ) -> dict[str, Any]:
+        """
+        Get information about a single Athena Spark calculation execution.
+
+        .. seealso::
+            - :external+boto3:py:meth:`Athena.Client.get_calculation_execution`
+
+        :param calculation_execution_id: CalculationExecutionId returned by 
start_spark_calculation.
+        :param use_cache: If True, use execution information cache. Defaults 
to False.
+        :return: Calculation execution response.
+        """
+        cache_key = f"calculation:{calculation_execution_id}"
+        if use_cache and cache_key in self.__query_results:
+            return self.__query_results[cache_key]
+
+        response = self.get_conn().get_calculation_execution(
+            CalculationExecutionId=calculation_execution_id,
+        )
+
+        if use_cache:
+            self.__query_results[cache_key] = response
+        return response
+
+    def check_spark_calculation_status(
+        self,
+        calculation_execution_id: str,
+        use_cache: bool = False,
+    ) -> str | None:
+        """
+        Fetch the state of a submitted Athena Spark calculation execution.
+
+        .. seealso::
+            - :external+boto3:py:meth:`Athena.Client.get_calculation_execution`
+
+        :param calculation_execution_id: CalculationExecutionId returned by 
start_spark_calculation.
+        :param use_cache: If True, use execution information cache. Defaults 
to False.
+        :return: One of valid calculation states, or *None* if the response is 
malformed.
+        """
+        response = self.get_spark_calculation_info(
+            calculation_execution_id=calculation_execution_id,
+            use_cache=use_cache,
+        )
+
+        status = self._get_spark_calculation_status(response)
+        state = status.get("State")
+
+        if state is None:
+            self.log.error("Could not parse status for calculation %s", 
calculation_execution_id)
+
+        return state
+
+    def get_spark_calculation_state_change_reason(
+        self,
+        calculation_execution_id: str,
+        use_cache: bool = False,
+    ) -> str | None:
+        """
+        Fetch the reason for an Athena Spark calculation state change, such as 
an error message.
+
+        .. seealso::
+            - :external+boto3:py:meth:`Athena.Client.get_calculation_execution`
+
+        :param calculation_execution_id: CalculationExecutionId returned by 
start_spark_calculation.
+        :param use_cache: If True, use execution information cache. Defaults 
to False.
+        :return: State change reason string, or None.
+        """
+        response = self.get_spark_calculation_info(
+            calculation_execution_id=calculation_execution_id,
+            use_cache=use_cache,
+        )
+        return 
self._get_spark_calculation_status(response).get("StateChangeReason")
+
+    def poll_spark_calculation_status(
+        self,
+        calculation_execution_id: str,
+        waiter_delay: int = 30,
+        waiter_max_attempts: int = 120,
+    ) -> str | None:
+        """
+        Poll an Athena Spark calculation until it reaches a terminal state.
+
+        :param calculation_execution_id: ID of the submitted calculation.
+        :param waiter_delay: Seconds to wait between status checks. Defaults 
to 30.
+        :param waiter_max_attempts: Maximum number of status checks. Defaults 
to 120.
+        :return: The latest calculation state, or ``None`` if the response is 
malformed.
+        """
+        try:
+            wait(
+                waiter=self.get_waiter("calculation_complete"),
+                waiter_delay=waiter_delay,
+                waiter_max_attempts=waiter_max_attempts,
+                args={"CalculationExecutionId": calculation_execution_id},
+                failure_message=(
+                    f"Error while waiting for calculation 
{calculation_execution_id} to complete"
+                ),
+                status_message=(
+                    f"Calculation execution ID {calculation_execution_id} is 
still in a non-terminal state"
+                ),
+                status_args=["Status.State"],
+            )
+        except Exception as error:

Review Comment:
   `WaiterTerminalFailure` is now being caughted with the latest state being 
returned via extraction from the `last_response` object. 
`WaiterMaxAttemptsError` will bubble up and get caught at the operator level. 
`AirflowException` in this case represents a generic error so I believe it 
should propagate without any explicit handling. 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to