# Path: awscfncli2/cli/utils/common.py
# def is_not_rate_limited_exception(e):
#     return not is_rate_limited_exception(e)
# 
# def is_rate_limited_exception(e):
#     if isinstance(e, (botocore.exceptions.ClientError, botocore.exceptions.WaiterError)):
#         return 'Rate exceeded' in str(e)
#     else:
#         return False
# 
# Path: awscfncli2/cli/utils/pprint.py
# def echo_pair(key, value=None, indent=0,
#               value_style=None, key_style=None,
#               sep=': '):
#     """Pretty print a key value pair
#     :param key: The key
#     :param value: The value
#     :param indent: Number of leading spaces
#     :param value_style: click.style parameters of value as a dict, default is none
#     :param key_style:  click.style parameters of value as a dict, default is bold text
#     :param sep: separator between key and value
#     """
#     assert key
#     key = ' ' * indent + key + sep
#     if key_style is None:
#         click.secho(key, bold=False, nl=False)
#     else:
#         click.secho(key, nl=False, **key_style)
# 
#     if value is None:
#         click.echo('')
#     else:
# 
#         if value_style is None:
#             click.echo(value)
#         else:
#             click.secho(value, **value_style)
# 
# Path: awscfncli2/runner/commands/command.py
# class Command(object):
#     SKIP_UPDATE_REFERENCES = False
# 
#     def __init__(self, pretty_printer, options):
#         assert isinstance(pretty_printer, StackPrettyPrinter)
#         self.ppt = pretty_printer
#         self.options = options
# 
#     def run(self, stack_context):
#         raise NotImplementedError
# 
# Path: awscfncli2/runner/commands/utils.py
# def update_termination_protection(session,
#                                   termination_protection,
#                                   stack_name,
#                                   ppt):
#     """Update termination protection on a stack"""
# 
#     if termination_protection is None:
#         # don't care, don't change
#         return
# 
#     client = session.client('cloudformation')
# 
#     if termination_protection:
#         ppt.secho('Enabling TerminationProtection')
#     else:
#         ppt.secho('Disabling TerminationProtection', fg='red')
# 
#     client.update_termination_protection(
#         StackName=stack_name,
#         EnableTerminationProtection=termination_protection)


import uuid
import backoff
import botocore.exceptions
from collections import namedtuple
from awscfncli2.cli.utils.common import is_not_rate_limited_exception, is_rate_limited_exception
from awscfncli2.cli.utils.pprint import echo_pair
from .command import Command
from .utils import update_termination_protection
            self.ppt.secho('ChangeSet execution started.')
        else:
            if is_new_stack:
                self.ppt.wait_until_deploy_complete(session, stack, self.options.disable_tail_events)
            else:
                self.ppt.wait_until_update_complete(session, stack, self.options.disable_tail_events)
            self.ppt.secho('ChangeSet execution complete.', fg='green')

    @backoff.on_exception(backoff.expo, botocore.exceptions.ClientError, max_tries=10,
                          giveup=is_not_rate_limited_exception)
    def create_change_set(self, client, parameters):
        return client.create_change_set(**parameters)

    @backoff.on_exception(backoff.expo, botocore.exceptions.ClientError, max_tries=10,
                          giveup=is_not_rate_limited_exception)
    def describe_change_set(self, client, changeset_name, parameters):
        return client.describe_change_set(
            ChangeSetName=changeset_name,
            StackName=parameters['StackName'],
        )

    @backoff.on_exception(backoff.expo, botocore.exceptions.ClientError, max_tries=10,
                          giveup=is_not_rate_limited_exception)
    def check_changeset_type(self, client, parameters):
        try:
            # check whether stack is already created.
            status = client.describe_stacks(StackName=parameters['StackName'])
            stack_status = status['Stacks'][0]['StackStatus']
        except botocore.exceptions.ClientError as e:


