workers-runtime-sdk 1.4.0__tar.gz → 1.4.2__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.
Files changed (17) hide show
  1. {workers_runtime_sdk-1.4.0 → workers_runtime_sdk-1.4.2}/CHANGELOG.md +18 -0
  2. {workers_runtime_sdk-1.4.0 → workers_runtime_sdk-1.4.2}/PKG-INFO +1 -1
  3. {workers_runtime_sdk-1.4.0 → workers_runtime_sdk-1.4.2}/pyproject.toml +1 -1
  4. {workers_runtime_sdk-1.4.0 → workers_runtime_sdk-1.4.2}/src/workers/_workers.py +56 -12
  5. {workers_runtime_sdk-1.4.0 → workers_runtime_sdk-1.4.2}/.gitignore +0 -0
  6. {workers_runtime_sdk-1.4.0 → workers_runtime_sdk-1.4.2}/AGENTS.md +0 -0
  7. {workers_runtime_sdk-1.4.0 → workers_runtime_sdk-1.4.2}/README.md +0 -0
  8. {workers_runtime_sdk-1.4.0 → workers_runtime_sdk-1.4.2}/src/_cloudflare_compat_flags.pyi +0 -0
  9. {workers_runtime_sdk-1.4.0 → workers_runtime_sdk-1.4.2}/src/_pyodide_entrypoint_helper.pyi +0 -0
  10. {workers_runtime_sdk-1.4.0 → workers_runtime_sdk-1.4.2}/src/_workers_sdk_entropy_import_context.pth +0 -0
  11. {workers_runtime_sdk-1.4.0 → workers_runtime_sdk-1.4.2}/src/_workers_sdk_entropy_import_context.py +0 -0
  12. {workers_runtime_sdk-1.4.0 → workers_runtime_sdk-1.4.2}/src/_workers_sdk_entropy_import_context_loader.py +0 -0
  13. {workers_runtime_sdk-1.4.0 → workers_runtime_sdk-1.4.2}/src/asgi.py +0 -0
  14. {workers_runtime_sdk-1.4.0 → workers_runtime_sdk-1.4.2}/src/workers/__init__.py +0 -0
  15. {workers_runtime_sdk-1.4.0 → workers_runtime_sdk-1.4.2}/src/workers/py.typed +0 -0
  16. {workers_runtime_sdk-1.4.0 → workers_runtime_sdk-1.4.2}/src/workers/workflows.py +0 -0
  17. {workers_runtime_sdk-1.4.0 → workers_runtime_sdk-1.4.2}/uv.lock +0 -0
@@ -2,6 +2,24 @@
2
2
 
3
3
  <!-- version list -->
4
4
 
