From 6c81b94ee18b75a8d905770081f0dfd89e7999b5 Mon Sep 17 00:00:00 2001
From: mdsakalu <mdsakalu@users.noreply.github.com>
Date: Tue, 11 Oct 2022 11:44:26 -0400
Subject: [PATCH 01/11] Update simplejson to 3.17.6

Fixes #62
---
 requirements/base.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements/base.txt b/requirements/base.txt
index 614d435..515470b 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -1 +1 @@
-simplejson==3.17.2
+simplejson==3.17.6

From 44300b7db83a675737dcd3331ef4b5232f76bd63 Mon Sep 17 00:00:00 2001
From: mdsakalu <mdsakalu@users.noreply.github.com>
Date: Tue, 11 Oct 2022 11:46:30 -0400
Subject: [PATCH 02/11] Use unicode chars instead of escape sequences in json
 encoder

Fixes #87
---
 awslambdaric/lambda_runtime_marshaller.py | 3 ++-
 tests/test_lambda_runtime_marshaller.py   | 6 ++++++
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/awslambdaric/lambda_runtime_marshaller.py b/awslambdaric/lambda_runtime_marshaller.py
index 7eee25d..b591e69 100644
--- a/awslambdaric/lambda_runtime_marshaller.py
+++ b/awslambdaric/lambda_runtime_marshaller.py
@@ -12,9 +12,10 @@
 
 # simplejson's Decimal encoding allows '-NaN' as an output, which is a parse error for json.loads
 # to get the good parts of Decimal support, we'll special-case NaN decimals and otherwise duplicate the encoding for decimals the same way simplejson does
+# We also set 'ensure_ascii=False' so that the encoded json contains unicode characters instead of unicode escape sequences
 class Encoder(json.JSONEncoder):
     def __init__(self):
-        super().__init__(use_decimal=False)
+        super().__init__(use_decimal=False, ensure_ascii=False)
 
     def default(self, obj):
         if isinstance(obj, decimal.Decimal):
diff --git a/tests/test_lambda_runtime_marshaller.py b/tests/test_lambda_runtime_marshaller.py
index 8268de1..eeb2715 100644
--- a/tests/test_lambda_runtime_marshaller.py
+++ b/tests/test_lambda_runtime_marshaller.py
@@ -37,3 +37,9 @@ def test_json_serializer_is_not_default_json(self):
         self.assertTrue(hasattr(internal_json, "YOLO"))
         self.assertFalse(hasattr(stock_json, "YOLO"))
         self.assertTrue(hasattr(simplejson, "YOLO"))
+
+    def test_to_json_unicode_encoding(self):
+        response = to_json({"price": "£1.00"})
+        self.assertEqual('{"price": "£1.00"}', response)
+        self.assertNotEqual('{"price": "\\u00a31.00"}', response)
+        self.assertEqual(19, len(response.encode('utf-8')))  # would be 23 bytes if a unicode escape was returned

From 20715b6e5dda6705d95aea196207c576ad3f0d27 Mon Sep 17 00:00:00 2001
From: Tiangang Song <git@bitsea.link>
Date: Thu, 14 Sep 2023 13:16:19 -0700
Subject: [PATCH 03/11] Fix runtime_client blocking main thread

runtime_client.next is calling into the C extension which blocks the
main thread. Moving it to a separate thread enables the main thread
to process signal, see this issue for more details: #105
---
 awslambdaric/lambda_runtime_client.py | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/awslambdaric/lambda_runtime_client.py b/awslambdaric/lambda_runtime_client.py
index 2066f6c..b268abc 100644
--- a/awslambdaric/lambda_runtime_client.py
+++ b/awslambdaric/lambda_runtime_client.py
@@ -5,6 +5,7 @@
 import http
 import http.client
 import sys
+from concurrent.futures import ThreadPoolExecutor
 from awslambdaric import __version__
 
 
@@ -65,7 +66,9 @@ def post_init_error(self, error_response_data):
             raise LambdaRuntimeClientError(endpoint, response.code, response_body)
 
     def wait_next_invocation(self):