# Path: awscfncli2/cli/utils/common.py
# def is_not_rate_limited_exception(e):
#     return not is_rate_limited_exception(e)
# 
# def is_rate_limited_exception(e):
#     if isinstance(e, (botocore.exceptions.ClientError, botocore.exceptions.WaiterError)):
#         return 'Rate exceeded' in str(e)
#     else:
#         return False
# 
# Path: awscfncli2/cli/utils/pprint.py
# def echo_pair(key, value=None, indent=0,
#               value_style=None, key_style=None,
#               sep=': '):
#     """Pretty print a key value pair
#     :param key: The key
#     :param value: The value
#     :param indent: Number of leading spaces
#     :param value_style: click.style parameters of value as a dict, default is none
#     :param key_style:  click.style parameters of value as a dict, default is bold text
#     :param sep: separator between key and value
#     """
#     assert key
#     key = ' ' * indent + key + sep
#     if key_style is None:
#         click.secho(key, bold=False, nl=False)
#     else:
#         click.secho(key, nl=False, **key_style)
# 
#     if value is None:
#         click.echo('')
#     else:
# 
#         if value_style is None:
#             click.echo(value)
#         else:
#             click.secho(value, **value_style)
# 
# Path: awscfncli2/runner/commands/command.py
# class Command(object):
#     SKIP_UPDATE_REFERENCES = False
# 
#     def __init__(self, pretty_printer, options):
#         assert isinstance(pretty_printer, StackPrettyPrinter)
#         self.ppt = pretty_printer
#         self.options = options
# 
#     def run(self, stack_context):
#         raise NotImplementedError
# 
# Path: awscfncli2/runner/commands/utils.py
# def update_termination_protection(session,
#                                   termination_protection,
#                                   stack_name,
#                                   ppt):
#     """Update termination protection on a stack"""
# 
#     if termination_protection is None:
#         # don't care, don't change
#         return
# 
#     client = session.client('cloudformation')
# 
#     if termination_protection:
#         ppt.secho('Enabling TerminationProtection')
#     else:
#         ppt.secho('Disabling TerminationProtection', fg='red')
# 
#     client.update_termination_protection(
#         StackName=stack_name,
#         EnableTerminationProtection=termination_protection)


# Path: awscfncli2/runner/commands/stack_sync_command.py
import uuid
import backoff
import botocore.exceptions
from collections import namedtuple
from awscfncli2.cli.utils.common import is_not_rate_limited_exception, is_rate_limited_exception
from awscfncli2.cli.utils.pprint import echo_pair
from .command import Command
from .utils import update_termination_protection

            self.ppt.secho('ChangeSet execution started.')
        else:
            if is_new_stack:
                self.ppt.wait_until_deploy_complete(session, stack, self.options.disable_tail_events)
            else:
                self.ppt.wait_until_update_complete(session, stack, self.options.disable_tail_events)
            self.ppt.secho('ChangeSet execution complete.', fg='green')

    @backoff.on_exception(backoff.expo, botocore.exceptions.ClientError, max_tries=10,
                          giveup=is_not_rate_limited_exception)
    def create_change_set(self, client, parameters):
        return client.create_change_set(**parameters)

    @backoff.on_exception(backoff.expo, botocore.exceptions.ClientError, max_tries=10,
                          giveup=is_not_rate_limited_exception)
    def describe_change_set(self, client, changeset_name, parameters):
        return client.describe_change_set(
            ChangeSetName=changeset_name,
            StackName=parameters['StackName'],
        )

    @backoff.on_exception(backoff.expo, botocore.exceptions.ClientError, max_tries=10,
                          giveup=is_not_rate_limited_exception)
    def check_changeset_type(self, client, parameters):
        try:
            # check whether stack is already created.
            status = client.describe_stacks(StackName=parameters['StackName'])
            stack_status = status['Stacks'][0]['StackStatus']
        except botocore.exceptions.ClientError as e:


            if is_rate_limited_exception(e):