code: def wait_till_change_set_complete(cfn_client, change_set_id, try_count=25,
                                    sleep_time=.5, max_sleep=3):
      
      complete = False
      response = None
      for i in range(try_count):
          response = cfn_client.describe_change_set(
              ChangeSetName=change_set_id,
          )
          complete = response["Status"] in ("FAILED", "CREATE_COMPLETE")
          if complete:
              break
          if sleep_time == max_sleep:
              logger.debug(
                  "Still waiting on changeset for another %s seconds",
                  sleep_time
              )
          time.sleep(sleep_time)
  
          # exponential backoff with max
          sleep_time = min(sleep_time * 2, max_sleep)
      if not complete:
          raise exceptions.ChangesetDidNotStabilize(change_set_id)
      return response
docs: Checks state of a changeset, returning when it is in a complete state.
  
      Since changesets can take a little bit of time to get into a complete
      state, we need to poll it until it does so. This will try to get the
      state `try_count` times, waiting `sleep_time` * 2 seconds between each try
      up to the `max_sleep` number of seconds. If, after that time, the changeset
      is not in a complete state it fails. These default settings will wait a
      little over one minute.
  
      Args:
          cfn_client (:class:`botocore.client.CloudFormation`): Used to query
              cloudformation.
          change_set_id (str): The unique changeset id to wait for.
          try_count (int): Number of times to try the call.
          sleep_time (int): Time to sleep between attempts.
          max_sleep (int): Max time to sleep during backoff
  
      Return:
          dict: The response from cloudformation for the describe_change_set
              call.