workers-runtime-sdk 1.1.1__tar.gz → 1.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,24 @@
2
2
 
3
3
  <!-- version list -->
4
4
 
5
+ ## v1.1.3 (2026-05-04)
6
+
7
+ ### Bug Fixes
8
+
9
+ - Add entropy import context for packages from workerd
10
+ ([#99](https://github.com/cloudflare/workers-py/pull/99),
11
+ [`6e574ca`](https://github.com/cloudflare/workers-py/commit/6e574ca000776645d3cf2883e515c96f49a43c2c))
12
+
13
+
14
+ ## v1.1.2 (2026-04-21)
15
+
16
+ ### Bug Fixes
17
+
18
+ - Make top level asgi import work with snapshots
19
+ ([#93](https://github.com/cloudflare/workers-py/pull/93),
20
+ [`3dd4115`](https://github.com/cloudflare/workers-py/commit/3dd41151d201aca4e1b895638fd3926eb1c68756))
21
+
22
+
5
23
  ## v1.1.1 (2026-03-18)
6
24
 
7
25
  ### Bug Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: workers-runtime-sdk
3
- Version: 1.1.1
3
+ Version: 1.1.3
4
4
  Summary: Python SDK for Cloudflare Workers
5
5
  Project-URL: Homepage, https://github.com/cloudflare/workers-py
6
6
  Project-URL: Bug Tracker, https://github.com/cloudflare/workers-py/issues
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "workers-runtime-sdk"
7
- version = "1.1.1"
7
+ version = "1.1.3"
8
8
  description = "Python SDK for Cloudflare Workers"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -68,11 +68,10 @@ warn_unreachable = true
68
68
  no_implicit_optional = true
69
69
 
70
70
  [tool.hatch.build.targets.wheel]
71
- packages = ["src/workers", "src/asgi.py"]
71
+ include = ["src/_*.py", "src/_*.pyi", "src/_*.pth", "src/workers"]
72
72
 
73
- [tool.hatch.build.targets.wheel.force-include]
74
- "src/_cloudflare_compat_flags.pyi" = "_cloudflare_compat_flags.pyi"
75
- "src/_pyodide_entrypoint_helper.pyi" = "_pyodide_entrypoint_helper.pyi"
73
+ [tool.hatch.build.targets.wheel.sources]
74
+ "src" = ""
76
75
 
77
76
  [tool.semantic_release]
78
77
  assets = []
@@ -0,0 +1 @@
1
+ import _workers_sdk_entropy_import_context
@@ -0,0 +1,183 @@
1
+ """
2
+ Top level entropy patches for packages
3
+ """
4
+
5
+ import sys
6
+ from contextlib import contextmanager
7
+
8
+ from _cloudflare.allow_entropy import (
9
+ allow_bad_entropy_calls,
10
+ )
11
+ from _cloudflare.import_patch_manager import (
12
+ block_calls,
13
+ register_after_snapshot,
14
+ register_before_first_request,
15
+ register_create_patch,
16
+ register_exec_patch,
17
+ )
18
+
19
+
20
+ class STATE:
21
+ imported_rust_package = False
22
+ numpy_random = None
23
+
24
+
25
+ @register_create_patch("tiktoken._tiktoken")
26
+ @register_exec_patch("cryptography.exceptions")
27
+ @register_exec_patch("jiter")
28
+ @contextmanager
29
+ def rust_package_context(module):
30
+ """Rust packages need one entropy call if they create a rust hash map at
31
+ init time.
32
+
33
+ For reasons I don't entirely understand, in Pyodide 0.28 only the first Rust package to be
34
+ imported makes the get_entropy call. See gen_rust_import_tests() which tests that importing
35
+ four rust packages in different permutations works correctly.
36
+ """
37
+ if STATE.imported_rust_package:
38
+ yield
39
+ return
40
+ STATE.imported_rust_package = True
41
+ with allow_bad_entropy_calls(1):
42
+ yield
43
+
44
+
45
+ @register_exec_patch("numpy.random")
46
+ @contextmanager
47
+ def numpy_random_context(numpy_random):
48
+ """numpy.random doesn't call getentropy() itself, but we want to block calls
49
+ that might use the bad seed.
50
+
51
+ TODO: Maybe there are more calls we can whitelist?
52
+ TODO: Is it not enough to just block numpy.random.mtrand calls?
53
+ """
54
+ yield
55
+ # Calling default_rng() with a given seed is fine, calling it without a seed
56
+ # will call getentropy() and fail.
57
+ block_calls(numpy_random, allowlist=("default_rng", "RandomState"))
58
+
59
+
60
+ @register_after_snapshot("numpy.random")
61
+ def numpy_random_after_snapshot(numpy_random):
62
+ r1 = numpy_random.random()
63
+ numpy_random.set_state(STATE.numpy_random)
64
+ r2 = numpy_random.random()
65
+ if r1 != r2:
66
+ raise RuntimeError("random seed in bad state")
67
+
68
+
69
+ @register_before_first_request("numpy.random")
70
+ def numpy_random_before_first_request(numpy_random):
71
+ numpy_random.seed()
72
+
73
+
74
+ @register_exec_patch("numpy.random.mtrand")
75
+ @contextmanager
76
+ def numpy_random_mtrand_context(module):
77
+ # numpy.random.mtrand calls secrets.randbits at top level to seed itself.
78
+ # This will fail if we don't let it through.
79
+ with allow_bad_entropy_calls(1):
80
+ yield
81
+ # Block calls until we get a chance to replace the bad random seed.
82
+ STATE.numpy_random = module.get_state()
83
+ block_calls(module, allowlist=("RandomState",))
84
+
85
+
86
+ @register_exec_patch("pydantic_core")
87
+ @contextmanager
88
+ def pydantic_core_context(module):
89
+ try:
90
+ # Initial import needs one entropy call to initialize
91
+ # std::collections::HashMap hash seed
92
+ with allow_bad_entropy_calls(1):
93
+ yield
94
+ finally:
95
+ try:
96
+ with allow_bad_entropy_calls(1):
97
+ # validate_core_schema makes an ahash::AHashMap which makes
98
+ # another entropy call for its hash seed. It will throw an error
99
+ # but only after making the needed entropy call.
100
+ module.validate_core_schema(None)
101
+ except module.SchemaError:
102
+ pass
103
+
104
+
105
+ @register_exec_patch("aiohttp.http_websocket")
106
+ @contextmanager
107
+ def aiohttp_http_websocket_context(module):
108
+ import random
109
+
110
+ Random = random.Random
111
+
112
+ def patched_Random():
113
+ return random
114
+
115
+ random.Random = patched_Random
116
+ try:
117
+ yield
118
+ finally:
119
+ random.Random = Random
120
+
121
+
122
+ class NoSslFinder:
123
+ def find_spec(self, fullname, path, target):
124
+ if fullname == "ssl":
125
+ raise ModuleNotFoundError(
126
+ f"No module named {fullname!r}", name=fullname
127
+ ) from None
128
+
129
+
130
+ @contextmanager
131
+ def no_ssl():
132
+ """
133
+ Various packages will call ssl.create_default_context() at top level which uses entropy if they
134
+ can import ssl. By temporarily making importing ssl raise an import error, we exercise the
135
+ workaround code and so avoid the entropy calls. After, we put the ssl module back to the normal
136
+ value.
137
+ """
138
+ try:
139
+ f = NoSslFinder()
140
+ ssl = sys.modules.pop("ssl", None)
141
+ sys.meta_path.insert(0, f)
142
+ yield
143
+ finally:
144
+ sys.meta_path.remove(f)
145
+ if ssl:
146
+ sys.modules["ssl"] = ssl
147
+
148
+
149
+ @register_exec_patch("aiohttp.connector")
150
+ @contextmanager
151
+ def aiohttp_connector_context(module):
152
+ with no_ssl():
153
+ yield
154
+
155
+
156
+ @register_exec_patch("requests.adapters")
157
+ @contextmanager
158
+ def requests_adapters_context(module):
159
+ with no_ssl():
160
+ yield
161
+
162
+
163
+ @register_exec_patch("urllib3.util.ssl_")
164
+ @contextmanager
165
+ def urllib3_util_ssl__context(module):
166
+ with no_ssl():
167
+ yield
168
+
169
+
170
+ @register_exec_patch("langsmith._internal._constants")
171
+ @contextmanager
172
+ def langsmith__internal__constants_context(module):
173
+ # Langsmith uses a UUID to communicate with a background thread. This obviously won't work so we
174
+ # might as well allow it to make a UUID.
175
+ with allow_bad_entropy_calls(1):
176
+ yield
177
+
178
+
179
+ @register_exec_patch("langchain_openai.chat_models.base")
180
+ @contextmanager
181
+ def langchain_openai_chat_models_base_context(module):
182
+ with allow_bad_entropy_calls(1):
183
+ yield
@@ -7,7 +7,7 @@ from typing import Any
7
7
 
8
8
  import js
9
9
 
10
- from workers import Context, Request, wait_until
10
+ from workers import Context, Request
11
11
 
12
12
  ASGI = {"spec_version": "2.0", "version": "3.0"}
13
13
  logger = logging.getLogger("asgi")
@@ -221,6 +221,9 @@ async def process_request(
221
221
 
222
222
  # Create task to run the application in the background
223
223
  app_task = create_proxy(create_task(run_app()))
224
+
225
+ from workers import wait_until
226
+
224
227
  wait_until(app_task)
225
228
 
226
229
  try:
@@ -210,7 +210,7 @@ wheels = [
210
210
 
211
211
  [[package]]
212
212
  name = "workers-runtime-sdk"
213
- version = "1.1.0"
213
+ version = "1.1.2"
214
214
  source = { editable = "." }
215
215
 
216
216
  [package.dev-dependencies]