-        response_body, headers = runtime_client.next()
+        with ThreadPoolExecutor() as e:
+            fut = e.submit(runtime_client.next)
+        response_body, headers = fut.result()
         return InvocationRequest(
             invoke_id=headers.get("Lambda-Runtime-Aws-Request-Id"),
             x_amzn_trace_id=headers.get("Lambda-Runtime-Trace-Id"),

From 10913dc9f15ada85921161cc565fb12b77954b8f Mon Sep 17 00:00:00 2001
From: Sean O Brien <briensea@amazon.com>
Date: Tue, 10 Oct 2023 15:46:01 +0000
Subject: [PATCH 04/11] Add Python3.12.

---
 README.md                                                  | 2 +-
 setup.py                                                   | 1 +
 tests/integration/codebuild/buildspec.os.alpine.yml        | 1 +
 tests/integration/codebuild/buildspec.os.amazonlinux.1.yml | 1 +
 tests/integration/codebuild/buildspec.os.amazonlinux.2.yml | 1 +
 tests/integration/codebuild/buildspec.os.centos.yml        | 1 +
 tests/integration/codebuild/buildspec.os.debian.yml        | 1 +
 tests/integration/codebuild/buildspec.os.ubuntu.yml        | 1 +
 8 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index ca1a0f9..248fde4 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ You can include this package in your preferred base image to make that base imag
 
 ## Requirements
 The Python Runtime Interface Client package currently supports Python versions:
- - 3.7.x up to and including 3.11.x
+ - 3.7.x up to and including 3.12.x
 
 ## Usage
 
diff --git a/setup.py b/setup.py
index a69c646..2544b21 100644
--- a/setup.py
+++ b/setup.py
@@ -90,6 +90,7 @@ def read_requirements(req="base.txt"):
         "Programming Language :: Python :: 3.9",
         "Programming Language :: Python :: 3.10",
         "Programming Language :: Python :: 3.11",
+        "Programming Language :: Python :: 3.12",
         "License :: OSI Approved :: Apache Software License",
         "Operating System :: OS Independent",
     ],
diff --git a/tests/integration/codebuild/buildspec.os.alpine.yml b/tests/integration/codebuild/buildspec.os.alpine.yml
index eba1d14..da09a26 100644
--- a/tests/integration/codebuild/buildspec.os.alpine.yml
+++ b/tests/integration/codebuild/buildspec.os.alpine.yml
@@ -24,6 +24,7 @@ batch:
             - "3.9"
             - "3.10"
             - "3.11"
+            - "3.12"
 phases:
   pre_build:
     commands:
diff --git a/tests/integration/codebuild/buildspec.os.amazonlinux.1.yml b/tests/integration/codebuild/buildspec.os.amazonlinux.1.yml
index 5ec01d2..91bb021 100644
--- a/tests/integration/codebuild/buildspec.os.amazonlinux.1.yml
+++ b/tests/integration/codebuild/buildspec.os.amazonlinux.1.yml
@@ -22,6 +22,7 @@ batch:
             - "3.9"
             - "3.10"
             - "3.11"
+            - "3.12"
 phases:
   pre_build:
     commands:
diff --git a/tests/integration/codebuild/buildspec.os.amazonlinux.2.yml b/tests/integration/codebuild/buildspec.os.amazonlinux.2.yml
index 18cabc9..38f2509 100644
--- a/tests/integration/codebuild/buildspec.os.amazonlinux.2.yml
+++ b/tests/integration/codebuild/buildspec.os.amazonlinux.2.yml
@@ -22,6 +22,7 @@ batch:
             - "3.9"
             - "3.10"
             - "3.11"
+            - "3.12"
 phases:
   pre_build:
     commands:
diff --git a/tests/integration/codebuild/buildspec.os.centos.yml b/tests/integration/codebuild/buildspec.os.centos.yml
index f993c7d..4058a1e 100644
--- a/tests/integration/codebuild/buildspec.os.centos.yml
+++ b/tests/integration/codebuild/buildspec.os.centos.yml
@@ -22,6 +22,7 @@ batch:
             - "3.9"
             - "3.10"
             - "3.11"
+            - "3.12"
 phases:
   pre_build:
     commands:
diff --git a/tests/integration/codebuild/buildspec.os.debian.yml b/tests/integration/codebuild/buildspec.os.debian.yml
index 48305bb..628fd95 100644
--- a/tests/integration/codebuild/buildspec.os.debian.yml
+++ b/tests/integration/codebuild/buildspec.os.debian.yml
@@ -23,6 +23,7 @@ batch:
             - "3.9"
             - "3.10"
             - "3.11"
+            - "3.12"
 phases:
   pre_build:
     commands:
diff --git a/tests/integration/codebuild/buildspec.os.ubuntu.yml b/tests/integration/codebuild/buildspec.os.ubuntu.yml
index 7c66865..b876817 100644
--- a/tests/integration/codebuild/buildspec.os.ubuntu.yml
+++ b/tests/integration/codebuild/buildspec.os.ubuntu.yml
@@ -23,6 +23,7 @@ batch:
             - "3.9"
             - "3.10"
             - "3.11"
+            - "3.12"
 phases:
   pre_build:
     commands:

From 5cb30290d1b69fa170c3333ac8d40218ddd8529b Mon Sep 17 00:00:00 2001
From: Aleksei Grebenkin <alexqq@amazon.com>
Date: Wed, 18 Oct 2023 17:56:34 +0000
Subject: [PATCH 05/11] Lazily initialize http modules used for error handling

---
 awslambdaric/lambda_runtime_client.py | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/awslambdaric/lambda_runtime_client.py b/awslambdaric/lambda_runtime_client.py
index 2066f6c..7df0f88 100644
--- a/awslambdaric/lambda_runtime_client.py
+++ b/awslambdaric/lambda_runtime_client.py
@@ -2,8 +2,6 @@
 Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 """
 
-import http
-import http.client
 import sys
 from awslambdaric import __version__
 
@@ -54,6 +52,11 @@ def __init__(self, lambda_runtime_address):
         self.lambda_runtime_address = lambda_runtime_address
 
     def post_init_error(self, error_response_data):
+        # These imports are heavy-weight. They implicitly trigger `import ssl, hashlib`.
+        # Importing them lazily to speed up critical path of a common case.
+        import http
+        import http.client
+
         runtime_connection = http.client.HTTPConnection(self.lambda_runtime_address)
         runtime_connection.connect()
         endpoint = "/2018-06-01/runtime/init/error"

From 8db75c515504f76090ebc1b9fb856020a4133dcc Mon Sep 17 00:00:00 2001
From: Sean O Brien <briensea@amazon.com>
Date: Thu, 26 Oct 2023 16:11:53 +0100
Subject: [PATCH 06/11] Downgrade to simplejson 3.17.2. (#123)

* Downgrade to simplejson 3.17.2.

* Fix test formatting.
---
 requirements/base.txt                   | 2 +-
 tests/test_lambda_runtime_marshaller.py | 4 +++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/requirements/base.txt b/requirements/base.txt
index 515470b..614d435 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -1 +1 @@
-simplejson==3.17.6
+simplejson==3.17.2
diff --git a/tests/test_lambda_runtime_marshaller.py b/tests/test_lambda_runtime_marshaller.py
index eeb2715..eb8b848 100644
--- a/tests/test_lambda_runtime_marshaller.py
+++ b/tests/test_lambda_runtime_marshaller.py
@@ -42,4 +42,6 @@ def test_to_json_unicode_encoding(self):
         response = to_json({"price": "£1.00"})
         self.assertEqual('{"price": "£1.00"}', response)
         self.assertNotEqual('{"price": "\\u00a31.00"}', response)
-        self.assertEqual(19, len(response.encode('utf-8')))  # would be 23 bytes if a unicode escape was returned
+        self.assertEqual(
+            19, len(response.encode("utf-8"))
+        )  # would be 23 bytes if a unicode escape was returned

From 80eefef7da59c814369b3ec37f6d30d46461f181 Mon Sep 17 00:00:00 2001
From: Sean O Brien <briensea@amazon.com>
Date: Fri, 27 Oct 2023 17:27:29 +0100
Subject: [PATCH 07/11] Control unicode encoding behaviour based on execution
 environment. (#122)

---
 awslambdaric/lambda_runtime_marshaller.py |  7 ++--
 requirements/dev.txt                      |  1 +
 tests/test_lambda_runtime_marshaller.py   | 39 +++++++++++++++++++++--
 3 files changed, 43 insertions(+), 4 deletions(-)

diff --git a/awslambdaric/lambda_runtime_marshaller.py b/awslambdaric/lambda_runtime_marshaller.py
index b591e69..42ee127 100644
--- a/awslambdaric/lambda_runtime_marshaller.py
+++ b/awslambdaric/lambda_runtime_marshaller.py
@@ -4,7 +4,7 @@
 
 import decimal
 import math
-
+import os
 import simplejson as json
 
 from .lambda_runtime_exception import FaultException
@@ -15,7 +15,10 @@
 # We also set 'ensure_ascii=False' so that the encoded json contains unicode characters instead of unicode escape sequences
 class Encoder(json.JSONEncoder):
     def __init__(self):
-        super().__init__(use_decimal=False, ensure_ascii=False)
+        if os.environ.get("AWS_EXECUTION_ENV") == "AWS_Lambda_python3.12":
+            super().__init__(use_decimal=False, ensure_ascii=False)
+        else:
+            super().__init__(use_decimal=False)
 
     def default(self, obj):
         if isinstance(obj, decimal.Decimal):
diff --git a/requirements/dev.txt b/requirements/dev.txt
index c432413..68377ce 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -9,3 +9,4 @@ bandit>=1.6.2
 # Test requirements
 pytest>=3.0.7
 mock>=2.0.0
+parameterized>=0.9.0
\ No newline at end of file
diff --git a/tests/test_lambda_runtime_marshaller.py b/tests/test_lambda_runtime_marshaller.py
index eb8b848..7cd73b4 100644
--- a/tests/test_lambda_runtime_marshaller.py
+++ b/tests/test_lambda_runtime_marshaller.py
@@ -3,12 +3,35 @@
 """
 
 import decimal
