# Buggy code
def apply_patches():
    s3_models.DEFAULT_KEY_BUFFER_SIZE = S3_MAX_FILE_SIZE_MB * 1024 * 1024

    def init(
        self,
        name,
        value,
        storage="STANDARD",
        etag=None,
        is_versioned=False,
        version_id=0,
        max_buffer_size=None,
        *args,
        **kwargs,
    ):
        return original_init(
            self,
            name,
            value,
            storage=storage,
            etag=etag,
            is_versioned=is_versioned,
            version_id=version_id,
            max_buffer_size=s3_models.DEFAULT_KEY_BUFFER_SIZE,
            *args,
            **kwargs,
        )

    original_init = s3_models.FakeKey.__init__
    s3_models.FakeKey.__init__ = init

    def s3_update_acls(self, request, query, bucket_name, key_name):
        # fix for - https://github.com/localstack/localstack/issues/1733
        #         - https://github.com/localstack/localstack/issues/1170
        acl_key = "acl|%s|%s" % (bucket_name, key_name)
        acl = self._acl_from_headers(request.headers)
        if acl:
            TMP_STATE[acl_key] = acl
        if not query.get("uploadId"):
            return
        bucket = self.backend.get_bucket(bucket_name)
        key = bucket and self.backend.get_key(bucket_name, key_name)
        if not key:
            return
        acl = acl or TMP_STATE.pop(acl_key, None) or bucket.acl
        if acl:
            key.set_acl(acl)

    # patch Bucket.create_from_cloudformation_json in moto
    @classmethod
    def Bucket_create_from_cloudformation_json(
        cls, resource_name, cloudformation_json, region_name
    ):
        result = create_from_cloudformation_json_orig(
            resource_name, cloudformation_json, region_name
        )
        # remove the bucket from the backend, as our template_deployer will take care of creating the resource
        resource_name = s3_listener.normalize_bucket_name(resource_name)
        s3_models.s3_backend.buckets.pop(resource_name)
        return result

    create_from_cloudformation_json_orig = (
        s3_models.FakeBucket.create_from_cloudformation_json
    )
    s3_models.FakeBucket.create_from_cloudformation_json = (
        Bucket_create_from_cloudformation_json
    )

    # patch S3Bucket.create_bucket(..)
    def create_bucket(self, bucket_name, region_name, *args, **kwargs):
        bucket_name = s3_listener.normalize_bucket_name(bucket_name)
        return create_bucket_orig(bucket_name, region_name, *args, **kwargs)

    create_bucket_orig = s3_models.s3_backend.create_bucket
    s3_models.s3_backend.create_bucket = types.MethodType(
        create_bucket, s3_models.s3_backend
    )

    # patch S3Bucket.get_bucket(..)
    def get_bucket(self, bucket_name, *args, **kwargs):
        bucket_name = s3_listener.normalize_bucket_name(bucket_name)
        return get_bucket_orig(bucket_name, *args, **kwargs)

    get_bucket_orig = s3_models.s3_backend.get_bucket
    s3_models.s3_backend.get_bucket = types.MethodType(get_bucket, s3_models.s3_backend)

    # patch S3Bucket.get_bucket(..)
    def delete_bucket(self, bucket_name, *args, **kwargs):
        bucket_name = s3_listener.normalize_bucket_name(bucket_name)
        return delete_bucket_orig(bucket_name, *args, **kwargs)

    delete_bucket_orig = s3_models.s3_backend.delete_bucket
    s3_models.s3_backend.delete_bucket = types.MethodType(
        delete_bucket, s3_models.s3_backend
    )

    # patch _key_response_post(..)
    def s3_key_response_post(
        self, request, body, bucket_name, query, key_name, *args, **kwargs
    ):
        result = s3_key_response_post_orig(
            request, body, bucket_name, query, key_name, *args, **kwargs
        )
        s3_update_acls(self, request, query, bucket_name, key_name)
        return result

    s3_key_response_post_orig = s3_responses.S3ResponseInstance._key_response_post
    s3_responses.S3ResponseInstance._key_response_post = types.MethodType(
        s3_key_response_post, s3_responses.S3ResponseInstance
    )

    # patch _key_response_put(..)
    def s3_key_response_put(
        self, request, body, bucket_name, query, key_name, headers, *args, **kwargs
    ):
        result = s3_key_response_put_orig(
            request, body, bucket_name, query, key_name, headers, *args, **kwargs
        )
        s3_update_acls(self, request, query, bucket_name, key_name)
        return result

    s3_key_response_put_orig = s3_responses.S3ResponseInstance._key_response_put
    s3_responses.S3ResponseInstance._key_response_put = types.MethodType(
        s3_key_response_put, s3_responses.S3ResponseInstance
    )

    # patch DeleteObjectTagging
    def s3_key_response_delete(self, bucket_name, query, key_name, *args, **kwargs):
        # Fixes https://github.com/localstack/localstack/issues/1083
        if query.get("tagging"):
            self._set_action("KEY", "DELETE", query)
            self._authenticate_and_authorize_s3_action()
            key = self.backend.get_key(bucket_name, key_name)
            key.tags = s3_models.FakeTagging()
            return 204, {}, ""
        result = s3_key_response_delete_orig(
            bucket_name, query, key_name, *args, **kwargs
        )
        return result

    s3_key_response_delete_orig = s3_responses.S3ResponseInstance._key_response_delete
    s3_responses.S3ResponseInstance._key_response_delete = types.MethodType(
        s3_key_response_delete, s3_responses.S3ResponseInstance
    )
    s3_responses.ACTION_MAP["KEY"]["DELETE"]["tagging"] = "DeleteObjectTagging"

    # patch max-keys
    def s3_truncate_result(self, result_keys, max_keys):
        return s3_truncate_result_orig(result_keys, max_keys or 1000)

    s3_truncate_result_orig = s3_responses.S3ResponseInstance._truncate_result
    s3_responses.S3ResponseInstance._truncate_result = types.MethodType(
        s3_truncate_result, s3_responses.S3ResponseInstance
    )

    # patch _bucket_response_delete_keys(..)
    # https://github.com/localstack/localstack/issues/2077
    s3_delete_keys_response_template = """<?xml version="1.0" encoding="UTF-8"?>
    <DeleteResult xmlns="http://s3.amazonaws.com/doc/2006-03-01">
    {% for k in deleted %}
    <Deleted>
    <Key>{{k.key}}</Key>
    <VersionId>{{k.version_id}}</VersionId>
    </Deleted>
    {% endfor %}
    {% for k in delete_errors %}
    <Error>
    <Key>{{k}}</Key>
    </Error>
    {% endfor %}
    </DeleteResult>"""

    def s3_bucket_response_delete_keys(self, request, body, bucket_name):
        template = self.response_template(s3_delete_keys_response_template)

        elements = minidom.parseString(body).getElementsByTagName("Object")
        if len(elements) == 0:
            raise MalformedXML()

        deleted_names = []
        error_names = []

        keys = []
        for element in elements:
            if len(element.getElementsByTagName("VersionId")) == 0:
                version_id = None
            else:
                version_id = element.getElementsByTagName("VersionId")[
                    0
                ].firstChild.nodeValue

            keys.append(
                {
                    "key_name": element.getElementsByTagName("Key")[
                        0
                    ].firstChild.nodeValue,
                    "version_id": version_id,
                }
            )

        for k in keys:
            key_name = k["key_name"]
            version_id = k["version_id"]
            success = self.backend.delete_key(
                bucket_name, undo_clean_key_name(key_name), version_id
            )

            if success:
                deleted_names.append({"key": key_name, "version_id": version_id})
            else:
                error_names.append(key_name)

        return (
            200,
            {},
            template.render(deleted=deleted_names, delete_errors=error_names),
        )

    s3_responses.S3ResponseInstance._bucket_response_delete_keys = types.MethodType(
        s3_bucket_response_delete_keys, s3_responses.S3ResponseInstance
    )

    # Patch _handle_range_header(..)
    # https://github.com/localstack/localstack/issues/2146
    s3_response_handle_range_header_orig = (
        s3_responses.S3ResponseInstance._handle_range_header
    )

    def s3_response_handle_range_header(self, request, headers, response_content):
        rs_code, rs_headers, rs_content = s3_response_handle_range_header_orig(
            request, headers, response_content
        )
        if rs_code == 206:
            for k in ["ETag", "last-modified"]:
                v = headers.get(k)
                if v and not rs_headers.get(k):
                    rs_headers[k] = v

        return rs_code, rs_headers, rs_content

    s3_responses.S3ResponseInstance._handle_range_header = types.MethodType(
        s3_response_handle_range_header, s3_responses.S3ResponseInstance
    )


