From 7613a96b14aa5bb5baa7434a9cd1b8192763bc34 Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Tue, 13 Oct 2026 16:19:14 +0900 Subject: [PATCH 1/2] fix(runtime-sdk): lazily bound ssl module to make boto3 work --- .../_workers_sdk_entropy_import_context.py | 39 ++++++++++++++++--- .../entropy-patches/pyproject.toml | 1 + .../entropy-patches/tests/test_boto3.py | 8 ++++ .../workerd-test/entropy-patches/worker.py | 1 + 4 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 packages/runtime-sdk/tests/workerd-test/entropy-patches/tests/test_boto3.py diff --git a/packages/runtime-sdk/src/_workers_sdk_entropy_import_context.py b/packages/runtime-sdk/src/_workers_sdk_entropy_import_context.py index 353e2d48..f251ef9e 100644 --- a/packages/runtime-sdk/src/_workers_sdk_entropy_import_context.py +++ b/packages/runtime-sdk/src/_workers_sdk_entropy_import_context.py @@ -149,13 +149,41 @@ def find_spec(self, fullname, path, target): ) from None +def add_lazy_ssl_attribute(module): + """ + When `import ssl` fails inside a module, the fallback code would leave the name `ssl` + unbound. Other packages may still re-export it from there, e.g. botocore does + `from urllib3.util.ssl_ import ssl`. + + This is a rare situation, and actually something that needs to be fixed in the package. + However, boto3 is a very commonly downloaded package in Python, so we handle this case specifically. + We can drop this when https://github.com/boto/botocore/pull/3640 is merged. + """ + if "ssl" in module.__dict__: + return + + orig_getattr = module.__dict__.get("__getattr__") + + def __getattr__(name): + if name == "ssl": + import ssl + + module.ssl = ssl + return ssl + if orig_getattr is not None: + return orig_getattr(name) + raise AttributeError(f"module {module.__name__!r} has no attribute {name!r}") + + module.__getattr__ = __getattr__ + + @contextmanager -def no_ssl(): +def no_ssl(module): """ Various packages will call ssl.create_default_context() at top level which uses entropy if they can import ssl. By temporarily making importing ssl raise an import error, we exercise the workaround code and so avoid the entropy calls. After, we put the ssl module back to the normal - value. + value and make `module.ssl` available again in case the workaround code left it unbound. """ try: f = NoSslFinder() @@ -166,26 +194,27 @@ def no_ssl(): sys.meta_path.remove(f) if ssl: sys.modules["ssl"] = ssl + add_lazy_ssl_attribute(module) @register_exec_patch("aiohttp.connector") @contextmanager def aiohttp_connector_context(module): - with no_ssl(): + with no_ssl(module): yield @register_exec_patch("requests.adapters") @contextmanager def requests_adapters_context(module): - with no_ssl(): + with no_ssl(module): yield @register_exec_patch("urllib3.util.ssl_") @contextmanager def urllib3_util_ssl__context(module): - with no_ssl(): + with no_ssl(module): yield diff --git a/packages/runtime-sdk/tests/workerd-test/entropy-patches/pyproject.toml b/packages/runtime-sdk/tests/workerd-test/entropy-patches/pyproject.toml index 06640c6a..1026d309 100644 --- a/packages/runtime-sdk/tests/workerd-test/entropy-patches/pyproject.toml +++ b/packages/runtime-sdk/tests/workerd-test/entropy-patches/pyproject.toml @@ -9,6 +9,7 @@ dependencies = [ "pydantic", "requests", "urllib3", + "boto3", "tiktoken", "cryptography", "jiter", diff --git a/packages/runtime-sdk/tests/workerd-test/entropy-patches/tests/test_boto3.py b/packages/runtime-sdk/tests/workerd-test/entropy-patches/tests/test_boto3.py new file mode 100644 index 00000000..ba1e67fc --- /dev/null +++ b/packages/runtime-sdk/tests/workerd-test/entropy-patches/tests/test_boto3.py @@ -0,0 +1,8 @@ +# ruff: noqa: F401 +# boto3 does `from urllib3.util.ssl_ import ssl`, but `urllib3.util.ssl_` is +# imported without ssl to avoid entropy calls, which leaves `ssl` unbound there. +import boto3 + + +def test_import(): + pass diff --git a/packages/runtime-sdk/tests/workerd-test/entropy-patches/worker.py b/packages/runtime-sdk/tests/workerd-test/entropy-patches/worker.py index c4fc3c2e..719ceda0 100644 --- a/packages/runtime-sdk/tests/workerd-test/entropy-patches/worker.py +++ b/packages/runtime-sdk/tests/workerd-test/entropy-patches/worker.py @@ -2,6 +2,7 @@ # a snapshot. from tests import ( # noqa: F401 test_aiohttp_websocket, + test_boto3, test_jsonschema, test_langsmith, test_litestar, From 05d6ddd59800adb338818028522ee7d4c71b7753 Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Fri, 16 Oct 2026 13:51:36 +0900 Subject: [PATCH 2/2] chore: address comment --- packages/runtime-sdk/src/_workers_sdk_entropy_import_context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-sdk/src/_workers_sdk_entropy_import_context.py b/packages/runtime-sdk/src/_workers_sdk_entropy_import_context.py index f251ef9e..6cd3befb 100644 --- a/packages/runtime-sdk/src/_workers_sdk_entropy_import_context.py +++ b/packages/runtime-sdk/src/_workers_sdk_entropy_import_context.py @@ -157,7 +157,7 @@ def add_lazy_ssl_attribute(module): This is a rare situation, and actually something that needs to be fixed in the package. However, boto3 is a very commonly downloaded package in Python, so we handle this case specifically. - We can drop this when https://github.com/boto/botocore/pull/3640 is merged. + TODO: We can drop this when https://github.com/boto/botocore/pull/3640 is merged. """ if "ssl" in module.__dict__: return