+import os
 import unittest
-
+from parameterized import parameterized
 from awslambdaric.lambda_runtime_marshaller import to_json
 
 
 class TestLambdaRuntimeMarshaller(unittest.TestCase):
+    execution_envs = (
+        "AWS_Lambda_python3.12",
+        "AWS_Lambda_python3.11",
+        "AWS_Lambda_python3.10",
+        "AWS_Lambda_python3.9",
+    )
+
+    envs_lambda_marshaller_ensure_ascii_false = {"AWS_Lambda_python3.12"}
+
+    execution_envs_lambda_marshaller_ensure_ascii_true = tuple(
+        set(execution_envs).difference(envs_lambda_marshaller_ensure_ascii_false)
+    )
+    execution_envs_lambda_marshaller_ensure_ascii_false = tuple(
+        envs_lambda_marshaller_ensure_ascii_false
+    )
+
+    def setUp(self):
+        self.org_os_environ = os.environ
+
+    def tearDown(self):
+        os.environ = self.org_os_environ
+
     def test_to_json_decimal_encoding(self):
         response = to_json({"pi": decimal.Decimal("3.14159")})
         self.assertEqual('{"pi": 3.14159}', response)
@@ -38,10 +61,22 @@ def test_json_serializer_is_not_default_json(self):
         self.assertFalse(hasattr(stock_json, "YOLO"))
         self.assertTrue(hasattr(simplejson, "YOLO"))
 
-    def test_to_json_unicode_encoding(self):
+    @parameterized.expand(execution_envs_lambda_marshaller_ensure_ascii_false)
+    def test_to_json_unicode_not_escaped_encoding(self, execution_env):
+        os.environ = {"AWS_EXECUTION_ENV": execution_env}
         response = to_json({"price": "£1.00"})
         self.assertEqual('{"price": "£1.00"}', response)
         self.assertNotEqual('{"price": "\\u00a31.00"}', response)
         self.assertEqual(
             19, len(response.encode("utf-8"))
         )  # would be 23 bytes if a unicode escape was returned
