class_docstr: Interact with Amazon EMR Containers (Amazon EMR on EKS).
  
  Provide thick wrapper around :py:class:`boto3.client("emr-containers") <EMRContainers.Client>`.
  
  :param virtual_cluster_id: Cluster ID of the EMR on EKS virtual cluster
  
  Additional arguments (such as ``aws_conn_id``) may be specified and
  are passed down to the underlying AwsBaseHook.
  
  .. seealso::
      - :class:`airflow.providers.amazon.aws.hooks.base_aws.AwsBaseHook`
class_docstr_tok: ["Interact","with","Amazon","EMR","Containers","(","Amazon","EMR","on","EKS",")",".","Provide","thick","wrapper","around",":","py",":","class",":","`","boto3.client","(","``","emr-containers","''",")","<","EMRContainers.Client",">","`",".",":","param","virtual_cluster_id",":","Cluster","ID","of","the","EMR","on","EKS","virtual","cluster","Additional","arguments","(","such","as","``","aws_conn_id","``",")","may","be","specified","and","are","passed","down","to","the","underlying","AwsBaseHook",".","..","seealso",":",":","-",":","class",":","`","airflow.providers.amazon.aws.hooks.base_aws.AwsBaseHook","`"]
docstr: Poll the status of submitted job run until query state reaches final state; returns the final state.
  
  :param job_id: The ID of the job run request.
  :param poll_interval: Time (in seconds) to wait between calls to check query status on EMR
  :param max_polling_attempts: Number of times to poll for query state before function exits
docstr_tok: ["Poll","the","status","of","submitted","job","run","until","query","state","reaches","final","state",";","returns","the","final","state",".",":","param","job_id",":","The","ID","of","the","job","run","request",".",":","param","poll_interval",":","Time","(","in","seconds",")","to","wait","between","calls","to","check","query","status","on","EMR",":","param","max_polling_attempts",":","Number","of","times","to","poll","for","query","state","before","function","exits"]
code: def poll_query_status(
      self,
      job_id: str,
      poll_interval: int = 30,
      max_polling_attempts: int | None = None,
  ) -> str | None:
      """
      Poll the status of submitted job run until query state reaches final state; returns the final state.
  
      :param job_id: The ID of the job run request.
      :param poll_interval: Time (in seconds) to wait between calls to check query status on EMR
      :param max_polling_attempts: Number of times to poll for query state before function exits
      """
      try_number = 1
      while True:
          query_state = self.check_query_status(job_id)
          if query_state in self.TERMINAL_STATES:
              self.log.info(
                  "Try %s: Query execution completed. Final state is %s",
                  try_number,
                  query_state,
              )
              return query_state
          if query_state is None:
              self.log.info(
                  "Try %s: Invalid query state. Retrying again",
                  try_number,
              )
          else:
              self.log.info(
                  "Try %s: Query is still in non-terminal state - %s",
                  try_number,
                  query_state,
              )
          if (
              max_polling_attempts
              and try_number >= max_polling_attempts
          ):  # Break loop if max_polling_attempts reached
              return query_state
          try_number += 1
          time.sleep(poll_interval)
  
imports: from __future__ import annotations
  import json
  import time
  import warnings
  from typing import Any
  from botocore.exceptions import ClientError
  from airflow.exceptions import AirflowException, AirflowNotFoundException
  from airflow.providers.amazon.aws.hooks.base_aws import AwsBaseHook
  from airflow.providers.amazon.aws.utils.waiter_with_logging import wait
label_desc: Use image node_id 6 for calling the EmrContainerHook obj's underlying member method code with example usage: obj.poll_query_status(job_id, poll_interval, max_polling_attempts) and returns: query_state, query_state