workers-runtime-sdk 1.8.6__tar.gz → 1.9.1__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.8.6 → workers_runtime_sdk-1.9.1}/CHANGELOG.md +22 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/PKG-INFO +1 -1
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/pyproject.toml +1 -1
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/_workers_sdk_entropy_import_context.py +1 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/workers/entrypoints.py +22 -2
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/workers/utils.py +35 -1
- workers_runtime_sdk-1.9.1/src/workers/workflows.js +91 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/pyproject.toml +7 -2
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/test_workflow.py +11 -1
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/worker_workflow.py +20 -4
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/conftest.py +11 -23
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/test_in_workerd.py +23 -19
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/pyproject.toml +3 -1
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/pyproject.toml +3 -1
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/src/worker.py +4 -11
- workers_runtime_sdk-1.9.1/tests/web-frameworks-test/flask-tests/pyproject.toml +9 -0
- workers_runtime_sdk-1.9.1/tests/workerd-test/asgi/pyproject.toml +9 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/asgi/worker.py +2 -20
- workers_runtime_sdk-1.9.1/tests/workerd-test/asgi-ws-disconnect/pyproject.toml +9 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/asgi-ws-disconnect/worker.py +2 -19
- workers_runtime_sdk-1.9.1/tests/workerd-test/durable-object/pyproject.toml +8 -0
- workers_runtime_sdk-1.9.1/tests/workerd-test/durable-object-abort/pyproject.toml +8 -0
- workers_runtime_sdk-1.9.1/tests/workerd-test/durable-object-inheritance/pyproject.toml +8 -0
- workers_runtime_sdk-1.9.1/tests/workerd-test/durable-object-websocket/pyproject.toml +8 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/entropy-patches/pyproject.toml +5 -0
- workers_runtime_sdk-1.9.1/tests/workerd-test/entropy-patches/tests/test_jsonschema.py +9 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/entropy-patches/worker.py +1 -0
- workers_runtime_sdk-1.9.1/tests/workerd-test/http-client/pyproject.toml +8 -0
- workers_runtime_sdk-1.9.1/tests/workerd-test/python-rpc/pyproject.toml +8 -0
- workers_runtime_sdk-1.9.1/tests/workerd-test/sdk/pyproject.toml +9 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/sdk/worker.py +2 -21
- workers_runtime_sdk-1.9.1/tests/workerd-test/workflow/worker.js +112 -0
- workers_runtime_sdk-1.9.1/tests/workerd-test/workflow/worker.py +72 -0
- workers_runtime_sdk-1.9.1/tests/workerd-test/workflow/workflow.wd-test +30 -0
- workers_runtime_sdk-1.9.1/tests/workerd-test/workflow/wrangler.jsonc +5 -0
- workers_runtime_sdk-1.9.1/tests/workerd-test/wsgi/pyproject.toml +9 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/wsgi/worker.py +2 -20
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/uv.lock +12 -1
- workers_runtime_sdk-1.8.6/tests/web-frameworks-test/flask-tests/pyproject.toml +0 -8
- workers_runtime_sdk-1.8.6/tests/workerd-test/asgi/pyproject.toml +0 -5
- workers_runtime_sdk-1.8.6/tests/workerd-test/asgi-ws-disconnect/pyproject.toml +0 -5
- workers_runtime_sdk-1.8.6/tests/workerd-test/durable-object/pyproject.toml +0 -5
- workers_runtime_sdk-1.8.6/tests/workerd-test/durable-object-inheritance/pyproject.toml +0 -5
- workers_runtime_sdk-1.8.6/tests/workerd-test/durable-object-websocket/pyproject.toml +0 -5
- workers_runtime_sdk-1.8.6/tests/workerd-test/http-client/pyproject.toml +0 -5
- workers_runtime_sdk-1.8.6/tests/workerd-test/python-rpc/pyproject.toml +0 -5
- workers_runtime_sdk-1.8.6/tests/workerd-test/sdk/pyproject.toml +0 -5
- workers_runtime_sdk-1.8.6/tests/workerd-test/wsgi/pyproject.toml +0 -5
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/.gitignore +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/AGENTS.md +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/README.md +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/_cloudflare_compat_flags.pyi +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/_pyodide_entrypoint_helper.pyi +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/_workers_sdk_package_patches.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/_workers_sdk_patch_loader.pth +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/_workers_sdk_patch_loader.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/asgi.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/workers/__init__.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/workers/asgi.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/workers/blob.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/workers/fetch.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/workers/formdata.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/workers/py.typed +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/workers/request.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/workers/response.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/workers/rpc.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/workers/types.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/workers/workflows.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/workers/wsgi.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/public/data.json +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/public/hello.txt +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/conftest.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/test_analytics_engine.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/test_assets.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/test_d1.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/test_do.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/test_hyperdrive_mysql.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/test_hyperdrive_postgresql.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/test_images.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/test_kv.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/test_mtls.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/test_queue.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/test_r2.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/test_ratelimit.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/test_vectorize.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/test_version_metadata.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/worker.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/worker_durable_object.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/test_bindings.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/test_fastapi.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/test_flask.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/test_web_frameworks.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/_client.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/conftest.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/django_app/__init__.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/django_app/api_urls.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/django_app/auth_backend.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/django_app/drf_auth.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/django_app/drf_serializers.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/django_app/drf_throttling.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/django_app/drf_views.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/django_app/forms.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/django_app/middleware.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/django_app/templatetags/__init__.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/django_app/templatetags/custom_tags.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/django_app/urls.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/django_app/views.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/templates/base.html +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/templates/context_test.html +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/templates/csrf_form.html +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/templates/hello.html +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_async_utilities.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_auth.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_caching.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_cors.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_csrf.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_drf_auth_permissions.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_drf_content_negotiation.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_drf_serializers.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_drf_throttling.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_drf_versioning.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_drf_views.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_error_handling.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_file_uploads.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_forms.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_middleware.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_pagination.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_request_response.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_sessions.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_signals.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_streaming.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_templates.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_url_routing.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/test_views_basic.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/src/worker.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/django-async/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/public/data.json +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/public/index.html +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/public/style.css +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/src/_client.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/src/conftest.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/src/static/app.css +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/src/static/data.json +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/src/static/hello.txt +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/src/static/index.html +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/src/test_assets.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/src/test_error_handling.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/src/test_platform_boundaries.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/src/test_platform_io.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/src/test_platform_lifecycle.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/src/test_response_types.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/src/test_routing.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/src/test_staticfiles.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/src/test_sync_handlers.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/src/test_websocket.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/fastapi-tests/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/flask-tests/src/test_response.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/flask-tests/src/worker.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/web-frameworks-test/flask-tests/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/asgi/asgi.wd-test +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/asgi/tests/test_asgi.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/asgi/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/asgi-ws-disconnect/asgi-ws-disconnect.wd-test +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/asgi-ws-disconnect/tests/test_ws_disconnect.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/asgi-ws-disconnect/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/durable-object/durable-object.wd-test +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/durable-object/worker.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/durable-object/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/durable-object-abort/durable-object-abort.wd-test +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/durable-object-abort/worker.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/durable-object-abort/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/durable-object-inheritance/durable-object-inheritance.wd-test +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/durable-object-inheritance/worker.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/durable-object-inheritance/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/durable-object-websocket/durable-object-websocket.wd-test +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/durable-object-websocket/tester.js +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/durable-object-websocket/worker.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/durable-object-websocket/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/entropy-patches/entropy-patches.wd-test +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/entropy-patches/tests/test_aiohttp_websocket.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/entropy-patches/tests/test_langsmith.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/entropy-patches/tests/test_litestar.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/entropy-patches/tests/test_mcp.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/entropy-patches/tests/test_numpy.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/entropy-patches/tests/test_opentelemetry.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/entropy-patches/tests/test_pydantic.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/entropy-patches/tests/test_rust_packages.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/entropy-patches/tests/test_ssl_avoidance.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/entropy-patches/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/http-client/http-client.wd-test +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/http-client/origin.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/http-client/tests/test_http_client.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/http-client/tests/test_urllib_request.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/http-client/worker.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/http-client/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/python-rpc/python-rpc.wd-test +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/python-rpc/worker.js +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/python-rpc/worker.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/python-rpc/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/sdk/sdk.wd-test +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/sdk/server.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/sdk/tests/test_sdk.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/sdk/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.8.6/tests/workerd-test/durable-object-abort → workers_runtime_sdk-1.9.1/tests/workerd-test/workflow}/pyproject.toml +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/wsgi/tests/test_wsgi.py +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/wsgi/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/workerd-test/wsgi/wsgi.wd-test +0 -0
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- version list -->
|
|
4
4
|
|
|
5
|
+
## v1.9.1 (2026-09-29)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- **runtime-sdk**: Add rpds to rust package entropy context
|
|
10
|
+
([#277](https://github.com/cloudflare/workers-py/pull/277),
|
|
11
|
+
[`a097b7c`](https://github.com/cloudflare/workers-py/commit/a097b7c5d2e5bba17f34d46abb6e39039d4673d4))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## v1.9.0 (2026-09-18)
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
- **runtime-sdk**: Make NonRetryableError raised in a workflow step non-retryable
|
|
19
|
+
([`1c1980a`](https://github.com/cloudflare/workers-py/commit/1c1980afabaa27d39bf18d3fe19c7385c2890fa6))
|
|
20
|
+
|
|
21
|
+
### Features
|
|
22
|
+
|
|
23
|
+
- **runtime-sdk**: Add helpers to import JavaScript modules shipped with the SDK
|
|
24
|
+
([`caa9364`](https://github.com/cloudflare/workers-py/commit/caa9364f38a111d5496f81f6bc23ebb88d191928))
|
|
25
|
+
|
|
26
|
+
|
|
5
27
|
## v1.8.6 (2026-09-18)
|
|
6
28
|
|
|
7
29
|
### Bug Fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: workers-runtime-sdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.9.1
|
|
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
|
{workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/src/_workers_sdk_entropy_import_context.py
RENAMED
|
@@ -49,6 +49,7 @@ class STATE:
|
|
|
49
49
|
@register_create_patch("tiktoken._tiktoken")
|
|
50
50
|
@register_exec_patch("cryptography.exceptions")
|
|
51
51
|
@register_exec_patch("jiter")
|
|
52
|
+
@register_exec_patch("rpds")
|
|
52
53
|
@register_exec_patch("uuid_utils._uuid_utils")
|
|
53
54
|
@contextmanager
|
|
54
55
|
def rust_package_context(module):
|
|
@@ -17,7 +17,11 @@ from .rpc import (
|
|
|
17
17
|
python_from_rpc,
|
|
18
18
|
python_to_rpc,
|
|
19
19
|
)
|
|
20
|
-
from .utils import
|
|
20
|
+
from .utils import (
|
|
21
|
+
_from_js_error,
|
|
22
|
+
_is_js_instance,
|
|
23
|
+
import_sdk_javascript_module_async,
|
|
24
|
+
)
|
|
21
25
|
|
|
22
26
|
if TYPE_CHECKING:
|
|
23
27
|
from js import DurableObjectState, Env, ExecutionContext
|
|
@@ -294,6 +298,22 @@ def _wrap_class(cls):
|
|
|
294
298
|
return cls
|
|
295
299
|
|
|
296
300
|
|
|
301
|
+
_workflows_js_module = None
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
async def _wrap_js_workflow_step(js_step):
|
|
305
|
+
"""
|
|
306
|
+
Wrap the JS `WorkflowStep` stub with `wrapWorkflowStep` from the SDK's `workflows.js`.
|
|
307
|
+
|
|
308
|
+
The wrapper makes a Python `NonRetryableError` raised inside a step reach the Workflows
|
|
309
|
+
engine as a JS error the engine recognises as non-retryable (see `workflows.js`).
|
|
310
|
+
"""
|
|
311
|
+
global _workflows_js_module # noqa: PLW0603
|
|
312
|
+
if _workflows_js_module is None:
|
|
313
|
+
_workflows_js_module = await import_sdk_javascript_module_async("workflows.js")
|
|
314
|
+
return _workflows_js_module.wrapWorkflowStep(js_step)
|
|
315
|
+
|
|
316
|
+
|
|
297
317
|
def _wrap_workflow_step(cls):
|
|
298
318
|
run_fn = cls.__dict__.get("run")
|
|
299
319
|
if run_fn is None:
|
|
@@ -304,7 +324,7 @@ def _wrap_workflow_step(cls):
|
|
|
304
324
|
if event is not None:
|
|
305
325
|
event = python_from_rpc(event)
|
|
306
326
|
if step is not None:
|
|
307
|
-
step = _WorkflowStepWrapper(step)
|
|
327
|
+
step = _WorkflowStepWrapper(await _wrap_js_workflow_step(step))
|
|
308
328
|
|
|
309
329
|
result = run_fn(self, event, step, *args, **kwargs)
|
|
310
330
|
|
|
@@ -126,6 +126,25 @@ def import_from_javascript(module_name: str) -> Any:
|
|
|
126
126
|
raise
|
|
127
127
|
|
|
128
128
|
|
|
129
|
+
# Directory, relative to the worker bundle root, that `pywrangler sync` vendors Python packages
|
|
130
|
+
# into. wrangler registers `.js`/`.mjs` files found under `python_modules/workers/` as ES modules
|
|
131
|
+
# (everything else in `python_modules/` is opaque data), which is what lets them be imported via
|
|
132
|
+
# `import_from_javascript`.
|
|
133
|
+
_SDK_JS_MODULE_PREFIX = "python_modules/workers/"
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
async def import_sdk_javascript_module_async(name: str) -> Any:
|
|
137
|
+
"""
|
|
138
|
+
Asynchronous function to import an sdk js module
|
|
139
|
+
|
|
140
|
+
This does not rely on JSPI, so it also works with Pyodide 0.26.0a2.
|
|
141
|
+
"""
|
|
142
|
+
try:
|
|
143
|
+
return await _pyodide_entrypoint_helper.doAnImport(_SDK_JS_MODULE_PREFIX + name)
|
|
144
|
+
except JsException as e:
|
|
145
|
+
raise ImportError(f"Failed to import '{name}': {e}") from e
|
|
146
|
+
|
|
147
|
+
|
|
129
148
|
@contextmanager
|
|
130
149
|
def patch_env(
|
|
131
150
|
d: dict[str, Any] | Sequence[tuple[str, Any]] | None = None, **kwds: dict[str, Any]
|
|
@@ -144,15 +163,30 @@ def _to_python_exception(exc: JsException) -> Exception:
|
|
|
144
163
|
return exc
|
|
145
164
|
|
|
146
165
|
|
|
166
|
+
_NON_RETRYABLE_ERROR_NAME = "NonRetryableError"
|
|
167
|
+
|
|
168
|
+
|
|
147
169
|
def _from_js_error(exc: JsException) -> Exception:
|
|
148
170
|
# convert into Python exception after a full round trip
|
|
149
171
|
# Python - JS - Python
|
|
150
172
|
message = exc.message or ""
|
|
151
173
|
|
|
174
|
+
# A NonRetryableError raised inside a workflow step is translated by the
|
|
175
|
+
# runtime into a JS error named "NonRetryableError" before it reaches the
|
|
176
|
+
# Workflows engine, which is how the engine knows not to retry the step.
|
|
177
|
+
# When the engine rethrows it back to us the name either survives, or is
|
|
178
|
+
# folded into the message as a prefix.
|
|
179
|
+
if getattr(exc, "name", None) == _NON_RETRYABLE_ERROR_NAME:
|
|
180
|
+
return NonRetryableError(message)
|
|
181
|
+
if message == _NON_RETRYABLE_ERROR_NAME:
|
|
182
|
+
return NonRetryableError()
|
|
183
|
+
if message.startswith(_NON_RETRYABLE_ERROR_NAME + ": "):
|
|
184
|
+
return NonRetryableError(message[len(_NON_RETRYABLE_ERROR_NAME) + 2 :])
|
|
185
|
+
|
|
152
186
|
# A Python exception that escaped to JS is a Pyodide `PythonError` whose
|
|
153
187
|
# message is the formatted traceback. Depending on how it was serialized
|
|
154
188
|
# over RPC it either keeps its name or arrives as a plain `Error` with
|
|
155
|
-
# "PythonError: " folded into the message
|
|
189
|
+
# "PythonError: " folded into the message
|
|
156
190
|
if getattr(exc, "name", None) != "PythonError" and not message.startswith(
|
|
157
191
|
"PythonError"
|
|
158
192
|
):
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// JavaScript helpers for Python Workflows
|
|
2
|
+
|
|
3
|
+
const NON_RETRYABLE_ERROR_NAME = "NonRetryableError";
|
|
4
|
+
|
|
5
|
+
function isPythonError(e) {
|
|
6
|
+
return (
|
|
7
|
+
e instanceof Error &&
|
|
8
|
+
e.constructor?.name === "PythonError" &&
|
|
9
|
+
typeof e.type === "string"
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Extract the message the user passed to the Python exception from the
|
|
14
|
+
// traceback stored in `PythonError.message`.
|
|
15
|
+
//
|
|
16
|
+
// The last non-empty line of a formatted traceback is
|
|
17
|
+
// `<qualified.ExceptionType>: <message>`, or just `<qualified.ExceptionType>`
|
|
18
|
+
// when the exception was raised without a message. For example, given
|
|
19
|
+
//
|
|
20
|
+
// Traceback (most recent call last):
|
|
21
|
+
// File "/session/metadata/worker.py", line 19, in non_retryable
|
|
22
|
+
// raise NonRetryableError("do not retry")
|
|
23
|
+
// workers.workflows.NonRetryableError: do not retry
|
|
24
|
+
//
|
|
25
|
+
// this returns "do not retry", and given
|
|
26
|
+
//
|
|
27
|
+
// Traceback (most recent call last):
|
|
28
|
+
// File "/session/metadata/worker.py", line 34, in no_message
|
|
29
|
+
// raise NonRetryableError()
|
|
30
|
+
// workers.workflows.NonRetryableError
|
|
31
|
+
//
|
|
32
|
+
// it returns "".
|
|
33
|
+
function pythonExceptionMessage(e) {
|
|
34
|
+
const lines = e.message.split("\n").filter((line) => line.trim() !== "");
|
|
35
|
+
const last = lines.at(-1) ?? "";
|
|
36
|
+
const sep = last.indexOf(": ");
|
|
37
|
+
return sep === -1 ? "" : last.slice(sep + 2);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Wraps a Python step callback passed to `WorkflowStep.do()`.
|
|
41
|
+
//
|
|
42
|
+
// If the error thrown is a Python NonRetryable error, translate it into a JS
|
|
43
|
+
// error that the engine will recognize as non retryable. Leave other errors
|
|
44
|
+
// alone.
|
|
45
|
+
export function wrapWorkflowStepCallback(pyCallback) {
|
|
46
|
+
return async function (...args) {
|
|
47
|
+
try {
|
|
48
|
+
return await pyCallback(...args);
|
|
49
|
+
} catch (e) {
|
|
50
|
+
if (isPythonError(e) && e.type === NON_RETRYABLE_ERROR_NAME) {
|
|
51
|
+
const err = new Error(pythonExceptionMessage(e));
|
|
52
|
+
err.name = NON_RETRYABLE_ERROR_NAME;
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
throw e;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Wraps the `WorkflowStep` RPC stub passed to a Python
|
|
61
|
+
// `WorkflowEntrypoint.run()` so that the callback given to `step.do()` goes
|
|
62
|
+
// through `wrapWorkflowStepCallback`.
|
|
63
|
+
//
|
|
64
|
+
// Because the wrapped `do` returns the stub's own promise, a Python callback
|
|
65
|
+
// passed to it keeps exactly the lifetime it would have had without the
|
|
66
|
+
// wrapper.
|
|
67
|
+
export function wrapWorkflowStep(step) {
|
|
68
|
+
// RPC stubs are callable, so `typeof step` is 'function'.
|
|
69
|
+
if (
|
|
70
|
+
step === null ||
|
|
71
|
+
(typeof step !== "object" && typeof step !== "function")
|
|
72
|
+
) {
|
|
73
|
+
return step;
|
|
74
|
+
}
|
|
75
|
+
return new Proxy(step, {
|
|
76
|
+
apply(target, thisArg, args) {
|
|
77
|
+
return Reflect.apply(target, thisArg, args);
|
|
78
|
+
},
|
|
79
|
+
get(target, prop) {
|
|
80
|
+
if (prop !== "do") {
|
|
81
|
+
return Reflect.get(target, prop);
|
|
82
|
+
}
|
|
83
|
+
return function (name, ...rest) {
|
|
84
|
+
const args = rest.map((arg) =>
|
|
85
|
+
typeof arg === "function" ? wrapWorkflowStepCallback(arg) : arg
|
|
86
|
+
);
|
|
87
|
+
return target.do(name, ...args);
|
|
88
|
+
};
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
}
|
|
@@ -7,13 +7,18 @@ dependencies = [
|
|
|
7
7
|
"pytest-asyncio<1.2.0",
|
|
8
8
|
# These are for hyperdrive tests
|
|
9
9
|
"pg8000",
|
|
10
|
-
|
|
10
|
+
# There is a regression in upstream pymysql.
|
|
11
|
+
# Remove when the following PR is merged and released.
|
|
12
|
+
# https://github.com/PyMySQL/PyMySQL/pull/1275
|
|
13
|
+
"pymysql<=1.2.0",
|
|
11
14
|
"aiomysql",
|
|
12
15
|
"psycopg[binary]; python_version >= '3.14'",
|
|
13
16
|
"asyncpg; python_version >= '3.14'",
|
|
14
17
|
"cryptography",
|
|
15
18
|
"testlib",
|
|
19
|
+
"workers-runtime-sdk",
|
|
16
20
|
]
|
|
17
21
|
|
|
18
22
|
[tool.uv.sources]
|
|
19
|
-
testlib = { path = "../
|
|
23
|
+
testlib = { path = "../packages/testlib" }
|
|
24
|
+
workers-runtime-sdk = { path = "../packages/runtime-sdk" }
|
{workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/test_workflow.py
RENAMED
|
@@ -149,7 +149,17 @@ async def test_step_retry_config(env):
|
|
|
149
149
|
async def test_non_retryable_error(env):
|
|
150
150
|
instance = await env.MY_WORKFLOW.create({"params": {"mode": "non_retryable"}})
|
|
151
151
|
status = await _poll(instance)
|
|
152
|
-
assert status["status"] == "
|
|
152
|
+
assert status["status"] == "complete", f"unexpected status: {dict(status)!r}"
|
|
153
|
+
out = status["output"]
|
|
154
|
+
# The step would succeed on attempt 2, so `retried` means the engine ignored
|
|
155
|
+
# the NonRetryableError.
|
|
156
|
+
assert out["retried"] is False, f"step was retried: {out!r}"
|
|
157
|
+
# ...and the error must come back to `run()` as a Python NonRetryableError.
|
|
158
|
+
assert out["caught"] == "NonRetryableError", out
|
|
159
|
+
# The message is preserved end to end in workerd, but miniflare currently
|
|
160
|
+
# truncates error messages crossing from the Python worker into its
|
|
161
|
+
# Workflows engine at the first ": " so accept an empty message here.
|
|
162
|
+
assert out["message"] in ("do not retry", ""), out
|
|
153
163
|
|
|
154
164
|
|
|
155
165
|
async def test_error_handling_catch(env):
|
{workers_runtime_sdk-1.8.6 → workers_runtime_sdk-1.9.1}/tests/bindings-test/src/worker_workflow.py
RENAMED
|
@@ -109,14 +109,30 @@ class TestWorkflow(WorkflowEntrypoint):
|
|
|
109
109
|
return await flaky()
|
|
110
110
|
|
|
111
111
|
async def _non_retryable(self, event, step):
|
|
112
|
+
# Fails on the first attempt with NonRetryableError and would succeed on a
|
|
113
|
+
# second attempt. If the engine honours NonRetryableError the step fails
|
|
114
|
+
# once and `run()` sees a NonRetryableError; if the engine retries, the step
|
|
115
|
+
# completes with `attempt == 2`.
|
|
112
116
|
@step.do(
|
|
113
117
|
"non-retryable-step",
|
|
114
|
-
config={"retries": {"limit":
|
|
118
|
+
config={"retries": {"limit": 3, "delay": 0, "backoff": "constant"}},
|
|
115
119
|
)
|
|
116
|
-
async def boom():
|
|
117
|
-
|
|
120
|
+
async def boom(ctx):
|
|
121
|
+
if int(ctx["attempt"]) < 2:
|
|
122
|
+
raise NonRetryableError("do not retry")
|
|
123
|
+
return {"retried": True, "attempt": int(ctx["attempt"])}
|
|
118
124
|
|
|
119
|
-
|
|
125
|
+
try:
|
|
126
|
+
result = await boom()
|
|
127
|
+
except NonRetryableError as exc:
|
|
128
|
+
return {
|
|
129
|
+
"retried": False,
|
|
130
|
+
"caught": "NonRetryableError",
|
|
131
|
+
"message": str(exc),
|
|
132
|
+
}
|
|
133
|
+
except Exception as exc:
|
|
134
|
+
return {"retried": False, "caught": type(exc).__name__, "message": str(exc)}
|
|
135
|
+
return result
|
|
120
136
|
|
|
121
137
|
async def _duplicate_step_names(self, event, step):
|
|
122
138
|
# The engine disambiguates repeated step names with a counter, so two
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import shutil
|
|
5
|
-
import subprocess
|
|
6
5
|
from collections.abc import Generator
|
|
7
6
|
from pathlib import Path
|
|
8
7
|
from typing import Any
|
|
@@ -12,6 +11,8 @@ from testlib.host import (
|
|
|
12
11
|
COMPAT_CONFIGS,
|
|
13
12
|
CompatConfig,
|
|
14
13
|
configure_compatibility,
|
|
14
|
+
link_packages,
|
|
15
|
+
pywrangler_sync,
|
|
15
16
|
)
|
|
16
17
|
from testlib.host import (
|
|
17
18
|
dev_server as run_dev_server,
|
|
@@ -21,9 +22,6 @@ from testlib.host import (
|
|
|
21
22
|
)
|
|
22
23
|
|
|
23
24
|
TEST_DIR: Path = Path(__file__).parent
|
|
24
|
-
WORKERS_PY: Path = TEST_DIR.parent.parent / "cli"
|
|
25
|
-
WORKERS_RUNTIME_SDK: Path = TEST_DIR.parent / "src"
|
|
26
|
-
TESTLIB: Path = TEST_DIR.parent.parent / "testlib"
|
|
27
25
|
|
|
28
26
|
DEV_STARTUP_TIMEOUT: int = 120
|
|
29
27
|
OPT_IN_MARKERS: tuple[str, ...] = ("hyperdrive",)
|
|
@@ -43,11 +41,6 @@ def pytest_collection_modifyitems(
|
|
|
43
41
|
item.add_marker(skip)
|
|
44
42
|
|
|
45
43
|
|
|
46
|
-
@pytest.fixture(scope="session", autouse=True)
|
|
47
|
-
def build_testlib():
|
|
48
|
-
subprocess.run(["uv", "build"], cwd=TESTLIB, check=True)
|
|
49
|
-
|
|
50
|
-
|
|
51
44
|
@pytest.fixture(
|
|
52
45
|
scope="module",
|
|
53
46
|
params=COMPAT_CONFIGS,
|
|
@@ -74,32 +67,27 @@ def dev_server(
|
|
|
74
67
|
worker_project_dir: Path,
|
|
75
68
|
compat_config: CompatConfig,
|
|
76
69
|
) -> Generator[str]:
|
|
77
|
-
"""Start a pywrangler dev server on a free port and yield its base URL.
|
|
70
|
+
"""Start a pywrangler dev server on a free port and yield its base URL.
|
|
71
|
+
|
|
72
|
+
The project is copied next to a ``packages`` symlink so that its
|
|
73
|
+
``../packages/...`` sources resolve and ``sync`` vendors the working-tree
|
|
74
|
+
testlib and runtime-sdk.
|
|
75
|
+
"""
|
|
78
76
|
tmp_path = tmp_path_factory.mktemp(f"{worker_project_dir.name}_dev")
|
|
79
77
|
target = tmp_path / worker_project_dir.name
|
|
80
78
|
shutil.copytree(worker_project_dir, target, ignore=shutil.ignore_patterns(".venv"))
|
|
81
|
-
|
|
79
|
+
link_packages(tmp_path)
|
|
82
80
|
env = os.environ | {"_PYODIDE_EXTRA_MOUNTS": str(tmp_path)}
|
|
83
81
|
|
|
84
82
|
wrangler_jsonc = target / "wrangler.jsonc"
|
|
85
83
|
configure_compatibility(wrangler_jsonc, compat_config)
|
|
86
84
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
subprocess.run(
|
|
90
|
-
[*pywrangler_cmd, "sync"],
|
|
91
|
-
cwd=target,
|
|
92
|
-
check=True,
|
|
93
|
-
env=env,
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
shutil.copytree(WORKERS_RUNTIME_SDK, target / "python_modules", dirs_exist_ok=True)
|
|
85
|
+
pywrangler_sync(target, env)
|
|
97
86
|
|
|
98
87
|
with run_dev_server(
|
|
99
88
|
target,
|
|
100
89
|
tmp_path,
|
|
101
90
|
env,
|
|
102
|
-
pywrangler_cmd,
|
|
103
91
|
startup_timeout=DEV_STARTUP_TIMEOUT,
|
|
104
92
|
readiness_path="/health",
|
|
105
93
|
require_success=True,
|
|
@@ -113,4 +101,4 @@ def register_in_worker_suites(
|
|
|
113
101
|
src_dir: Path,
|
|
114
102
|
marks: dict[str, pytest.MarkDecorator] | None = None,
|
|
115
103
|
) -> None:
|
|
116
|
-
register_testlib_suites(namespace, src_dir, marks=marks
|
|
104
|
+
register_testlib_suites(namespace, src_dir, marks=marks)
|
|
@@ -8,12 +8,12 @@ from conftest import (
|
|
|
8
8
|
COMPAT_CONFIGS,
|
|
9
9
|
CompatConfig,
|
|
10
10
|
configure_compatibility,
|
|
11
|
+
link_packages,
|
|
12
|
+
pywrangler_sync,
|
|
11
13
|
)
|
|
12
14
|
|
|
13
15
|
TEST_DIR = Path(__file__).parent
|
|
14
16
|
WORKERD_TESTS = TEST_DIR / "workerd-test"
|
|
15
|
-
WORKERS_PY = TEST_DIR.parent.parent / "cli"
|
|
16
|
-
WORKERS_RUNTIME_SDK = TEST_DIR.parent / "src"
|
|
17
17
|
DISK_SERVICE_NAME = "TEST_TMPDIR"
|
|
18
18
|
|
|
19
19
|
|
|
@@ -66,14 +66,27 @@ def embed(dir: Path, root: Path, level: int = 0):
|
|
|
66
66
|
module_path = path.absolute().relative_to(module_path_root)
|
|
67
67
|
embed_path = path.absolute().relative_to(root)
|
|
68
68
|
if path.suffix == ".py":
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
module_type = "pythonModule"
|
|
70
|
+
elif _is_sdk_js_module(path.relative_to(dir)):
|
|
71
|
+
module_type = "esModule"
|
|
72
72
|
else:
|
|
73
|
-
|
|
73
|
+
module_type = "data"
|
|
74
|
+
modules.append(
|
|
75
|
+
f'(name = "{module_path}", {module_type} = embed "{embed_path}")'
|
|
76
|
+
)
|
|
74
77
|
return modules
|
|
75
78
|
|
|
76
79
|
|
|
80
|
+
def _is_sdk_js_module(path_in_vendor_dir: Path) -> bool:
|
|
81
|
+
"""Mirror wrangler: `.js`/`.mjs` files under `python_modules/workers/` are ES modules.
|
|
82
|
+
|
|
83
|
+
Other vendored packages may ship `.js` assets that are not valid ES modules, so wrangler
|
|
84
|
+
only applies this rule to the SDK's own `workers/` package. Everything else stays `data`.
|
|
85
|
+
"""
|
|
86
|
+
in_sdk_package = path_in_vendor_dir.parts[:1] == ("workers",)
|
|
87
|
+
return in_sdk_package and path_in_vendor_dir.suffix in (".js", ".mjs")
|
|
88
|
+
|
|
89
|
+
|
|
77
90
|
@pytest.fixture(scope="module")
|
|
78
91
|
def bundle_cache_dir(tmp_path_factory):
|
|
79
92
|
yield tmp_path_factory.mktemp("bundle_cache")
|
|
@@ -110,23 +123,14 @@ def test_in_workerd( # noqa: PLR0913, PLR0917 (too-many-arguments)
|
|
|
110
123
|
target = tmp_path / test_dir.name
|
|
111
124
|
disk_service_dir = target / DISK_SERVICE_NAME
|
|
112
125
|
shutil.copytree(test_dir, target, ignore=shutil.ignore_patterns(".venv"))
|
|
126
|
+
# Makes the project's `../packages/{runtime-sdk,testlib}` sources resolve
|
|
127
|
+
# so `sync` vendors the working-tree SDK and testlib.
|
|
128
|
+
link_packages(tmp_path)
|
|
113
129
|
disk_service_dir.mkdir(exist_ok=True)
|
|
114
130
|
|
|
115
131
|
configure_compatibility(target / "wrangler.jsonc", compat_config)
|
|
116
132
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
subprocess.run(
|
|
120
|
-
[*pywrangler_cmd, "sync"],
|
|
121
|
-
cwd=target,
|
|
122
|
-
check=True,
|
|
123
|
-
env=os.environ | {"_PYODIDE_EXTRA_MOUNTS": str(tmp_path)},
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
# Copy runtime-sdk to the python modules as well
|
|
127
|
-
# FIXME: remove this and pass runtime-sdk as a dependency explicitly after
|
|
128
|
-
# https://github.com/cloudflare/workers-py/pull/81 is merged
|
|
129
|
-
shutil.copytree(WORKERS_RUNTIME_SDK, target / "python_modules", dirs_exist_ok=True)
|
|
133
|
+
pywrangler_sync(target, os.environ | {"_PYODIDE_EXTRA_MOUNTS": str(tmp_path)})
|
|
130
134
|
|
|
131
135
|
modules = embed(target / "python_modules", target, level=1) + embed(
|
|
132
136
|
target / "tests", target, level=1
|
|
@@ -11,7 +11,9 @@ dependencies = [
|
|
|
11
11
|
"pytest",
|
|
12
12
|
"pytest-asyncio<1.2.0",
|
|
13
13
|
"testlib",
|
|
14
|
+
"workers-runtime-sdk",
|
|
14
15
|
]
|
|
15
16
|
|
|
16
17
|
[tool.uv.sources]
|
|
17
|
-
testlib = { path = "../
|
|
18
|
+
testlib = { path = "../packages/testlib" }
|
|
19
|
+
workers-runtime-sdk = { path = "../packages/runtime-sdk" }
|
|
@@ -8,7 +8,9 @@ dependencies = [
|
|
|
8
8
|
"pytest",
|
|
9
9
|
"pytest-asyncio<1.2.0",
|
|
10
10
|
"testlib",
|
|
11
|
+
"workers-runtime-sdk",
|
|
11
12
|
]
|
|
12
13
|
|
|
13
14
|
[tool.uv.sources]
|
|
14
|
-
testlib = { path = "../
|
|
15
|
+
testlib = { path = "../packages/testlib" }
|
|
16
|
+
workers-runtime-sdk = { path = "../packages/runtime-sdk" }
|
|
@@ -30,21 +30,12 @@ from fastapi.responses import (
|
|
|
30
30
|
from fastapi.responses import Response as FastAPIResponse
|
|
31
31
|
from fastapi.staticfiles import StaticFiles
|
|
32
32
|
from pydantic import BaseModel
|
|
33
|
-
from pyodide.webloop import WebLoop
|
|
34
33
|
from starlette.background import BackgroundTask
|
|
35
34
|
from starlette.middleware.gzip import GZipMiddleware
|
|
36
|
-
from testlib.entrypoint import TestRunner
|
|
35
|
+
from testlib.entrypoint import RunSuiteRequest, TestRunner
|
|
37
36
|
|
|
38
37
|
import asgi
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
async def _noop(*args):
|
|
42
|
-
pass
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
WebLoop.shutdown_asyncgens = _noop
|
|
46
|
-
WebLoop.shutdown_default_executor = _noop
|
|
47
|
-
|
|
48
39
|
STATIC_DIR = Path(__file__).parent / "static"
|
|
49
40
|
|
|
50
41
|
|
|
@@ -603,7 +594,9 @@ class FastAPIAppPlugin:
|
|
|
603
594
|
@app.get("/run-tests/{suite_name:path}")
|
|
604
595
|
async def run_suite(suite_name: str, request: Request):
|
|
605
596
|
runner = TestRunner(request.scope["env"], extra_plugins=[FastAPIAppPlugin()])
|
|
606
|
-
result = runner.run_suite(
|
|
597
|
+
result = runner.run_suite(
|
|
598
|
+
suite_name, RunSuiteRequest.from_query_params(request.query_params)
|
|
599
|
+
)
|
|
607
600
|
return JSONResponse(result.payload, status_code=result.status)
|
|
608
601
|
|
|
609
602
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "flask-tests"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
requires-python = ">=3.12"
|
|
5
|
+
dependencies = ["flask", "pytest", "testlib", "workers-runtime-sdk"]
|
|
6
|
+
|
|
7
|
+
[tool.uv.sources]
|
|
8
|
+
testlib = { path = "../packages/testlib" }
|
|
9
|
+
workers-runtime-sdk = { path = "../packages/runtime-sdk" }
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "test"
|
|
3
|
+
version = "0.0.0"
|
|
4
|
+
requires-python = ">=3.12"
|
|
5
|
+
dependencies = ["pytest", "pytest-asyncio<1.2.0", "testlib", "workers-runtime-sdk"]
|
|
6
|
+
|
|
7
|
+
[tool.uv.sources]
|
|
8
|
+
testlib = { path = "../packages/testlib" }
|
|
9
|
+
workers-runtime-sdk = { path = "../packages/runtime-sdk" }
|
|
@@ -1,28 +1,10 @@
|
|
|
1
|
-
import asyncio
|
|
2
1
|
import os
|
|
3
|
-
import sys
|
|
4
2
|
|
|
5
|
-
import
|
|
6
|
-
from pyodide.webloop import WebLoop
|
|
3
|
+
from testlib.entrypoint import run_pytest
|
|
7
4
|
|
|
8
5
|
import asgi
|
|
9
6
|
from workers import WorkerEntrypoint
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
async def noop(*args):
|
|
13
|
-
pass
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
# pytest-asyncio relies on these but in Pyodide < 0.29 WebLoop does not implement them
|
|
17
|
-
WebLoop.shutdown_asyncgens = noop
|
|
18
|
-
WebLoop.shutdown_default_executor = noop
|
|
19
|
-
|
|
20
|
-
# Pyodide 0.26.0a2's _cancel_all_tasks calls task.exception() on pending tasks,
|
|
21
|
-
# which raises InvalidStateError under Pyodide's WebLoop.
|
|
22
|
-
if sys.version_info < (3, 13):
|
|
23
|
-
asyncio.runners._cancel_all_tasks = lambda loop: None # type: ignore[attr-defined]
|
|
24
|
-
|
|
25
|
-
|
|
26
8
|
# ---------------------------------------------------------------------------
|
|
27
9
|
# ASGI apps
|
|
28
10
|
# ---------------------------------------------------------------------------
|
|
@@ -291,4 +273,4 @@ class Default(WorkerEntrypoint):
|
|
|
291
273
|
async def test(self):
|
|
292
274
|
os.chdir("/session/metadata/tests")
|
|
293
275
|
args = [".", "-vv"]
|
|
294
|
-
|
|
276
|
+
run_pytest(args)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "test"
|
|
3
|
+
version = "0.0.0"
|
|
4
|
+
requires-python = ">=3.12"
|
|
5
|
+
dependencies = ["pytest", "pytest-asyncio<1.2.0", "testlib", "workers-runtime-sdk"]
|
|
6
|
+
|
|
7
|
+
[tool.uv.sources]
|
|
8
|
+
testlib = { path = "../packages/testlib" }
|
|
9
|
+
workers-runtime-sdk = { path = "../packages/runtime-sdk" }
|
|
@@ -1,29 +1,12 @@
|
|
|
1
|
-
import asyncio
|
|
2
1
|
import os
|
|
3
|
-
import sys
|
|
4
2
|
from urllib.parse import urlsplit
|
|
5
3
|
|
|
6
|
-
import
|
|
7
|
-
from pyodide.webloop import WebLoop
|
|
4
|
+
from testlib.entrypoint import run_pytest
|
|
8
5
|
|
|
9
6
|
import asgi
|
|
10
7
|
from workers import WorkerEntrypoint
|
|
11
8
|
|
|
12
9
|
|
|
13
|
-
async def _noop(*args):
|
|
14
|
-
pass
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
# pytest-asyncio relies on these but in Pyodide < 0.29 WebLoop does not implement them
|
|
18
|
-
WebLoop.shutdown_asyncgens = _noop
|
|
19
|
-
WebLoop.shutdown_default_executor = _noop
|
|
20
|
-
|
|
21
|
-
# Pyodide 0.26.0a2's _cancel_all_tasks calls task.exception() on pending tasks,
|
|
22
|
-
# which raises InvalidStateError under Pyodide's WebLoop.
|
|
23
|
-
if sys.version_info < (3, 13):
|
|
24
|
-
asyncio.runners._cancel_all_tasks = lambda loop: None # type: ignore[attr-defined]
|
|
25
|
-
|
|
26
|
-
|
|
27
10
|
async def _drain_lifespan(receive, send):
|
|
28
11
|
message = await receive()
|
|
29
12
|
if message["type"] == "lifespan.startup":
|
|
@@ -180,4 +163,4 @@ class Default(WorkerEntrypoint):
|
|
|
180
163
|
async def test(self):
|
|
181
164
|
os.chdir("/session/metadata/tests")
|
|
182
165
|
args = [".", "-vv"]
|
|
183
|
-
|
|
166
|
+
run_pytest(args)
|