workers-runtime-sdk 1.2.0__tar.gz → 1.4.0__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.
- {workers_runtime_sdk-1.2.0 → workers_runtime_sdk-1.4.0}/CHANGELOG.md +30 -0
- {workers_runtime_sdk-1.2.0 → workers_runtime_sdk-1.4.0}/PKG-INFO +1 -1
- {workers_runtime_sdk-1.2.0 → workers_runtime_sdk-1.4.0}/pyproject.toml +1 -1
- {workers_runtime_sdk-1.2.0 → workers_runtime_sdk-1.4.0}/src/workers/_workers.py +146 -15
- {workers_runtime_sdk-1.2.0 → workers_runtime_sdk-1.4.0}/.gitignore +0 -0
- {workers_runtime_sdk-1.2.0 → workers_runtime_sdk-1.4.0}/AGENTS.md +0 -0
- {workers_runtime_sdk-1.2.0 → workers_runtime_sdk-1.4.0}/README.md +0 -0
- {workers_runtime_sdk-1.2.0 → workers_runtime_sdk-1.4.0}/src/_cloudflare_compat_flags.pyi +0 -0
- {workers_runtime_sdk-1.2.0 → workers_runtime_sdk-1.4.0}/src/_pyodide_entrypoint_helper.pyi +0 -0
- {workers_runtime_sdk-1.2.0 → workers_runtime_sdk-1.4.0}/src/_workers_sdk_entropy_import_context.pth +0 -0
- {workers_runtime_sdk-1.2.0 → workers_runtime_sdk-1.4.0}/src/_workers_sdk_entropy_import_context.py +0 -0
- {workers_runtime_sdk-1.2.0 → workers_runtime_sdk-1.4.0}/src/_workers_sdk_entropy_import_context_loader.py +0 -0
- {workers_runtime_sdk-1.2.0 → workers_runtime_sdk-1.4.0}/src/asgi.py +0 -0
- {workers_runtime_sdk-1.2.0 → workers_runtime_sdk-1.4.0}/src/workers/__init__.py +0 -0
- {workers_runtime_sdk-1.2.0 → workers_runtime_sdk-1.4.0}/src/workers/py.typed +0 -0
- {workers_runtime_sdk-1.2.0 → workers_runtime_sdk-1.4.0}/src/workers/workflows.py +0 -0
- {workers_runtime_sdk-1.2.0 → workers_runtime_sdk-1.4.0}/uv.lock +0 -0
|
@@ -2,6 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- version list -->
|
|
4
4
|
|
|
5
|
+
## v1.4.0 (2026-06-17)
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
- Auto-convert Python objects that are passed to/from Queue
|
|
10
|
+
([#123](https://github.com/cloudflare/workers-py/pull/123),
|
|
11
|
+
[`906a10a`](https://github.com/cloudflare/workers-py/commit/906a10a7392f9d823a1b6bba044300ece8763401))
|
|
12
|
+
|
|
13
|
+
- Auto-convert Python objects that are passed to/from Queue Binding
|
|
14
|
+
([#123](https://github.com/cloudflare/workers-py/pull/123),
|
|
15
|
+
[`906a10a`](https://github.com/cloudflare/workers-py/commit/906a10a7392f9d823a1b6bba044300ece8763401))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## v1.3.0 (2026-06-15)
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
- **runtime-sdk**: Revise type conversion for Durable Object binding
|
|
23
|
+
([#112](https://github.com/cloudflare/workers-py/pull/112),
|
|
24
|
+
[`b12650e`](https://github.com/cloudflare/workers-py/commit/b12650ef91bb71f4ebebd9827bad2d1f0946fd62))
|
|
25
|
+
|
|
26
|
+
- **runtime-sdk**: Revise type conversion to support bindings more natively
|
|
27
|
+
([#112](https://github.com/cloudflare/workers-py/pull/112),
|
|
28
|
+
[`b12650e`](https://github.com/cloudflare/workers-py/commit/b12650ef91bb71f4ebebd9827bad2d1f0946fd62))
|
|
29
|
+
|
|
30
|
+
- **runtime-sdk**: Update js object conversion logic to support cloudflare bindings more natively.
|
|
31
|
+
([#112](https://github.com/cloudflare/workers-py/pull/112),
|
|
32
|
+
[`b12650e`](https://github.com/cloudflare/workers-py/commit/b12650ef91bb71f4ebebd9827bad2d1f0946fd62))
|
|
33
|
+
|
|
34
|
+
|
|
5
35
|
## v1.2.0 (2026-06-12)
|
|
6
36
|
|
|
7
37
|
### Features
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: workers-runtime-sdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
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
|
|
@@ -17,7 +17,6 @@ from collections.abc import (
|
|
|
17
17
|
from contextlib import ExitStack, contextmanager
|
|
18
18
|
from enum import StrEnum
|
|
19
19
|
from http import HTTPMethod, HTTPStatus
|
|
20
|
-
from types import LambdaType
|
|
21
20
|
from typing import TYPE_CHECKING, Any, Never, Protocol, TypedDict, Unpack
|
|
22
21
|
|
|
23
22
|
import _cloudflare_compat_flags
|
|
@@ -320,8 +319,13 @@ def _manage_pyproxies():
|
|
|
320
319
|
destroy_proxies(proxies)
|
|
321
320
|
|
|
322
321
|
|
|
323
|
-
def _is_js_instance(val,
|
|
324
|
-
|
|
322
|
+
def _is_js_instance(val, js_cls_names: str | set[str]):
|
|
323
|
+
if not hasattr(val, "constructor"):
|
|
324
|
+
return False
|
|
325
|
+
name = val.constructor.name
|
|
326
|
+
if isinstance(js_cls_names, set):
|
|
327
|
+
return name in js_cls_names
|
|
328
|
+
return name == js_cls_names
|
|
325
329
|
|
|
326
330
|
|
|
327
331
|
try:
|
|
@@ -916,6 +920,9 @@ class Request:
|
|
|
916
920
|
|
|
917
921
|
|
|
918
922
|
def _python_from_rpc_default_converter(value, convert, cache):
|
|
923
|
+
if value is jsnull:
|
|
924
|
+
return None
|
|
925
|
+
|
|
919
926
|
if not hasattr(value, "constructor"):
|
|
920
927
|
# Assume that the object doesn't need conversion as it's not a JS object.
|
|
921
928
|
return value
|
|
@@ -947,6 +954,41 @@ def _python_from_rpc_default_converter(value, convert, cache):
|
|
|
947
954
|
return value
|
|
948
955
|
|
|
949
956
|
|
|
957
|
+
class JsDict(dict):
|
|
958
|
+
"""
|
|
959
|
+
Python dictionary that allows attribute access to keys.
|
|
960
|
+
|
|
961
|
+
This is used to convert JS objects to Python dictionaries while maintaining
|
|
962
|
+
the ability to access keys as attributes.
|
|
963
|
+
"""
|
|
964
|
+
|
|
965
|
+
def __getattr__(self, name):
|
|
966
|
+
# The limitation of this approach is that if there is a key that conflicts with a built-in
|
|
967
|
+
# method or attribute of the dict class, it will not be accessible through attribute access.
|
|
968
|
+
# But that is a reasonable trade-off for the convenience of being able to access keys as
|
|
969
|
+
# attributes.
|
|
970
|
+
try:
|
|
971
|
+
return self[name]
|
|
972
|
+
except KeyError:
|
|
973
|
+
raise AttributeError(name) from None
|
|
974
|
+
|
|
975
|
+
def __setattr__(self, name, value):
|
|
976
|
+
self[name] = value
|
|
977
|
+
|
|
978
|
+
|
|
979
|
+
def _replace_jsnull_with_none(obj):
|
|
980
|
+
"""
|
|
981
|
+
Recursively converts JS objects to Python objects.
|
|
982
|
+
"""
|
|
983
|
+
if obj is jsnull:
|
|
984
|
+
return None
|
|
985
|
+
if isinstance(obj, dict):
|
|
986
|
+
return JsDict({k: _replace_jsnull_with_none(v) for k, v in obj.items()})
|
|
987
|
+
if isinstance(obj, list):
|
|
988
|
+
return [_replace_jsnull_with_none(v) for v in obj]
|
|
989
|
+
return obj
|
|
990
|
+
|
|
991
|
+
|
|
950
992
|
def python_from_rpc(obj: "JsProxy"):
|
|
951
993
|
"""
|
|
952
994
|
Converts JS objects like Response, Request, Blob, etc. to equivalent Python objects defined in
|
|
@@ -956,6 +998,9 @@ def python_from_rpc(obj: "JsProxy"):
|
|
|
956
998
|
it does not support serializing all JS object types.
|
|
957
999
|
"""
|
|
958
1000
|
|
|
1001
|
+
if obj is jsnull:
|
|
1002
|
+
return None
|
|
1003
|
+
|
|
959
1004
|
if not hasattr(obj, "constructor"):
|
|
960
1005
|
return obj
|
|
961
1006
|
|
|
@@ -966,14 +1011,20 @@ def python_from_rpc(obj: "JsProxy"):
|
|
|
966
1011
|
|
|
967
1012
|
result = obj.to_py(default_converter=_python_from_rpc_default_converter)
|
|
968
1013
|
|
|
969
|
-
return result
|
|
1014
|
+
return _replace_jsnull_with_none(result)
|
|
970
1015
|
|
|
971
1016
|
|
|
972
1017
|
def _raise_on_disabled_type(value):
|
|
1018
|
+
if isinstance(value, _BindingWrapper):
|
|
1019
|
+
return
|
|
1020
|
+
|
|
1021
|
+
if callable(value) and not isinstance(value, type):
|
|
1022
|
+
return
|
|
1023
|
+
|
|
973
1024
|
if _is_js_instance(value, "RegExp"):
|
|
974
1025
|
raise TypeError(f"{value.constructor.name} cannot be sent over RPC.")
|
|
975
1026
|
|
|
976
|
-
if isinstance(value, (tuple, bytearray
|
|
1027
|
+
if isinstance(value, (tuple, bytearray)):
|
|
977
1028
|
raise TypeError(f"{type(value)} cannot be sent over RPC.")
|
|
978
1029
|
|
|
979
1030
|
if inspect.isawaitable(value):
|
|
@@ -991,7 +1042,10 @@ def _raise_on_disabled_type(value):
|
|
|
991
1042
|
|
|
992
1043
|
def _python_to_rpc_default_converter(obj, convert, cache):
|
|
993
1044
|
if obj is None:
|
|
994
|
-
return
|
|
1045
|
+
return jsnull
|
|
1046
|
+
|
|
1047
|
+
if isinstance(obj, _BindingWrapper):
|
|
1048
|
+
return obj._binding
|
|
995
1049
|
|
|
996
1050
|
if hasattr(obj, "js_object"):
|
|
997
1051
|
return obj.js_object
|
|
@@ -1003,11 +1057,26 @@ def _python_to_rpc_default_converter(obj, convert, cache):
|
|
|
1003
1057
|
if isinstance(obj, Exception):
|
|
1004
1058
|
return js.Error.new(str(obj))
|
|
1005
1059
|
|
|
1060
|
+
if callable(obj) and not isinstance(obj, type):
|
|
1061
|
+
# Wrap function with create_proxy so that
|
|
1062
|
+
# it doesn't get garbage collected
|
|
1063
|
+
return create_proxy(obj)
|
|
1064
|
+
|
|
1006
1065
|
_raise_on_disabled_type(obj)
|
|
1007
1066
|
|
|
1008
1067
|
return obj
|
|
1009
1068
|
|
|
1010
1069
|
|
|
1070
|
+
def _replace_none_with_jsnull(value):
|
|
1071
|
+
if value is None:
|
|
1072
|
+
return jsnull
|
|
1073
|
+
if isinstance(value, dict):
|
|
1074
|
+
return {k: _replace_none_with_jsnull(v) for k, v in value.items()}
|
|
1075
|
+
if isinstance(value, list):
|
|
1076
|
+
return [_replace_none_with_jsnull(v) for v in value]
|
|
1077
|
+
return value
|
|
1078
|
+
|
|
1079
|
+
|
|
1011
1080
|
def python_to_rpc(value) -> JsProxy:
|
|
1012
1081
|
"""
|
|
1013
1082
|
Converts Python objects defined in this module (Response, Request, etc) and native Python types
|
|
@@ -1017,37 +1086,65 @@ def python_to_rpc(value) -> JsProxy:
|
|
|
1017
1086
|
it does not support serializing all Python object types.
|
|
1018
1087
|
"""
|
|
1019
1088
|
|
|
1089
|
+
if value is None:
|
|
1090
|
+
return jsnull
|
|
1091
|
+
|
|
1092
|
+
if isinstance(value, _BindingWrapper):
|
|
1093
|
+
return value._binding
|
|
1094
|
+
|
|
1095
|
+
value = _replace_none_with_jsnull(value)
|
|
1096
|
+
|
|
1020
1097
|
# `to_js` won't always call the default_converter, for example when a list of tuples is passed
|
|
1021
1098
|
_raise_on_disabled_type(value)
|
|
1022
1099
|
|
|
1023
1100
|
result = to_js(
|
|
1024
1101
|
value,
|
|
1025
1102
|
default_converter=_python_to_rpc_default_converter,
|
|
1026
|
-
dict_converter=
|
|
1103
|
+
dict_converter=Object.fromEntries,
|
|
1027
1104
|
)
|
|
1028
1105
|
|
|
1029
1106
|
return result
|
|
1030
1107
|
|
|
1031
1108
|
|
|
1032
|
-
class
|
|
1109
|
+
class _BindingWrapper:
|
|
1033
1110
|
def __init__(self, binding):
|
|
1034
1111
|
self._binding = binding
|
|
1035
1112
|
|
|
1113
|
+
def _convert_result(self, result):
|
|
1114
|
+
converted = python_from_rpc(result)
|
|
1115
|
+
|
|
1116
|
+
# 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):
|
|
1123
|
+
return self.__class__(converted)
|
|
1124
|
+
if isinstance(converted, list):
|
|
1125
|
+
return [
|
|
1126
|
+
self.__class__(item) if isinstance(item, JsProxy) else item
|
|
1127
|
+
for item in converted
|
|
1128
|
+
]
|
|
1129
|
+
return converted
|
|
1130
|
+
|
|
1036
1131
|
def _getattr_helper(self, name):
|
|
1037
1132
|
attr = getattr(self._binding, name)
|
|
1038
1133
|
|
|
1039
1134
|
if not callable(attr):
|
|
1040
|
-
return attr
|
|
1135
|
+
return self._convert_result(attr)
|
|
1041
1136
|
|
|
1042
|
-
|
|
1043
|
-
async def wrapper(*args, **kwargs):
|
|
1137
|
+
def wrapper(*args, **kwargs):
|
|
1044
1138
|
js_args = [python_to_rpc(arg) for arg in args]
|
|
1045
1139
|
js_kwargs = {k: python_to_rpc(v) for k, v in kwargs.items()}
|
|
1046
1140
|
result = attr(*js_args, **js_kwargs)
|
|
1047
1141
|
if hasattr(result, "then") and callable(result.then):
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1142
|
+
|
|
1143
|
+
async def await_and_convert():
|
|
1144
|
+
return self._convert_result(await result)
|
|
1145
|
+
|
|
1146
|
+
return await_and_convert()
|
|
1147
|
+
return self._convert_result(result)
|
|
1051
1148
|
|
|
1052
1149
|
return wrapper
|
|
1053
1150
|
|
|
@@ -1056,6 +1153,11 @@ class _FetcherWrapper:
|
|
|
1056
1153
|
setattr(self, name, result)
|
|
1057
1154
|
return result
|
|
1058
1155
|
|
|
1156
|
+
def __getitem__(self, key):
|
|
1157
|
+
return self._convert_result(getattr(self._binding, key))
|
|
1158
|
+
|
|
1159
|
+
|
|
1160
|
+
class _FetcherWrapper(_BindingWrapper):
|
|
1059
1161
|
def fetch(self, *args, **kwargs):
|
|
1060
1162
|
return fetch(*args, fetcher=self._binding.fetch, **kwargs)
|
|
1061
1163
|
|
|
@@ -1089,6 +1191,9 @@ class DurableObjectContext:
|
|
|
1089
1191
|
|
|
1090
1192
|
def __getattr__(self, name: str):
|
|
1091
1193
|
result = getattr(self._ctx, name)
|
|
1194
|
+
if _is_js_instance(result, "DurableObjectStorage"):
|
|
1195
|
+
# durable_object.ctx.storage
|
|
1196
|
+
result = _BindingWrapper(result)
|
|
1092
1197
|
setattr(self, name, result)
|
|
1093
1198
|
return result
|
|
1094
1199
|
|
|
@@ -1159,6 +1264,13 @@ class _WorkflowBindingWrapper:
|
|
|
1159
1264
|
|
|
1160
1265
|
|
|
1161
1266
|
class _EnvWrapper:
|
|
1267
|
+
_BINDING_TYPES = {
|
|
1268
|
+
"KvNamespace",
|
|
1269
|
+
"R2Bucket",
|
|
1270
|
+
"D1Database",
|
|
1271
|
+
"WorkerQueue",
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1162
1274
|
def __init__(self, env: Any):
|
|
1163
1275
|
self._env = env
|
|
1164
1276
|
|
|
@@ -1173,7 +1285,9 @@ class _EnvWrapper:
|
|
|
1173
1285
|
if _is_js_instance(binding, "WorkflowImpl"):
|
|
1174
1286
|
return _WorkflowBindingWrapper(binding)
|
|
1175
1287
|
|
|
1176
|
-
|
|
1288
|
+
if _is_js_instance(binding, self._BINDING_TYPES):
|
|
1289
|
+
return _BindingWrapper(binding)
|
|
1290
|
+
|
|
1177
1291
|
return binding
|
|
1178
1292
|
|
|
1179
1293
|
def __getattr__(self, name):
|
|
@@ -1449,6 +1563,7 @@ class WorkerEntrypoint:
|
|
|
1449
1563
|
|
|
1450
1564
|
def __init_subclass__(cls, **_kwargs: Any):
|
|
1451
1565
|
_wrap_subclass(cls)
|
|
1566
|
+
_wrap_queue_handler(cls)
|
|
1452
1567
|
|
|
1453
1568
|
|
|
1454
1569
|
class WorkflowEntrypoint:
|
|
@@ -1466,3 +1581,19 @@ class WorkflowEntrypoint:
|
|
|
1466
1581
|
def __init_subclass__(cls, **_kwargs: Any):
|
|
1467
1582
|
_wrap_subclass(cls)
|
|
1468
1583
|
_wrap_workflow_step(cls)
|
|
1584
|
+
|
|
1585
|
+
|
|
1586
|
+
def _wrap_queue_handler(cls):
|
|
1587
|
+
queue_fn = getattr(cls, "queue", None)
|
|
1588
|
+
if queue_fn is None:
|
|
1589
|
+
return
|
|
1590
|
+
|
|
1591
|
+
@functools.wraps(queue_fn)
|
|
1592
|
+
async def wrapped_queue(self, batch, *args, **kwargs):
|
|
1593
|
+
wrapped_batch = _BindingWrapper(batch)
|
|
1594
|
+
result = queue_fn(self, wrapped_batch, *args, **kwargs)
|
|
1595
|
+
if inspect.iscoroutine(result):
|
|
1596
|
+
result = await result
|
|
1597
|
+
return result
|
|
1598
|
+
|
|
1599
|
+
cls.queue = wrapped_queue
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{workers_runtime_sdk-1.2.0 → workers_runtime_sdk-1.4.0}/src/_workers_sdk_entropy_import_context.pth
RENAMED
|
File without changes
|
{workers_runtime_sdk-1.2.0 → workers_runtime_sdk-1.4.0}/src/_workers_sdk_entropy_import_context.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|