workers-runtime-sdk 1.1.6__tar.gz → 1.3.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.1.6 → workers_runtime_sdk-1.3.0}/CHANGELOG.md +25 -0
- {workers_runtime_sdk-1.1.6 → workers_runtime_sdk-1.3.0}/PKG-INFO +1 -1
- {workers_runtime_sdk-1.1.6 → workers_runtime_sdk-1.3.0}/pyproject.toml +1 -1
- {workers_runtime_sdk-1.1.6 → workers_runtime_sdk-1.3.0}/src/workers/_workers.py +128 -13
- {workers_runtime_sdk-1.1.6 → workers_runtime_sdk-1.3.0}/.gitignore +0 -0
- {workers_runtime_sdk-1.1.6 → workers_runtime_sdk-1.3.0}/AGENTS.md +0 -0
- {workers_runtime_sdk-1.1.6 → workers_runtime_sdk-1.3.0}/README.md +0 -0
- {workers_runtime_sdk-1.1.6 → workers_runtime_sdk-1.3.0}/src/_cloudflare_compat_flags.pyi +0 -0
- {workers_runtime_sdk-1.1.6 → workers_runtime_sdk-1.3.0}/src/_pyodide_entrypoint_helper.pyi +0 -0
- {workers_runtime_sdk-1.1.6 → workers_runtime_sdk-1.3.0}/src/_workers_sdk_entropy_import_context.pth +0 -0
- {workers_runtime_sdk-1.1.6 → workers_runtime_sdk-1.3.0}/src/_workers_sdk_entropy_import_context.py +0 -0
- {workers_runtime_sdk-1.1.6 → workers_runtime_sdk-1.3.0}/src/_workers_sdk_entropy_import_context_loader.py +0 -0
- {workers_runtime_sdk-1.1.6 → workers_runtime_sdk-1.3.0}/src/asgi.py +0 -0
- {workers_runtime_sdk-1.1.6 → workers_runtime_sdk-1.3.0}/src/workers/__init__.py +0 -0
- {workers_runtime_sdk-1.1.6 → workers_runtime_sdk-1.3.0}/src/workers/py.typed +0 -0
- {workers_runtime_sdk-1.1.6 → workers_runtime_sdk-1.3.0}/src/workers/workflows.py +0 -0
- {workers_runtime_sdk-1.1.6 → workers_runtime_sdk-1.3.0}/uv.lock +0 -0
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- version list -->
|
|
4
4
|
|
|
5
|
+
## v1.3.0 (2026-06-15)
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
- **runtime-sdk**: Revise type conversion for Durable Object binding
|
|
10
|
+
([#112](https://github.com/cloudflare/workers-py/pull/112),
|
|
11
|
+
[`b12650e`](https://github.com/cloudflare/workers-py/commit/b12650ef91bb71f4ebebd9827bad2d1f0946fd62))
|
|
12
|
+
|
|
13
|
+
- **runtime-sdk**: Revise type conversion to support bindings more natively
|
|
14
|
+
([#112](https://github.com/cloudflare/workers-py/pull/112),
|
|
15
|
+
[`b12650e`](https://github.com/cloudflare/workers-py/commit/b12650ef91bb71f4ebebd9827bad2d1f0946fd62))
|
|
16
|
+
|
|
17
|
+
- **runtime-sdk**: Update js object conversion logic to support cloudflare bindings more natively.
|
|
18
|
+
([#112](https://github.com/cloudflare/workers-py/pull/112),
|
|
19
|
+
[`b12650e`](https://github.com/cloudflare/workers-py/commit/b12650ef91bb71f4ebebd9827bad2d1f0946fd62))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## v1.2.0 (2026-06-12)
|
|
23
|
+
|
|
24
|
+
### Features
|
|
25
|
+
|
|
26
|
+
- Implements cf accessor on Request
|
|
27
|
+
([`5777f80`](https://github.com/cloudflare/workers-py/commit/5777f80ead8d9a3c452fe3b6b8f2dc041d6c80d3))
|
|
28
|
+
|
|
29
|
+
|
|
5
30
|
## v1.1.6 (2026-05-28)
|
|
6
31
|
|
|
7
32
|
### Bug Fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: workers-runtime-sdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.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
|
|
@@ -769,6 +768,20 @@ class Request:
|
|
|
769
768
|
def body_used(self) -> bool:
|
|
770
769
|
return self.js_object.bodyUsed
|
|
771
770
|
|
|
771
|
+
@property
|
|
772
|
+
def cf(self) -> "JsProxy | None":
|
|
773
|
+
"""
|
|
774
|
+
Cloudflare-specific properties about the incoming request
|
|
775
|
+
(IncomingRequestCfProperties). Access fields via attribute
|
|
776
|
+
notation, for example ``request.cf.colo``.
|
|
777
|
+
|
|
778
|
+
Returns None when not present (for example, in the dashboard/playground
|
|
779
|
+
preview or for requests constructed without a ``cf`` value).
|
|
780
|
+
|
|
781
|
+
See https://developers.cloudflare.com/workers/runtime-apis/request/#incomingrequestcfproperties
|
|
782
|
+
"""
|
|
783
|
+
return _jsnull_to_none(self.js_object.cf)
|
|
784
|
+
|
|
772
785
|
@property
|
|
773
786
|
def cache(self) -> str:
|
|
774
787
|
return self.js_object.cache
|
|
@@ -902,6 +915,9 @@ class Request:
|
|
|
902
915
|
|
|
903
916
|
|
|
904
917
|
def _python_from_rpc_default_converter(value, convert, cache):
|
|
918
|
+
if value is jsnull:
|
|
919
|
+
return None
|
|
920
|
+
|
|
905
921
|
if not hasattr(value, "constructor"):
|
|
906
922
|
# Assume that the object doesn't need conversion as it's not a JS object.
|
|
907
923
|
return value
|
|
@@ -933,6 +949,41 @@ def _python_from_rpc_default_converter(value, convert, cache):
|
|
|
933
949
|
return value
|
|
934
950
|
|
|
935
951
|
|
|
952
|
+
class JsDict(dict):
|
|
953
|
+
"""
|
|
954
|
+
Python dictionary that allows attribute access to keys.
|
|
955
|
+
|
|
956
|
+
This is used to convert JS objects to Python dictionaries while maintaining
|
|
957
|
+
the ability to access keys as attributes.
|
|
958
|
+
"""
|
|
959
|
+
|
|
960
|
+
def __getattr__(self, name):
|
|
961
|
+
# The limitation of this approach is that if there is a key that conflicts with a built-in
|
|
962
|
+
# method or attribute of the dict class, it will not be accessible through attribute access.
|
|
963
|
+
# But that is a reasonable trade-off for the convenience of being able to access keys as
|
|
964
|
+
# attributes.
|
|
965
|
+
try:
|
|
966
|
+
return self[name]
|
|
967
|
+
except KeyError:
|
|
968
|
+
raise AttributeError(name) from None
|
|
969
|
+
|
|
970
|
+
def __setattr__(self, name, value):
|
|
971
|
+
self[name] = value
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
def _replace_jsnull_with_none(obj):
|
|
975
|
+
"""
|
|
976
|
+
Recursively converts JS objects to Python objects.
|
|
977
|
+
"""
|
|
978
|
+
if obj is jsnull:
|
|
979
|
+
return None
|
|
980
|
+
if isinstance(obj, dict):
|
|
981
|
+
return JsDict({k: _replace_jsnull_with_none(v) for k, v in obj.items()})
|
|
982
|
+
if isinstance(obj, list):
|
|
983
|
+
return [_replace_jsnull_with_none(v) for v in obj]
|
|
984
|
+
return obj
|
|
985
|
+
|
|
986
|
+
|
|
936
987
|
def python_from_rpc(obj: "JsProxy"):
|
|
937
988
|
"""
|
|
938
989
|
Converts JS objects like Response, Request, Blob, etc. to equivalent Python objects defined in
|
|
@@ -942,6 +993,9 @@ def python_from_rpc(obj: "JsProxy"):
|
|
|
942
993
|
it does not support serializing all JS object types.
|
|
943
994
|
"""
|
|
944
995
|
|
|
996
|
+
if obj is jsnull:
|
|
997
|
+
return None
|
|
998
|
+
|
|
945
999
|
if not hasattr(obj, "constructor"):
|
|
946
1000
|
return obj
|
|
947
1001
|
|
|
@@ -952,14 +1006,20 @@ def python_from_rpc(obj: "JsProxy"):
|
|
|
952
1006
|
|
|
953
1007
|
result = obj.to_py(default_converter=_python_from_rpc_default_converter)
|
|
954
1008
|
|
|
955
|
-
return result
|
|
1009
|
+
return _replace_jsnull_with_none(result)
|
|
956
1010
|
|
|
957
1011
|
|
|
958
1012
|
def _raise_on_disabled_type(value):
|
|
1013
|
+
if isinstance(value, _BindingWrapper):
|
|
1014
|
+
return
|
|
1015
|
+
|
|
1016
|
+
if callable(value) and not isinstance(value, type):
|
|
1017
|
+
return
|
|
1018
|
+
|
|
959
1019
|
if _is_js_instance(value, "RegExp"):
|
|
960
1020
|
raise TypeError(f"{value.constructor.name} cannot be sent over RPC.")
|
|
961
1021
|
|
|
962
|
-
if isinstance(value, (tuple, bytearray
|
|
1022
|
+
if isinstance(value, (tuple, bytearray)):
|
|
963
1023
|
raise TypeError(f"{type(value)} cannot be sent over RPC.")
|
|
964
1024
|
|
|
965
1025
|
if inspect.isawaitable(value):
|
|
@@ -977,7 +1037,10 @@ def _raise_on_disabled_type(value):
|
|
|
977
1037
|
|
|
978
1038
|
def _python_to_rpc_default_converter(obj, convert, cache):
|
|
979
1039
|
if obj is None:
|
|
980
|
-
return
|
|
1040
|
+
return jsnull
|
|
1041
|
+
|
|
1042
|
+
if isinstance(obj, _BindingWrapper):
|
|
1043
|
+
return obj._binding
|
|
981
1044
|
|
|
982
1045
|
if hasattr(obj, "js_object"):
|
|
983
1046
|
return obj.js_object
|
|
@@ -989,11 +1052,26 @@ def _python_to_rpc_default_converter(obj, convert, cache):
|
|
|
989
1052
|
if isinstance(obj, Exception):
|
|
990
1053
|
return js.Error.new(str(obj))
|
|
991
1054
|
|
|
1055
|
+
if callable(obj) and not isinstance(obj, type):
|
|
1056
|
+
# Wrap function with create_proxy so that
|
|
1057
|
+
# it doesn't get garbage collected
|
|
1058
|
+
return create_proxy(obj)
|
|
1059
|
+
|
|
992
1060
|
_raise_on_disabled_type(obj)
|
|
993
1061
|
|
|
994
1062
|
return obj
|
|
995
1063
|
|
|
996
1064
|
|
|
1065
|
+
def _replace_none_with_jsnull(value):
|
|
1066
|
+
if value is None:
|
|
1067
|
+
return jsnull
|
|
1068
|
+
if isinstance(value, dict):
|
|
1069
|
+
return {k: _replace_none_with_jsnull(v) for k, v in value.items()}
|
|
1070
|
+
if isinstance(value, list):
|
|
1071
|
+
return [_replace_none_with_jsnull(v) for v in value]
|
|
1072
|
+
return value
|
|
1073
|
+
|
|
1074
|
+
|
|
997
1075
|
def python_to_rpc(value) -> JsProxy:
|
|
998
1076
|
"""
|
|
999
1077
|
Converts Python objects defined in this module (Response, Request, etc) and native Python types
|
|
@@ -1003,37 +1081,58 @@ def python_to_rpc(value) -> JsProxy:
|
|
|
1003
1081
|
it does not support serializing all Python object types.
|
|
1004
1082
|
"""
|
|
1005
1083
|
|
|
1084
|
+
if value is None:
|
|
1085
|
+
return jsnull
|
|
1086
|
+
|
|
1087
|
+
if isinstance(value, _BindingWrapper):
|
|
1088
|
+
return value._binding
|
|
1089
|
+
|
|
1090
|
+
value = _replace_none_with_jsnull(value)
|
|
1091
|
+
|
|
1006
1092
|
# `to_js` won't always call the default_converter, for example when a list of tuples is passed
|
|
1007
1093
|
_raise_on_disabled_type(value)
|
|
1008
1094
|
|
|
1009
1095
|
result = to_js(
|
|
1010
1096
|
value,
|
|
1011
1097
|
default_converter=_python_to_rpc_default_converter,
|
|
1012
|
-
dict_converter=
|
|
1098
|
+
dict_converter=Object.fromEntries,
|
|
1013
1099
|
)
|
|
1014
1100
|
|
|
1015
1101
|
return result
|
|
1016
1102
|
|
|
1017
1103
|
|
|
1018
|
-
class
|
|
1104
|
+
class _BindingWrapper:
|
|
1019
1105
|
def __init__(self, binding):
|
|
1020
1106
|
self._binding = binding
|
|
1021
1107
|
|
|
1108
|
+
def _convert_result(self, result):
|
|
1109
|
+
converted = python_from_rpc(result)
|
|
1110
|
+
if isinstance(converted, JsProxy):
|
|
1111
|
+
# If the RPC result is another JsProxy, we assume that
|
|
1112
|
+
# it is another RPC-wrapped object and wrap it as well.
|
|
1113
|
+
# for example, d1.bind() returns the same object as a result.
|
|
1114
|
+
# TODO: This is a bit of a hack. We should revisit when there are more
|
|
1115
|
+
# bindings to support with different patterns.
|
|
1116
|
+
return self.__class__(converted)
|
|
1117
|
+
return converted
|
|
1118
|
+
|
|
1022
1119
|
def _getattr_helper(self, name):
|
|
1023
1120
|
attr = getattr(self._binding, name)
|
|
1024
1121
|
|
|
1025
1122
|
if not callable(attr):
|
|
1026
|
-
return attr
|
|
1123
|
+
return self._convert_result(attr)
|
|
1027
1124
|
|
|
1028
|
-
|
|
1029
|
-
async def wrapper(*args, **kwargs):
|
|
1125
|
+
def wrapper(*args, **kwargs):
|
|
1030
1126
|
js_args = [python_to_rpc(arg) for arg in args]
|
|
1031
1127
|
js_kwargs = {k: python_to_rpc(v) for k, v in kwargs.items()}
|
|
1032
1128
|
result = attr(*js_args, **js_kwargs)
|
|
1033
1129
|
if hasattr(result, "then") and callable(result.then):
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1130
|
+
|
|
1131
|
+
async def await_and_convert():
|
|
1132
|
+
return self._convert_result(await result)
|
|
1133
|
+
|
|
1134
|
+
return await_and_convert()
|
|
1135
|
+
return self._convert_result(result)
|
|
1037
1136
|
|
|
1038
1137
|
return wrapper
|
|
1039
1138
|
|
|
@@ -1042,6 +1141,11 @@ class _FetcherWrapper:
|
|
|
1042
1141
|
setattr(self, name, result)
|
|
1043
1142
|
return result
|
|
1044
1143
|
|
|
1144
|
+
def __getitem__(self, key):
|
|
1145
|
+
return self._convert_result(getattr(self._binding, key))
|
|
1146
|
+
|
|
1147
|
+
|
|
1148
|
+
class _FetcherWrapper(_BindingWrapper):
|
|
1045
1149
|
def fetch(self, *args, **kwargs):
|
|
1046
1150
|
return fetch(*args, fetcher=self._binding.fetch, **kwargs)
|
|
1047
1151
|
|
|
@@ -1075,6 +1179,9 @@ class DurableObjectContext:
|
|
|
1075
1179
|
|
|
1076
1180
|
def __getattr__(self, name: str):
|
|
1077
1181
|
result = getattr(self._ctx, name)
|
|
1182
|
+
if _is_js_instance(result, "DurableObjectStorage"):
|
|
1183
|
+
# durable_object.ctx.storage
|
|
1184
|
+
result = _BindingWrapper(result)
|
|
1078
1185
|
setattr(self, name, result)
|
|
1079
1186
|
return result
|
|
1080
1187
|
|
|
@@ -1159,7 +1266,15 @@ class _EnvWrapper:
|
|
|
1159
1266
|
if _is_js_instance(binding, "WorkflowImpl"):
|
|
1160
1267
|
return _WorkflowBindingWrapper(binding)
|
|
1161
1268
|
|
|
1162
|
-
|
|
1269
|
+
if _is_js_instance(binding, "KvNamespace"):
|
|
1270
|
+
return _BindingWrapper(binding)
|
|
1271
|
+
|
|
1272
|
+
if _is_js_instance(binding, "R2Bucket"):
|
|
1273
|
+
return _BindingWrapper(binding)
|
|
1274
|
+
|
|
1275
|
+
if _is_js_instance(binding, "D1Database"):
|
|
1276
|
+
return _BindingWrapper(binding)
|
|
1277
|
+
|
|
1163
1278
|
return binding
|
|
1164
1279
|
|
|
1165
1280
|
def __getattr__(self, name):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{workers_runtime_sdk-1.1.6 → workers_runtime_sdk-1.3.0}/src/_workers_sdk_entropy_import_context.pth
RENAMED
|
File without changes
|
{workers_runtime_sdk-1.1.6 → workers_runtime_sdk-1.3.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
|