+
+    @parameterized.expand(execution_envs_lambda_marshaller_ensure_ascii_true)
+    def test_to_json_unicode_is_escaped_encoding(self, execution_env):
+        os.environ = {"AWS_EXECUTION_ENV": execution_env}
+        response = to_json({"price": "£1.00"})
+        self.assertEqual('{"price": "\\u00a31.00"}', response)
+        self.assertNotEqual('{"price": "£1.00"}', response)
+        self.assertEqual(
+            23, len(response.encode("utf-8"))
+        )  # would be 19 bytes if a escaped was returned

From 573f048edee06b5256af089aaa32d0680b777662 Mon Sep 17 00:00:00 2001
From: Pushkar Chawda <17747651+pushkarchawda@users.noreply.github.com>
Date: Fri, 27 Oct 2023 23:37:20 +0100
Subject: [PATCH 08/11] Tidy up changes added with PR#115 and limit it to only
 work with Python3.12 in Lambda. (#124)

* Tidy up changes added with PR#115 andlimit it to only work with Python3.12 in Lambda.
---------

Co-authored-by: Pushkar Chawda <chawdapu@amazon.com>
---
 awslambdaric/bootstrap.py                |  8 +++++++-
 awslambdaric/lambda_runtime_client.py    | 25 +++++++++++++++++++-----
 awslambdaric/lambda_runtime_exception.py |  1 +
 tests/test_lambda_runtime_client.py      | 15 ++++++++++++++
 4 files changed, 43 insertions(+), 6 deletions(-)

diff --git a/awslambdaric/bootstrap.py b/awslambdaric/bootstrap.py
index a3da58c..f87ee1b 100644
--- a/awslambdaric/bootstrap.py
+++ b/awslambdaric/bootstrap.py
@@ -462,8 +462,14 @@ def run(app_root, handler, lambda_runtime_api_addr):
     sys.stdout = Unbuffered(sys.stdout)
     sys.stderr = Unbuffered(sys.stderr)
 
+    use_thread_for_polling_next = (
+        os.environ.get("AWS_EXECUTION_ENV") == "AWS_Lambda_python3.12"
+    )
+
     with create_log_sink() as log_sink:
-        lambda_runtime_client = LambdaRuntimeClient(lambda_runtime_api_addr)
+        lambda_runtime_client = LambdaRuntimeClient(
+            lambda_runtime_api_addr, use_thread_for_polling_next
+        )
 
         try:
             _setup_logging(_AWS_LAMBDA_LOG_FORMAT, _AWS_LAMBDA_LOG_LEVEL, log_sink)
diff --git a/awslambdaric/lambda_runtime_client.py b/awslambdaric/lambda_runtime_client.py
index b05918b..91ebd4c 100644
--- a/awslambdaric/lambda_runtime_client.py
+++ b/awslambdaric/lambda_runtime_client.py
@@ -3,8 +3,8 @@
 """
 
 import sys
-from concurrent.futures import ThreadPoolExecutor
 from awslambdaric import __version__
+from .lambda_runtime_exception import FaultException
 
 
 def _user_agent():
@@ -49,8 +49,9 @@ class LambdaRuntimeClient(object):
     and response. It allows for function authors to override the the default implementation, LambdaMarshaller which
     unmarshals and marshals JSON, to an instance of a class that implements the same interface."""
 
-    def __init__(self, lambda_runtime_address):
+    def __init__(self, lambda_runtime_address, use_thread_for_polling_next=False):
         self.lambda_runtime_address = lambda_runtime_address
+        self.use_thread_for_polling_next = use_thread_for_polling_next
 
     def post_init_error(self, error_response_data):
         # These imports are heavy-weight. They implicitly trigger `import ssl, hashlib`.
@@ -69,9 +70,23 @@ def post_init_error(self, error_response_data):
             raise LambdaRuntimeClientError(endpoint, response.code, response_body)
 
     def wait_next_invocation(self):
-        with ThreadPoolExecutor() as e:
-            fut = e.submit(runtime_client.next)
-        response_body, headers = fut.result()
+        # Calling runtime_client.next() from a separate thread unblocks the main thread,
+        # which can then process signals.
+        if self.use_thread_for_polling_next:
+            try:
+                from concurrent.futures import ThreadPoolExecutor
+
+                with ThreadPoolExecutor(max_workers=1) as executor:
+                    future = executor.submit(runtime_client.next)
+                response_body, headers = future.result()
+            except Exception as e:
+                raise FaultException(
+                    FaultException.LAMBDA_RUNTIME_CLIENT_ERROR,
+                    "LAMBDA_RUNTIME Failed to get next invocation: {}".format(str(e)),
+                    None,
+                )
+        else:
+            response_body, headers = runtime_client.next()
         return InvocationRequest(
             invoke_id=headers.get("Lambda-Runtime-Aws-Request-Id"),
             x_amzn_trace_id=headers.get("Lambda-Runtime-Trace-Id"),
diff --git a/awslambdaric/lambda_runtime_exception.py b/awslambdaric/lambda_runtime_exception.py
index 416327e..e09af70 100644
--- a/awslambdaric/lambda_runtime_exception.py
+++ b/awslambdaric/lambda_runtime_exception.py
@@ -12,6 +12,7 @@ class FaultException(Exception):
     BUILT_IN_MODULE_CONFLICT = "Runtime.BuiltInModuleConflict"
     MALFORMED_HANDLER_NAME = "Runtime.MalformedHandlerName"
     LAMBDA_CONTEXT_UNMARSHAL_ERROR = "Runtime.LambdaContextUnmarshalError"
+    LAMBDA_RUNTIME_CLIENT_ERROR = "Runtime.LambdaRuntimeClientError"
 
     def __init__(self, exception_type, msg, trace=None):
         self.msg = msg
diff --git a/tests/test_lambda_runtime_client.py b/tests/test_lambda_runtime_client.py
index 47d95cf..b0eae4a 100644
--- a/tests/test_lambda_runtime_client.py
+++ b/tests/test_lambda_runtime_client.py
@@ -84,6 +84,21 @@ def test_wait_next_invocation(self, mock_runtime_client):
         self.assertEqual(event_request.content_type, "application/json")
         self.assertEqual(event_request.event_body, response_body)
 
+        # Using ThreadPoolExecutor to polling next()
+        runtime_client = LambdaRuntimeClient("localhost:1234", True)
+
+        event_request = runtime_client.wait_next_invocation()
+
+        self.assertIsNotNone(event_request)
+        self.assertEqual(event_request.invoke_id, "RID1234")
+        self.assertEqual(event_request.x_amzn_trace_id, "TID1234")
+        self.assertEqual(event_request.invoked_function_arn, "FARN1234")
+        self.assertEqual(event_request.deadline_time_in_ms, 12)
+        self.assertEqual(event_request.client_context, "client_context")
+        self.assertEqual(event_request.cognito_identity, "cognito_identity")
+        self.assertEqual(event_request.content_type, "application/json")
+        self.assertEqual(event_request.event_body, response_body)
+
     @patch("http.client.HTTPConnection", autospec=http.client.HTTPConnection)
     def test_post_init_error(self, MockHTTPConnection):
         mock_conn = MockHTTPConnection.return_value

From 745abedf926d8bf254ab07453c298dabf2bf0266 Mon Sep 17 00:00:00 2001
From: Aleksei Grebenkin <alexqq@amazon.com>
Date: Mon, 30 Oct 2023 09:58:13 +0000
Subject: [PATCH 09/11] Move ThreadPool conditional lazy imports from
 invocation to RIC initialization

---
 awslambdaric/lambda_runtime_client.py | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/awslambdaric/lambda_runtime_client.py b/awslambdaric/lambda_runtime_client.py
index 91ebd4c..ba85902 100644
--- a/awslambdaric/lambda_runtime_client.py
+++ b/awslambdaric/lambda_runtime_client.py
@@ -52,6 +52,12 @@ class LambdaRuntimeClient(object):
     def __init__(self, lambda_runtime_address, use_thread_for_polling_next=False):
         self.lambda_runtime_address = lambda_runtime_address
         self.use_thread_for_polling_next = use_thread_for_polling_next
+        if self.use_thread_for_polling_next:
+            # Conditionally import only for the case when TPE is used in this class.
+            from concurrent.futures import ThreadPoolExecutor
+
+            # Not defining symbol as global to avoid relying on TPE being imported unconditionally.
+            self.ThreadPoolExecutor = ThreadPoolExecutor
 
     def post_init_error(self, error_response_data):
         # These imports are heavy-weight. They implicitly trigger `import ssl, hashlib`.
@@ -74,9 +80,8 @@ def wait_next_invocation(self):
         # which can then process signals.
         if self.use_thread_for_polling_next:
             try:
-                from concurrent.futures import ThreadPoolExecutor
-
-                with ThreadPoolExecutor(max_workers=1) as executor:
+                # TPE class is supposed to be registered at construction time and be ready to use.
+                with self.ThreadPoolExecutor(max_workers=1) as executor:
                     future = executor.submit(runtime_client.next)
                 response_body, headers = future.result()
             except Exception as e:

From 737906f2fb9eadc86d74cd99172312549875d38b Mon Sep 17 00:00:00 2001
From: Aleksei Grebenkin <alexqq@amazon.com>
Date: Mon, 30 Oct 2023 10:05:44 +0000
Subject: [PATCH 10/11] Fix inconsistent test behaviour: @patch not applied

---
 tests/test_bootstrap.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py
index ca367fd..83d31ee 100644
--- a/tests/test_bootstrap.py
+++ b/tests/test_bootstrap.py
@@ -589,10 +589,11 @@ def raise_exception_handler(json_input, lambda_context):
 
         self.assertEqual(mock_stdout.getvalue(), error_logs)
 
-    @patch("sys.stdout", new_callable=StringIO)
+    # The order of patches matter. Using MagicMock resets sys.stdout to the default.
     @patch("importlib.import_module")
+    @patch("sys.stdout", new_callable=StringIO)
     def test_handle_event_request_fault_exception_logging_syntax_error(
-        self, mock_import_module, mock_stdout
+        self, mock_stdout, mock_import_module
     ):
         try:
             eval("-")

From 5693d8858c704dd41a5dc03d0baf1b2872f959e2 Mon Sep 17 00:00:00 2001
From: Aleksei Grebenkin <alexqq@amazon.com>
Date: Mon, 30 Oct 2023 14:52:43 +0000
Subject: [PATCH 11/11] Bump version to 2.0.8

---
 RELEASE.CHANGELOG.md     | 9 +++++++++
 awslambdaric/__init__.py | 2 +-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/RELEASE.CHANGELOG.md b/RELEASE.CHANGELOG.md
index 8ae5c33..e14c364 100644
--- a/RELEASE.CHANGELOG.md
+++ b/RELEASE.CHANGELOG.md
@@ -1,3 +1,12 @@
+### October 30, 2023
+
+`2.0.8`:
+
+- Onboarded Python3.12 ([#118](https://github.com/aws/aws-lambda-python-runtime-interface-client/pull/118))
+- Fix runtime_client blocking main thread from SIGTERM being handled. Enabled by default only for Python3.12 ([#115](https://github.com/aws/aws-lambda-python-runtime-interface-client/pull/115)) ([#124](https://github.com/aws/aws-lambda-python-runtime-interface-client/pull/124)) ([#125](https://github.com/aws/aws-lambda-python-runtime-interface-client/pull/125))
+- Use unicode chars instead of escape sequences in json encoder output. Enabled by default only for Python3.12 ([#88](https://github.com/aws/aws-lambda-python-runtime-interface-client/pull/88)) ([#122](https://github.com/aws/aws-lambda-python-runtime-interface-client/pull/122))
+- Cold start improvements ([#121](https://github.com/aws/aws-lambda-python-runtime-interface-client/pull/121))
+
 ### August 29, 2023
 
 `2.0.7`:
diff --git a/awslambdaric/__init__.py b/awslambdaric/__init__.py
index b0184d1..f9e4637 100644
--- a/awslambdaric/__init__.py
+++ b/awslambdaric/__init__.py
@@ -2,4 +2,4 @@
 Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 """
 
-__version__ = "2.0.7"
+__version__ = "2.0.8"