5
+ ## v1.4.2 (2026-06-18)
6
+
7
+ ### Bug Fixes
8
+
9
+ - Make iterables work correctly when returned from rpc call
10
+ ([#127](https://github.com/cloudflare/workers-py/pull/127),
11
+ [`36dc659`](https://github.com/cloudflare/workers-py/commit/36dc659d6ba394d75e3d11b3e78e2b08fcd91c9f))
12
+
13
+
14
+ ## v1.4.1 (2026-06-18)
15
+
16
+ ### Bug Fixes
17
+
18
+ - Fix ReadableStream being incorrectly wrapped by BindingWrapper
19
+ ([#128](https://github.com/cloudflare/workers-py/pull/128),
20
+ [`85ad1f3`](https://github.com/cloudflare/workers-py/commit/85ad1f33d5f23fd932c0eac5cc5a9f7d39159423))
21
+
22
+
5
23
  ## v1.4.0 (2026-06-17)
6
24
 
7
25
  ### Features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: workers-runtime-sdk
3
- Version: 1.4.0
3
+ Version: 1.4.2
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.4.0"
7
+ version = "1.4.2"
8
8
  description = "Python SDK for Cloudflare Workers"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -392,6 +392,19 @@ RESPONSE_ACCEPTED_TYPES = {
392
392
  "Response",
393
393
  }
394
394
 
395
+ # JS built-in types that should NOT be wrapped in _BindingWrapper.
396
+ # These have their own Python-side semantics (e.g. passed directly to Response())
397
+ # and wrapping them breaks property access like `.constructor.name`.
398
+ _JS_PASSTHROUGH_TYPES = RESPONSE_ACCEPTED_TYPES | {
399
+ "Headers",
400
+ }
401
+
402
+
403
+ def _get_js_constructor_name(obj) -> str | None:
404
+ if hasattr(obj, "constructor"):
405
+ return obj.constructor.name
406
+ return None
407
+
395
408
 
396
409
  class Response(FetchResponse):
397
410
  """
@@ -414,11 +427,10 @@ class Response(FetchResponse):
414
427
  https://developer.mozilla.org/en-US/docs/Web/API/Response/Response.
415
428
  """
416
429
  # Verify passed in types.
417
- if hasattr(body, "constructor"):
418
- if body.constructor.name not in RESPONSE_ACCEPTED_TYPES:
419
- raise TypeError(
420
- f"Unsupported type in Response: {body.constructor.name}"
421
- )
430
+ js_type = _get_js_constructor_name(body)
431
+ if js_type:
432
+ if js_type not in RESPONSE_ACCEPTED_TYPES:
433
+ raise TypeError(f"Unsupported type in Response: {js_type}")
422
434
  elif not isinstance(body, str | FormData | bytes) and body is not None:
423
435
  raise TypeError(f"Unsupported type in Response: {type(body).__name__}")
424
436
 
@@ -1110,20 +1122,37 @@ class _BindingWrapper:
1110
1122
  def __init__(self, binding):
1111
1123
  self._binding = binding
1112
1124
 
1125
+ @property
1126
+ def _real_name(self):
1127
+ js_name = _get_js_constructor_name(self._binding)
1128
+ if not js_name:
1129
+ # Should not happen, but just in case
1130
+ return type(self).__name__
1131
+ return js_name
1132
+
1133
+ def _should_wrap_nested_attribute(self, jsobj) -> bool:
1134
+ if not isinstance(jsobj, JsProxy):
1135
+ return False
1136
+
1137
+ # TODO: This allowlist approach is a workaround. The long-term fix is to
1138
+ # add dedicated Python wrappers for these types in python_from_rpc so they
1139
+ # never reach _BindingWrapper in the first place.
1140
+ js_type = _get_js_constructor_name(jsobj)
1141
+ return js_type and js_type not in _JS_PASSTHROUGH_TYPES
1142
+
1113
1143
  def _convert_result(self, result):
1114
1144
  converted = python_from_rpc(result)
1115
1145
 
1116
1146
  # After python_from_rpc, some objects may still be JsProxy objects.
1117
- # For now, we wrap all of them with the _BindingWrapper (or a subclass of it)
1118
- # so that accessing attributes on them will be properly converted.
1119
-
1120
- # TODO: This is a bit of a hack. We should revisit when there are more
1121
- # bindings to support with different return types.
1122
- if isinstance(converted, JsProxy):
1147
+ # We need to wrap them with _BindingWrapper (or a subclass of it) again
1148
+ # to ensure that accessing attributes on them will be properly converted.
1149
+ if self._should_wrap_nested_attribute(converted):
1123
1150
  return self.__class__(converted)
1124
1151
  if isinstance(converted, list):
1125
1152
  return [
1126
- self.__class__(item) if isinstance(item, JsProxy) else item
1153
+ self.__class__(item)
1154
+ if self._should_wrap_nested_attribute(item)
1155
+ else item
1127
1156
  for item in converted
1128
1157
  ]
1129
1158
  return converted
@@ -1154,8 +1183,23 @@ class _BindingWrapper:
1154
1183
  return result
1155
1184
 
1156
1185
  def __getitem__(self, key):
1186
+ if isinstance(key, int):
1187
+ return self._convert_result(self._binding[key])
1157
1188
  return self._convert_result(getattr(self._binding, key))
1158
1189
 
1190
+ def __iter__(self):
1191
+ binding = self._binding
1192
+ if not hasattr(binding, "__iter__"):
1193
+ raise TypeError(f"'{self._real_name}' object is not iterable")
1194
+ for item in binding:
1195
+ yield self._convert_result(item)
1196
+
1197
+ def __len__(self):
1198
+ binding = self._binding
1199
+ if not hasattr(binding, "length"):
1200
+ raise TypeError(f"'{self._real_name}' object has no len()")
1201
+ return binding.length
1202
+
1159
1203
 
1160
1204
  class _FetcherWrapper(_BindingWrapper):
1161
1205
  def fetch(self, *args, **kwargs):