# Traceback (KeyError)
localstack_main | 2020-03-17T14:10:42:warning:moto: No Moto CloudFormation support for AWS::S3::BucketPolicy
localstack_main | 2020-03-17T14:10:47:warning:localstack.services.awslambda.lambda_api: Function not found: arn:aws:lambda:us-east-1:000000000000:function:sample1-local-downloadFile
localstack_main | 2020-03-17T14:10:47:warning:localstack.services.cloudformation.cloudformation_starter: Unable to extract id for resource IamRoleLambdaExecution: {'Role': {'Path': '/', 'RoleName': 'sample1-local-us-east-1-lambdaRole', 'RoleId': '4uzole7bkp69kfgntgrq', 'Arn': 'arn:aws:iam::000000000000:role/sample1-local-us-east-1-lambdaRole', 'CreateDate': datetime.datetime(2020, 3, 17, 14, 10, 47, 927000, tzinfo=tzlocal()), 'AssumeRolePolicyDocument': {'Version': '2012-10-17', 'Statement': [{'Effect': 'Allow', 'Principal': {'Service': ['lambda.amazonaws.com']}, 'Action': ['sts:AssumeRole']}]}, 'MaxSessionDuration': 3600}, 'ResponseMetadata': {'RequestId': '4a93ceee-9966-11e1-b624-b1aEXAMPLE7c', 'HTTPStatusCode': 200, 'HTTPHeaders': {'server': 'BaseHTTP/0.6 Python/3.8.2', 'date': 'Tue, 17 Mar 2020 14:10:47 GMT', 'content-type': 'text/html; charset=utf-8', 'content-length': '709', 'access-control-allow-origin': '*', 'access-control-allow-methods': 'HEAD,GET,PUT,POST,DELETE,OPTIONS,PATCH', 'access-control-allow-headers': 'authorization,content-type,content-md5,cache-control,x-amz-content-sha256,x-amz-date,x-amz-security-token,x-amz-user-agent,x-amz-target,x-amz-acl,x-amz-version-id,x-localstack-target,x-amz-tagging', 'access-control-expose-headers': 'x-amz-version-id'}, 'RetryAttempts': 0}}
localstack_main | 2020-03-17T14:10:47:warning:localstack.services.awslambda.lambda_api: Function not found: arn:aws:lambda:us-east-1:000000000000:function:sample1-local-downloadFile
localstack_main | 2020-03-17T14:10:48:ERROR:localstack.services.cloudformation.cloudformation_starter: Unable to parse and create resource "DownloadFileLambdaFunction": An error occurred (InvalidParameterValueException) when calling the None operation: Error occurred while GetObject. S3 Error Code: NoSuchBucket. S3 Error Message: The specified bucket does not exist Traceback (most recent call last):
localstack_main |   File "/opt/code/localstack/.venv/lib/python3.8/site-packages/moto/s3/models.py", line 1190, in get_bucket
localstack_main |     return self.buckets[bucket_name]
localstack_main | KeyError: '__local__'
localstack_main |
localstack_main | During handling of the above exception, another exception occurred:
localstack_main |
localstack_main | Traceback (most recent call last):
localstack_main |   File "/opt/code/localstack/.venv/lib/python3.8/site-packages/moto/awslambda/models.py", line 219, in __init__
localstack_main |     key = s3_backend.get_key(self.code["S3Bucket"], self.code["S3Key"])
localstack_main |   File "/opt/code/localstack/.venv/lib/python3.8/site-packages/moto/s3/models.py", line 1323, in get_key
localstack_main |     bucket = self.get_bucket(bucket_name)
localstack_main |   File "/opt/code/localstack/localstack/services/s3/s3_starter.py", line 103, in get_bucket
localstack_main |     return get_bucket_orig(bucket_name, *args, **kwargs)
localstack_main |   File "/opt/code/localstack/.venv/lib/python3.8/site-packages/moto/s3/models.py", line 1192, in get_bucket
localstack_main |     raise MissingBucket(bucket=bucket_name)
localstack_main | moto.s3.exceptions.MissingBucket: 404 Not Found: <?xml version="1.0" encoding="UTF-8"?>
localstack_main | <Error>
localstack_main |     <Code>NoSuchBucket</Code>
localstack_main |     <Message>The specified bucket does not exist</Message>
localstack_main |     <BucketName>__local__</BucketName>
localstack_main |     <RequestID>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestID>
localstack_main | </Error>
localstack_main |
localstack_main | During handling of the above exception, another exception occurred:
localstack_main |
localstack_main | Traceback (most recent call last):
localstack_main |   File "/opt/code/localstack/localstack/services/cloudformation/cloudformation_starter.py", line 174, in parse_and_create_resource
localstack_main |     return _parse_and_create_resource(logical_id, resource_json,
localstack_main |   File "/opt/code/localstack/localstack/services/cloudformation/cloudformation_starter.py", line 242, in _parse_and_create_resource
localstack_main |     raise moto_create_error
localstack_main |   File "/opt/code/localstack/localstack/services/cloudformation/cloudformation_starter.py", line 228, in _parse_and_create_resource
localstack_main |     resource = parse_and_create_resource_orig(logical_id,
localstack_main |   File "/opt/code/localstack/.venv/lib/python3.8/site-packages/moto/cloudformation/parsing.py", line 338, in parse_and_create_resource
localstack_main |     resource = resource_class.create_from_cloudformation_json(
localstack_main |   File "/opt/code/localstack/localstack/services/cloudformation/cloudformation_starter.py", line 549, in Lambda_create_from_cloudformation_json
localstack_main |     return Lambda_create_from_cloudformation_json_orig(resource_name, cloudformation_json, region_name)
localstack_main |   File "/opt/code/localstack/.venv/lib/python3.8/site-packages/moto/awslambda/models.py", line 516, in create_from_cloudformation_json
localstack_main |     fn = backend.create_function(spec)
localstack_main |   File "/opt/code/localstack/.venv/lib/python3.8/site-packages/moto/awslambda/models.py", line 822, in create_function
localstack_main |     fn = LambdaFunction(spec, self.region_name, version="$LATEST")
localstack_main |   File "/opt/code/localstack/.venv/lib/python3.8/site-packages/moto/awslambda/models.py", line 222, in __init__
localstack_main |     raise InvalidParameterValueException(
localstack_main | moto.awslambda.exceptions.InvalidParameterValueException: An error occurred (InvalidParameterValueException) when calling the None operation: Error occurred while GetObject. S3 Error Code: NoSuchBucket. S3 Error Message: The specified bucket does not exist

# Fixed code
def apply_patches():
    s3_models.DEFAULT_KEY_BUFFER_SIZE = S3_MAX_FILE_SIZE_MB * 1024 * 1024

    def init(
        self,
        name,
        value,
        storage="STANDARD",
        etag=None,
        is_versioned=False,
        version_id=0,
        max_buffer_size=None,
        *args,
        **kwargs,
    ):
        return original_init(
            self,
            name,
            value,
            storage=storage,
            etag=etag,
            is_versioned=is_versioned,
            version_id=version_id,
            max_buffer_size=s3_models.DEFAULT_KEY_BUFFER_SIZE,
            *args,
            **kwargs,
        )

    original_init = s3_models.FakeKey.__init__
    s3_models.FakeKey.__init__ = init

    def s3_update_acls(self, request, query, bucket_name, key_name):
        # fix for - https://github.com/localstack/localstack/issues/1733
        #         - https://github.com/localstack/localstack/issues/1170
        acl_key = "acl|%s|%s" % (bucket_name, key_name)
        acl = self._acl_from_headers(request.headers)
        if acl:
            TMP_STATE[acl_key] = acl
        if not query.get("uploadId"):
            return
        bucket = self.backend.get_bucket(bucket_name)
        key = bucket and self.backend.get_key(bucket_name, key_name)
        if not key:
            return
        acl = acl or TMP_STATE.pop(acl_key, None) or bucket.acl
        if acl:
            key.set_acl(acl)

    # patch Bucket.create_from_cloudformation_json in moto
    @classmethod
    def Bucket_create_from_cloudformation_json(
        cls, resource_name, cloudformation_json, region_name
    ):
        result = create_from_cloudformation_json_orig(
            resource_name, cloudformation_json, region_name
        )
        # remove the bucket from the backend, as our template_deployer will take care of creating the resource
        resource_name = s3_listener.normalize_bucket_name(resource_name)
        s3_models.s3_backend.buckets.pop(resource_name)
        return result

    create_from_cloudformation_json_orig = (
        s3_models.FakeBucket.create_from_cloudformation_json
    )
    s3_models.FakeBucket.create_from_cloudformation_json = (
        Bucket_create_from_cloudformation_json
    )

    # patch S3Bucket.create_bucket(..)
    def create_bucket(self, bucket_name, region_name, *args, **kwargs):
        bucket_name = s3_listener.normalize_bucket_name(bucket_name)
        return create_bucket_orig(bucket_name, region_name, *args, **kwargs)

    create_bucket_orig = s3_models.s3_backend.create_bucket
    s3_models.s3_backend.create_bucket = types.MethodType(
        create_bucket, s3_models.s3_backend
    )

    # patch S3Bucket.get_bucket(..)
    def get_bucket(self, bucket_name, *args, **kwargs):
        bucket_name = s3_listener.normalize_bucket_name(bucket_name)
        if bucket_name == BUCKET_MARKER_LOCAL:
            return None
        return get_bucket_orig(bucket_name, *args, **kwargs)

    get_bucket_orig = s3_models.s3_backend.get_bucket
    s3_models.s3_backend.get_bucket = types.MethodType(get_bucket, s3_models.s3_backend)

    # patch S3Bucket.get_bucket(..)
    def delete_bucket(self, bucket_name, *args, **kwargs):
        bucket_name = s3_listener.normalize_bucket_name(bucket_name)
        return delete_bucket_orig(bucket_name, *args, **kwargs)

    delete_bucket_orig = s3_models.s3_backend.delete_bucket
    s3_models.s3_backend.delete_bucket = types.MethodType(
        delete_bucket, s3_models.s3_backend
    )

    # patch _key_response_post(..)
    def s3_key_response_post(
        self, request, body, bucket_name, query, key_name, *args, **kwargs
    ):
        result = s3_key_response_post_orig(
            request, body, bucket_name, query, key_name, *args, **kwargs
        )
        s3_update_acls(self, request, query, bucket_name, key_name)
        return result

    s3_key_response_post_orig = s3_responses.S3ResponseInstance._key_response_post
    s3_responses.S3ResponseInstance._key_response_post = types.MethodType(
        s3_key_response_post, s3_responses.S3ResponseInstance
    )

    # patch _key_response_put(..)
    def s3_key_response_put(
        self, request, body, bucket_name, query, key_name, headers, *args, **kwargs
    ):
        result = s3_key_response_put_orig(
            request, body, bucket_name, query, key_name, headers, *args, **kwargs
        )
        s3_update_acls(self, request, query, bucket_name, key_name)
        return result

    s3_key_response_put_orig = s3_responses.S3ResponseInstance._key_response_put
    s3_responses.S3ResponseInstance._key_response_put = types.MethodType(
        s3_key_response_put, s3_responses.S3ResponseInstance
    )

    # patch DeleteObjectTagging
    def s3_key_response_delete(self, bucket_name, query, key_name, *args, **kwargs):
        # Fixes https://github.com/localstack/localstack/issues/1083
        if query.get("tagging"):
            self._set_action("KEY", "DELETE", query)
            self._authenticate_and_authorize_s3_action()
            key = self.backend.get_key(bucket_name, key_name)
            key.tags = s3_models.FakeTagging()
            return 204, {}, ""
        result = s3_key_response_delete_orig(
            bucket_name, query, key_name, *args, **kwargs
        )
        return result

    s3_key_response_delete_orig = s3_responses.S3ResponseInstance._key_response_delete
    s3_responses.S3ResponseInstance._key_response_delete = types.MethodType(
        s3_key_response_delete, s3_responses.S3ResponseInstance
    )
    s3_responses.ACTION_MAP["KEY"]["DELETE"]["tagging"] = "DeleteObjectTagging"

    # patch max-keys
    def s3_truncate_result(self, result_keys, max_keys):
        return s3_truncate_result_orig(result_keys, max_keys or 1000)

    s3_truncate_result_orig = s3_responses.S3ResponseInstance._truncate_result
    s3_responses.S3ResponseInstance._truncate_result = types.MethodType(
        s3_truncate_result, s3_responses.S3ResponseInstance
    )

    # patch _bucket_response_delete_keys(..)
    # https://github.com/localstack/localstack/issues/2077
    s3_delete_keys_response_template = """<?xml version="1.0" encoding="UTF-8"?>
    <DeleteResult xmlns="http://s3.amazonaws.com/doc/2006-03-01">
    {% for k in deleted %}
    <Deleted>
    <Key>{{k.key}}</Key>
    <VersionId>{{k.version_id}}</VersionId>
    </Deleted>
    {% endfor %}
    {% for k in delete_errors %}
    <Error>
    <Key>{{k}}</Key>
    </Error>
    {% endfor %}
    </DeleteResult>"""

    def s3_bucket_response_delete_keys(self, request, body, bucket_name):
        template = self.response_template(s3_delete_keys_response_template)

        elements = minidom.parseString(body).getElementsByTagName("Object")
        if len(elements) == 0:
            raise MalformedXML()

        deleted_names = []
        error_names = []

        keys = []
        for element in elements:
            if len(element.getElementsByTagName("VersionId")) == 0:
                version_id = None
            else:
                version_id = element.getElementsByTagName("VersionId")[
                    0
                ].firstChild.nodeValue

            keys.append(
                {
                    "key_name": element.getElementsByTagName("Key")[
                        0
                    ].firstChild.nodeValue,
                    "version_id": version_id,
                }
            )

        for k in keys:
            key_name = k["key_name"]
            version_id = k["version_id"]
            success = self.backend.delete_key(
                bucket_name, undo_clean_key_name(key_name), version_id
            )

            if success:
                deleted_names.append({"key": key_name, "version_id": version_id})
            else:
                error_names.append(key_name)

        return (
            200,
            {},
            template.render(deleted=deleted_names, delete_errors=error_names),
        )

    s3_responses.S3ResponseInstance._bucket_response_delete_keys = types.MethodType(
        s3_bucket_response_delete_keys, s3_responses.S3ResponseInstance
    )

    # Patch _handle_range_header(..)
    # https://github.com/localstack/localstack/issues/2146
    s3_response_handle_range_header_orig = (
        s3_responses.S3ResponseInstance._handle_range_header
    )

    def s3_response_handle_range_header(self, request, headers, response_content):
        rs_code, rs_headers, rs_content = s3_response_handle_range_header_orig(
            request, headers, response_content
        )
        if rs_code == 206:
            for k in ["ETag", "last-modified"]:
                v = headers.get(k)
                if v and not rs_headers.get(k):
                    rs_headers[k] = v

        return rs_code, rs_headers, rs_content

    s3_responses.S3ResponseInstance._handle_range_header = types.MethodType(
        s3_response_handle_range_header, s3_responses.S3ResponseInstance
    )