workers-runtime-sdk 1.6.12__tar.gz → 1.6.14__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.6.12 → workers_runtime_sdk-1.6.14}/CHANGELOG.md +17 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/PKG-INFO +1 -1
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/pyproject.toml +4 -1
- workers_runtime_sdk-1.6.14/src/asgi.py +6 -0
- {workers_runtime_sdk-1.6.12/src → workers_runtime_sdk-1.6.14/src/workers}/asgi.py +22 -13
- workers_runtime_sdk-1.6.14/tests/bindings-test/pyproject.toml +5 -0
- {workers_runtime_sdk-1.6.12/tests/web-frameworks-test/fastapi-tests → workers_runtime_sdk-1.6.14/tests/bindings-test}/src/conftest.py +6 -0
- workers_runtime_sdk-1.6.14/tests/bindings-test/src/test_hyperdrive_mysql.py +208 -0
- workers_runtime_sdk-1.6.14/tests/bindings-test/src/test_hyperdrive_postgresql.py +182 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/wrangler.jsonc +12 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/conftest.py +25 -2
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/test_bindings.py +9 -1
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/pyproject.toml +1 -0
- workers_runtime_sdk-1.6.14/tests/web-frameworks-test/fastapi-tests/src/_client.py +66 -0
- workers_runtime_sdk-1.6.14/tests/web-frameworks-test/fastapi-tests/src/test_error_handling.py +156 -0
- workers_runtime_sdk-1.6.14/tests/web-frameworks-test/fastapi-tests/src/test_platform_lifecycle.py +12 -0
- workers_runtime_sdk-1.6.14/tests/web-frameworks-test/fastapi-tests/src/test_response_types.py +183 -0
- workers_runtime_sdk-1.6.14/tests/web-frameworks-test/fastapi-tests/src/test_routing.py +190 -0
- workers_runtime_sdk-1.6.14/tests/web-frameworks-test/fastapi-tests/src/test_sync_handlers.py +128 -0
- workers_runtime_sdk-1.6.14/tests/web-frameworks-test/fastapi-tests/src/worker.py +560 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/asgi/tests/test_asgi.py +16 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/asgi/worker.py +26 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/uv.lock +1 -1
- workers_runtime_sdk-1.6.12/tests/bindings-test/pyproject.toml +0 -5
- workers_runtime_sdk-1.6.12/tests/web-frameworks-test/fastapi-tests/src/_client.py +0 -26
- workers_runtime_sdk-1.6.12/tests/web-frameworks-test/fastapi-tests/src/worker.py +0 -159
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/.gitignore +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/AGENTS.md +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/README.md +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/_cloudflare_compat_flags.pyi +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/_pyodide_entrypoint_helper.pyi +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/_workers_sdk_entropy_import_context.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/_workers_sdk_package_patches.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/_workers_sdk_patch_loader.pth +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/_workers_sdk_patch_loader.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/__init__.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/blob.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/entrypoints.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/fetch.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/formdata.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/py.typed +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/request.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/response.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/rpc.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/types.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/utils.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/workflows.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/wsgi.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/public/data.json +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/public/hello.txt +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_analytics_engine.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_assets.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_d1.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_do.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_images.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_kv.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_mtls.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_queue.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_r2.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_ratelimit.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_vectorize.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_version_metadata.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_workflow.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/worker.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/worker_durable_object.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/worker_workflow.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/test_fastapi.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/test_in_workerd.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/test_web_frameworks.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/_client.py +0 -0
- {workers_runtime_sdk-1.6.12/tests/bindings-test → workers_runtime_sdk-1.6.14/tests/web-frameworks-test/django-async}/src/conftest.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/django_app/__init__.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/django_app/api_urls.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/django_app/auth_backend.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/django_app/drf_auth.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/django_app/drf_serializers.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/django_app/drf_throttling.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/django_app/drf_views.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/django_app/forms.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/django_app/middleware.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/django_app/templatetags/__init__.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/django_app/templatetags/custom_tags.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/django_app/urls.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/django_app/views.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/templates/base.html +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/templates/context_test.html +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/templates/csrf_form.html +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/templates/hello.html +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_async_utilities.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_auth.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_caching.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_cors.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_csrf.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_drf_auth_permissions.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_drf_content_negotiation.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_drf_serializers.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_drf_throttling.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_drf_versioning.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_drf_views.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_error_handling.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_file_uploads.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_forms.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_middleware.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_pagination.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_request_response.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_sessions.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_signals.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_streaming.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_templates.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_url_routing.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_views_basic.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/worker.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/public/data.json +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/public/index.html +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/public/style.css +0 -0
- {workers_runtime_sdk-1.6.12/tests/web-frameworks-test/django-async → workers_runtime_sdk-1.6.14/tests/web-frameworks-test/fastapi-tests}/src/conftest.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/src/static/app.css +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/src/static/data.json +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/src/static/hello.txt +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/src/static/index.html +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/src/test_assets.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/src/test_staticfiles.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/asgi/asgi.wd-test +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/asgi/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/asgi/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/asgi-ws-disconnect/asgi-ws-disconnect.wd-test +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/asgi-ws-disconnect/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/asgi-ws-disconnect/tests/test_ws_disconnect.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/asgi-ws-disconnect/worker.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/asgi-ws-disconnect/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object/durable-object.wd-test +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object/worker.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-abort/durable-object-abort.wd-test +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-abort/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-abort/worker.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-abort/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-inheritance/durable-object-inheritance.wd-test +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-inheritance/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-inheritance/worker.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-inheritance/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-websocket/durable-object-websocket.wd-test +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-websocket/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-websocket/tester.js +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-websocket/worker.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-websocket/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/entropy-patches.wd-test +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/tests/test_aiohttp_websocket.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/tests/test_langsmith.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/tests/test_numpy.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/tests/test_pydantic.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/tests/test_rust_packages.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/tests/test_ssl_avoidance.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/worker.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/python-rpc/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/python-rpc/python-rpc.wd-test +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/python-rpc/worker.js +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/python-rpc/worker.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/python-rpc/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/sdk/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/sdk/sdk.wd-test +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/sdk/server.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/sdk/tests/test_sdk.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/sdk/worker.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/sdk/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/wsgi/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/wsgi/tests/test_wsgi.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/wsgi/worker.py +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/wsgi/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/wsgi/wsgi.wd-test +0 -0
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- version list -->
|
|
4
4
|
|
|
5
|
+
## v1.6.14 (2026-08-19)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- Fixes fastapi lifespan state not persisting
|
|
10
|
+
([`fadd19b`](https://github.com/cloudflare/workers-py/commit/fadd19bcb6ca47518d5e42562879164dd62ab404))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## v1.6.13 (2026-08-12)
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
- **runtime-sdk**: Preserve repeated response headers (multiple `Set-Cookie`)
|
|
18
|
+
([#167](https://github.com/cloudflare/workers-py/pull/167),
|
|
19
|
+
[`f68bf6d`](https://github.com/cloudflare/workers-py/commit/f68bf6d622c896b6c61fe3c615d43d6e9b05d19b))
|
|
20
|
+
|
|
21
|
+
|
|
5
22
|
## v1.6.12 (2026-08-11)
|
|
6
23
|
|
|
7
24
|
### Bug Fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: workers-runtime-sdk
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.14
|
|
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.6.
|
|
7
|
+
version = "1.6.14"
|
|
8
8
|
description = "Python SDK for Cloudflare Workers"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -64,6 +64,9 @@ lint.per-file-ignores."tests/web-frameworks-test/**" = ["C901", "PLR0911", "PLR0
|
|
|
64
64
|
|
|
65
65
|
[tool.pytest.ini_options]
|
|
66
66
|
addopts = ["--ignore=tests/workerd-test", "--ignore=tests/bindings-test", "--ignore=tests/web-frameworks-test"]
|
|
67
|
+
markers = [
|
|
68
|
+
"hyperdrive: needs MySQL and PostgreSQL on localhost; skipped unless run with -m hyperdrive",
|
|
69
|
+
]
|
|
67
70
|
|
|
68
71
|
[tool.mypy]
|
|
69
72
|
packages = ["workers_runtime_sdk"]
|
|
@@ -8,6 +8,7 @@ from urllib.parse import unquote
|
|
|
8
8
|
import js
|
|
9
9
|
|
|
10
10
|
from workers import Context, Request
|
|
11
|
+
from workers.utils import _to_js_headers
|
|
11
12
|
|
|
12
13
|
ASGI = {"spec_version": "2.0", "version": "3.0"}
|
|
13
14
|
NULL_BODY_STATUSES = frozenset({101, 103, 204, 205, 304})
|
|
@@ -48,7 +49,7 @@ def acquire_js_buffer(pybuffer):
|
|
|
48
49
|
buf.release()
|
|
49
50
|
|
|
50
51
|
|
|
51
|
-
def request_to_scope(req, env, ws=False):
|
|
52
|
+
def request_to_scope(req, env, ws=False, state=None):
|
|
52
53
|
from js import URL
|
|
53
54
|
|
|
54
55
|
# @app.get("/example")
|
|
@@ -73,7 +74,7 @@ def request_to_scope(req, env, ws=False):
|
|
|
73
74
|
ty = "websocket"
|
|
74
75
|
else:
|
|
75
76
|
ty = "http"
|
|
76
|
-
|
|
77
|
+
scope = {
|
|
77
78
|
"asgi": ASGI,
|
|
78
79
|
"headers": headers,
|
|
79
80
|
"http_version": "1.1",
|
|
@@ -85,6 +86,10 @@ def request_to_scope(req, env, ws=False):
|
|
|
85
86
|
"type": ty,
|
|
86
87
|
"env": env,
|
|
87
88
|
}
|
|
89
|
+
if state is not None:
|
|
90
|
+
# ASGI requires a shallow copy of lifespan state for each request.
|
|
91
|
+
scope["state"] = dict(state)
|
|
92
|
+
return scope
|
|
88
93
|
|
|
89
94
|
|
|
90
95
|
async def start_application(app):
|
|
@@ -93,6 +98,7 @@ async def start_application(app):
|
|
|
93
98
|
# don't implement it and fall back to serving requests without lifespan.
|
|
94
99
|
# https://asgi.readthedocs.io/en/latest/specs/lifespan.html
|
|
95
100
|
receive_queue = Queue()
|
|
101
|
+
state = {}
|
|
96
102
|
await receive_queue.put({"type": "lifespan.startup"})
|
|
97
103
|
|
|
98
104
|
# `startup` resolves True on `lifespan.startup.complete`, False when the app
|
|
@@ -140,7 +146,7 @@ async def start_application(app):
|
|
|
140
146
|
await app(
|
|
141
147
|
{
|
|
142
148
|
"asgi": ASGI,
|
|
143
|
-
"state":
|
|
149
|
+
"state": state,
|
|
144
150
|
"type": "lifespan",
|
|
145
151
|
},
|
|
146
152
|
receive,
|
|
@@ -167,19 +173,20 @@ async def start_application(app):
|
|
|
167
173
|
run_in_background(run_lifespan())
|
|
168
174
|
supported = await startup
|
|
169
175
|
if not supported:
|
|
170
|
-
return no_lifespan_shutdown
|
|
171
|
-
return shutdown
|
|
176
|
+
return no_lifespan_shutdown, state
|
|
177
|
+
return shutdown, state
|
|
172
178
|
|
|
173
179
|
|
|
174
|
-
async def process_request(
|
|
180
|
+
async def process_request( # noqa: PLR0913
|
|
175
181
|
app: Any,
|
|
176
182
|
req: "Request | js.Request",
|
|
177
183
|
env: Any,
|
|
178
184
|
# added for waitUntil, but not used anymore
|
|
179
185
|
# TODO(later): remove this parameter after unvendoring Python SDK from workerd
|
|
180
186
|
ctx: Context | None,
|
|
187
|
+
state: dict[str, Any] | None = None,
|
|
181
188
|
) -> js.Response:
|
|
182
|
-
from js import
|
|
189
|
+
from js import Response, TransformStream
|
|
183
190
|
from pyodide.ffi import create_proxy
|
|
184
191
|
|
|
185
192
|
status = None
|
|
@@ -221,6 +228,8 @@ async def process_request(
|
|
|
221
228
|
if got["type"] == "http.response.start":
|
|
222
229
|
status = got["status"]
|
|
223
230
|
# Like above, we need to convert byte-pairs into string explicitly.
|
|
231
|
+
# A response header name may repeat (multiple Set-Cookie), so this
|
|
232
|
+
# must stay a sequence of pairs, not a name-keyed mapping.
|
|
224
233
|
headers = [(k.decode(), v.decode()) for k, v in got.get("headers", [])]
|
|
225
234
|
|
|
226
235
|
elif got["type"] == "http.response.body":
|
|
@@ -243,7 +252,7 @@ async def process_request(
|
|
|
243
252
|
readable = transform_stream.readable
|
|
244
253
|
writer = transform_stream.writable.getWriter()
|
|
245
254
|
resp = Response.new(
|
|
246
|
-
readable, headers=
|
|
255
|
+
readable, headers=_to_js_headers(headers), status=status
|
|
247
256
|
)
|
|
248
257
|
result.set_result(resp)
|
|
249
258
|
with acquire_js_buffer(body) as jsbytes:
|
|
@@ -252,7 +261,7 @@ async def process_request(
|
|
|
252
261
|
# 101/103/204/205/304 must not carry a body per the Fetch spec.
|
|
253
262
|
# https://fetch.spec.whatwg.org/#null-body-status
|
|
254
263
|
resp = Response.new(
|
|
255
|
-
None, headers=
|
|
264
|
+
None, headers=_to_js_headers(headers), status=status
|
|
256
265
|
)
|
|
257
266
|
result.set_result(resp)
|
|
258
267
|
finished_response.set()
|
|
@@ -262,7 +271,7 @@ async def process_request(
|
|
|
262
271
|
buf = px.getBuffer()
|
|
263
272
|
px.destroy()
|
|
264
273
|
resp = Response.new(
|
|
265
|
-
buf.data, headers=
|
|
274
|
+
buf.data, headers=_to_js_headers(headers), status=status
|
|
266
275
|
)
|
|
267
276
|
result.set_result(resp)
|
|
268
277
|
finished_response.set()
|
|
@@ -270,7 +279,7 @@ async def process_request(
|
|
|
270
279
|
# Run the application in the background
|
|
271
280
|
async def run_app():
|
|
272
281
|
try:
|
|
273
|
-
await app(request_to_scope(req, env), receive, send)
|
|
282
|
+
await app(request_to_scope(req, env, state=state), receive, send)
|
|
274
283
|
|
|
275
284
|
# If we get here and no response has been set yet, the app didn't generate a response
|
|
276
285
|
if not result.done():
|
|
@@ -377,9 +386,9 @@ async def fetch(
|
|
|
377
386
|
app: Any, req: "Request | js.Request", env: Any, ctx: Context | None = None
|
|
378
387
|
) -> js.Response:
|
|
379
388
|
logger.debug("ASGI request: %s %s", req.method, req.url)
|
|
380
|
-
shutdown = await start_application(app)
|
|
389
|
+
shutdown, state = await start_application(app)
|
|
381
390
|
try:
|
|
382
|
-
result = await process_request(app, req, env, ctx)
|
|
391
|
+
result = await process_request(app, req, env, ctx, state=state)
|
|
383
392
|
except Exception:
|
|
384
393
|
logger.exception("ASGI request failed")
|
|
385
394
|
raise
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# pyright: reportMissingImports=false
|
|
2
|
+
import uuid
|
|
2
3
|
|
|
3
4
|
import pytest
|
|
4
5
|
|
|
@@ -8,3 +9,8 @@ from workers import env as _env
|
|
|
8
9
|
@pytest.fixture
|
|
9
10
|
def env():
|
|
10
11
|
return _env
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def unique_table_name() -> str:
|
|
15
|
+
"""Unique per call"""
|
|
16
|
+
return f"test_{uuid.uuid4().hex[:10]}"
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# pyright: reportMissingImports=false
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
This test requires a MySQL server to be running on localhost.
|
|
5
|
+
|
|
6
|
+
Run mysql with docker:
|
|
7
|
+
|
|
8
|
+
docker run -d --name mysql \
|
|
9
|
+
-e MYSQL_ROOT_PASSWORD=rootpass \
|
|
10
|
+
-e MYSQL_USER=testuser \
|
|
11
|
+
-e MYSQL_PASSWORD=testpass \
|
|
12
|
+
-e MYSQL_DATABASE=testdb \
|
|
13
|
+
-p 3306:3306 \
|
|
14
|
+
--health-cmd="mysqladmin ping -h 127.0.0.1" \
|
|
15
|
+
--health-interval=10s \
|
|
16
|
+
--health-timeout=5s \
|
|
17
|
+
--health-retries=5 \
|
|
18
|
+
mysql:8.4
|
|
19
|
+
|
|
20
|
+
Then run the test:
|
|
21
|
+
|
|
22
|
+
uv run pytest tests/test_bindings.py -m hyperdrive -k mysql
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import sys
|
|
26
|
+
|
|
27
|
+
import pymysql
|
|
28
|
+
import pytest
|
|
29
|
+
from conftest import unique_table_name
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pytest.fixture(autouse=True)
|
|
33
|
+
def skip_if_no_socket_support():
|
|
34
|
+
if sys.version_info.minor < 14:
|
|
35
|
+
pytest.skip("Socket support requires Python 3.14+")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _connect(env):
|
|
39
|
+
hd = env.HYPERDRIVE_MYSQL
|
|
40
|
+
return pymysql.connect(
|
|
41
|
+
host=hd.host,
|
|
42
|
+
port=int(hd.port),
|
|
43
|
+
user=hd.user,
|
|
44
|
+
password=hd.password,
|
|
45
|
+
database=hd.database,
|
|
46
|
+
unix_socket=False,
|
|
47
|
+
# Hyperdrive terminates TLS to the origin, so this hop is plaintext.
|
|
48
|
+
ssl_disabled=True,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@pytest.mark.asyncio
|
|
53
|
+
async def test_connect(env):
|
|
54
|
+
conn = _connect(env)
|
|
55
|
+
cur = conn.cursor()
|
|
56
|
+
cur.execute("SELECT 1")
|
|
57
|
+
assert cur.fetchone() == (1,)
|
|
58
|
+
conn.close()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@pytest.mark.asyncio
|
|
62
|
+
async def test_create_insert_select(env):
|
|
63
|
+
conn = _connect(env)
|
|
64
|
+
table = unique_table_name()
|
|
65
|
+
cur = conn.cursor()
|
|
66
|
+
try:
|
|
67
|
+
cur.execute(
|
|
68
|
+
f"CREATE TABLE {table} "
|
|
69
|
+
"(id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100), value INT)"
|
|
70
|
+
)
|
|
71
|
+
cur.execute(f"INSERT INTO {table} (name, value) VALUES (%s, %s)", ("alpha", 1))
|
|
72
|
+
cur.execute(f"INSERT INTO {table} (name, value) VALUES (%s, %s)", ("beta", 2))
|
|
73
|
+
conn.commit()
|
|
74
|
+
|
|
75
|
+
cur.execute(f"SELECT name, value FROM {table} ORDER BY name")
|
|
76
|
+
assert cur.fetchall() == (("alpha", 1), ("beta", 2))
|
|
77
|
+
finally:
|
|
78
|
+
cur.execute(f"DROP TABLE IF EXISTS {table}")
|
|
79
|
+
conn.commit()
|
|
80
|
+
conn.close()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@pytest.mark.asyncio
|
|
84
|
+
async def test_update(env):
|
|
85
|
+
conn = _connect(env)
|
|
86
|
+
table = unique_table_name()
|
|
87
|
+
cur = conn.cursor()
|
|
88
|
+
try:
|
|
89
|
+
cur.execute(
|
|
90
|
+
f"CREATE TABLE {table} "
|
|
91
|
+
"(id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100), value INT)"
|
|
92
|
+
)
|
|
93
|
+
cur.execute(f"INSERT INTO {table} (name, value) VALUES (%s, %s)", ("alpha", 1))
|
|
94
|
+
conn.commit()
|
|
95
|
+
|
|
96
|
+
cur.execute(
|
|
97
|
+
f"UPDATE {table} SET value = value + 10 WHERE name = %s", ("alpha",)
|
|
98
|
+
)
|
|
99
|
+
conn.commit()
|
|
100
|
+
|
|
101
|
+
cur.execute(f"SELECT value FROM {table} WHERE name = %s", ("alpha",))
|
|
102
|
+
assert cur.fetchone() == (11,)
|
|
103
|
+
finally:
|
|
104
|
+
cur.execute(f"DROP TABLE IF EXISTS {table}")
|
|
105
|
+
conn.commit()
|
|
106
|
+
conn.close()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@pytest.mark.asyncio
|
|
110
|
+
async def test_delete(env):
|
|
111
|
+
conn = _connect(env)
|
|
112
|
+
table = unique_table_name()
|
|
113
|
+
cur = conn.cursor()
|
|
114
|
+
try:
|
|
115
|
+
cur.execute(
|
|
116
|
+
f"CREATE TABLE {table} (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100))"
|
|
117
|
+
)
|
|
118
|
+
cur.execute(f"INSERT INTO {table} (name) VALUES (%s)", ("alpha",))
|
|
119
|
+
cur.execute(f"INSERT INTO {table} (name) VALUES (%s)", ("beta",))
|
|
120
|
+
conn.commit()
|
|
121
|
+
|
|
122
|
+
cur.execute(f"DELETE FROM {table} WHERE name = %s", ("alpha",))
|
|
123
|
+
conn.commit()
|
|
124
|
+
|
|
125
|
+
cur.execute(f"SELECT name FROM {table}")
|
|
126
|
+
assert cur.fetchall() == (("beta",),)
|
|
127
|
+
finally:
|
|
128
|
+
cur.execute(f"DROP TABLE IF EXISTS {table}")
|
|
129
|
+
conn.commit()
|
|
130
|
+
conn.close()
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@pytest.mark.asyncio
|
|
134
|
+
async def test_transaction_rollback(env):
|
|
135
|
+
conn = _connect(env)
|
|
136
|
+
table = unique_table_name()
|
|
137
|
+
cur = conn.cursor()
|
|
138
|
+
try:
|
|
139
|
+
cur.execute(
|
|
140
|
+
f"CREATE TABLE {table} "
|
|
141
|
+
"(id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100)) ENGINE=InnoDB"
|
|
142
|
+
)
|
|
143
|
+
conn.commit()
|
|
144
|
+
|
|
145
|
+
cur.execute(f"INSERT INTO {table} (name) VALUES (%s)", ("should_disappear",))
|
|
146
|
+
conn.rollback()
|
|
147
|
+
|
|
148
|
+
cur.execute(f"SELECT COUNT(*) FROM {table}")
|
|
149
|
+
assert cur.fetchone() == (0,)
|
|
150
|
+
finally:
|
|
151
|
+
cur.execute(f"DROP TABLE IF EXISTS {table}")
|
|
152
|
+
conn.commit()
|
|
153
|
+
conn.close()
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@pytest.mark.asyncio
|
|
157
|
+
async def test_multiple_data_types(env):
|
|
158
|
+
conn = _connect(env)
|
|
159
|
+
table = unique_table_name()
|
|
160
|
+
cur = conn.cursor()
|
|
161
|
+
try:
|
|
162
|
+
cur.execute(
|
|
163
|
+
f"CREATE TABLE {table} ("
|
|
164
|
+
"id INT AUTO_INCREMENT PRIMARY KEY, "
|
|
165
|
+
"text_col VARCHAR(255), "
|
|
166
|
+
"int_col INT, "
|
|
167
|
+
"float_col DOUBLE, "
|
|
168
|
+
"bool_col BOOLEAN)"
|
|
169
|
+
)
|
|
170
|
+
cur.execute(
|
|
171
|
+
f"INSERT INTO {table} (text_col, int_col, float_col, bool_col) "
|
|
172
|
+
"VALUES (%s, %s, %s, %s)",
|
|
173
|
+
("hello", 42, 3.14, True),
|
|
174
|
+
)
|
|
175
|
+
conn.commit()
|
|
176
|
+
|
|
177
|
+
cur.execute(f"SELECT text_col, int_col, float_col, bool_col FROM {table}")
|
|
178
|
+
row = cur.fetchone()
|
|
179
|
+
assert row[0] == "hello"
|
|
180
|
+
assert row[1] == 42
|
|
181
|
+
assert abs(row[2] - 3.14) < 0.001
|
|
182
|
+
assert row[3] == 1
|
|
183
|
+
finally:
|
|
184
|
+
cur.execute(f"DROP TABLE IF EXISTS {table}")
|
|
185
|
+
conn.commit()
|
|
186
|
+
conn.close()
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@pytest.mark.asyncio
|
|
190
|
+
async def test_executemany(env):
|
|
191
|
+
conn = _connect(env)
|
|
192
|
+
table = unique_table_name()
|
|
193
|
+
cur = conn.cursor()
|
|
194
|
+
try:
|
|
195
|
+
cur.execute(
|
|
196
|
+
f"CREATE TABLE {table} (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100))"
|
|
197
|
+
)
|
|
198
|
+
cur.executemany(
|
|
199
|
+
f"INSERT INTO {table} (name) VALUES (%s)", [("a",), ("b",), ("c",)]
|
|
200
|
+
)
|
|
201
|
+
conn.commit()
|
|
202
|
+
|
|
203
|
+
cur.execute(f"SELECT name FROM {table} ORDER BY name")
|
|
204
|
+
assert cur.fetchall() == (("a",), ("b",), ("c",))
|
|
205
|
+
finally:
|
|
206
|
+
cur.execute(f"DROP TABLE IF EXISTS {table}")
|
|
207
|
+
conn.commit()
|
|
208
|
+
conn.close()
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# pyright: reportMissingImports=false
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
This test requires a PostgreSQL server to be running on localhost.
|
|
5
|
+
|
|
6
|
+
Run postgresql with docker:
|
|
7
|
+
|
|
8
|
+
docker run -d --name postgres \
|
|
9
|
+
-e POSTGRES_USER=testuser \
|
|
10
|
+
-e POSTGRES_PASSWORD=testpass \
|
|
11
|
+
-e POSTGRES_DB=testdb \
|
|
12
|
+
-e POSTGRES_HOST_AUTH_METHOD=md5 \
|
|
13
|
+
-e POSTGRES_INITDB_ARGS="--auth-host=md5" \
|
|
14
|
+
-p 5432:5432 \
|
|
15
|
+
--health-cmd="pg_isready -U testuser -d testdb" \
|
|
16
|
+
--health-interval=10s \
|
|
17
|
+
--health-timeout=5s \
|
|
18
|
+
--health-retries=5 \
|
|
19
|
+
postgres:16
|
|
20
|
+
|
|
21
|
+
Then run the test:
|
|
22
|
+
|
|
23
|
+
uv run pytest tests/test_bindings.py -m hyperdrive -k postgresql
|
|
24
|
+
|
|
25
|
+
Note: "POSTGRES_HOST_AUTH_METHOD=md5" is required for PostgreSQL to work with pg8000, since the
|
|
26
|
+
default `scram-sha-256` is not available in the pg8000 with Python workers (missing openssl)
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
import sys
|
|
30
|
+
|
|
31
|
+
import pg8000
|
|
32
|
+
import pytest
|
|
33
|
+
from conftest import unique_table_name
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@pytest.fixture(autouse=True)
|
|
37
|
+
def skip_if_no_socket_support():
|
|
38
|
+
if sys.version_info.minor < 14:
|
|
39
|
+
pytest.skip("Socket support requires Python 3.14+")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _connect(env):
|
|
43
|
+
hd = env.HYPERDRIVE_PG
|
|
44
|
+
return pg8000.connect(
|
|
45
|
+
host=hd.host,
|
|
46
|
+
port=int(hd.port),
|
|
47
|
+
user=hd.user,
|
|
48
|
+
password=hd.password,
|
|
49
|
+
database=hd.database,
|
|
50
|
+
# Hyperdrive terminates TLS to the origin, so this hop is plaintext.
|
|
51
|
+
ssl_context=False,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@pytest.mark.asyncio
|
|
56
|
+
async def test_connect(env):
|
|
57
|
+
conn = _connect(env)
|
|
58
|
+
cur = conn.cursor()
|
|
59
|
+
cur.execute("SELECT 1")
|
|
60
|
+
assert cur.fetchone() == [1]
|
|
61
|
+
conn.close()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@pytest.mark.asyncio
|
|
65
|
+
async def test_create_insert_select(env):
|
|
66
|
+
conn = _connect(env)
|
|
67
|
+
table = unique_table_name()
|
|
68
|
+
cur = conn.cursor()
|
|
69
|
+
try:
|
|
70
|
+
cur.execute(
|
|
71
|
+
f"CREATE TABLE {table} (id SERIAL PRIMARY KEY, name TEXT, value INT)"
|
|
72
|
+
)
|
|
73
|
+
cur.execute(f"INSERT INTO {table} (name, value) VALUES (%s, %s)", ("alpha", 1))
|
|
74
|
+
cur.execute(f"INSERT INTO {table} (name, value) VALUES (%s, %s)", ("beta", 2))
|
|
75
|
+
conn.commit()
|
|
76
|
+
|
|
77
|
+
cur.execute(f"SELECT name, value FROM {table} ORDER BY name")
|
|
78
|
+
assert list(cur.fetchall()) == [["alpha", 1], ["beta", 2]]
|
|
79
|
+
finally:
|
|
80
|
+
cur.execute(f"DROP TABLE IF EXISTS {table}")
|
|
81
|
+
conn.commit()
|
|
82
|
+
conn.close()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@pytest.mark.asyncio
|
|
86
|
+
async def test_update(env):
|
|
87
|
+
conn = _connect(env)
|
|
88
|
+
table = unique_table_name()
|
|
89
|
+
cur = conn.cursor()
|
|
90
|
+
try:
|
|
91
|
+
cur.execute(
|
|
92
|
+
f"CREATE TABLE {table} (id SERIAL PRIMARY KEY, name TEXT, value INT)"
|
|
93
|
+
)
|
|
94
|
+
cur.execute(f"INSERT INTO {table} (name, value) VALUES (%s, %s)", ("alpha", 1))
|
|
95
|
+
conn.commit()
|
|
96
|
+
|
|
97
|
+
cur.execute(
|
|
98
|
+
f"UPDATE {table} SET value = value + 10 WHERE name = %s", ("alpha",)
|
|
99
|
+
)
|
|
100
|
+
conn.commit()
|
|
101
|
+
|
|
102
|
+
cur.execute(f"SELECT value FROM {table} WHERE name = %s", ("alpha",))
|
|
103
|
+
assert cur.fetchone() == [11]
|
|
104
|
+
finally:
|
|
105
|
+
cur.execute(f"DROP TABLE IF EXISTS {table}")
|
|
106
|
+
conn.commit()
|
|
107
|
+
conn.close()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@pytest.mark.asyncio
|
|
111
|
+
async def test_delete(env):
|
|
112
|
+
conn = _connect(env)
|
|
113
|
+
table = unique_table_name()
|
|
114
|
+
cur = conn.cursor()
|
|
115
|
+
try:
|
|
116
|
+
cur.execute(f"CREATE TABLE {table} (id SERIAL PRIMARY KEY, name TEXT)")
|
|
117
|
+
cur.execute(f"INSERT INTO {table} (name) VALUES (%s)", ("alpha",))
|
|
118
|
+
cur.execute(f"INSERT INTO {table} (name) VALUES (%s)", ("beta",))
|
|
119
|
+
conn.commit()
|
|
120
|
+
|
|
121
|
+
cur.execute(f"DELETE FROM {table} WHERE name = %s", ("alpha",))
|
|
122
|
+
conn.commit()
|
|
123
|
+
|
|
124
|
+
cur.execute(f"SELECT name FROM {table}")
|
|
125
|
+
assert list(cur.fetchall()) == [["beta"]]
|
|
126
|
+
finally:
|
|
127
|
+
cur.execute(f"DROP TABLE IF EXISTS {table}")
|
|
128
|
+
conn.commit()
|
|
129
|
+
conn.close()
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@pytest.mark.asyncio
|
|
133
|
+
async def test_transaction_rollback(env):
|
|
134
|
+
conn = _connect(env)
|
|
135
|
+
table = unique_table_name()
|
|
136
|
+
cur = conn.cursor()
|
|
137
|
+
try:
|
|
138
|
+
cur.execute(f"CREATE TABLE {table} (id SERIAL PRIMARY KEY, name TEXT)")
|
|
139
|
+
conn.commit()
|
|
140
|
+
|
|
141
|
+
cur.execute(f"INSERT INTO {table} (name) VALUES (%s)", ("should_disappear",))
|
|
142
|
+
conn.rollback()
|
|
143
|
+
|
|
144
|
+
cur.execute(f"SELECT COUNT(*) FROM {table}")
|
|
145
|
+
assert cur.fetchone() == [0]
|
|
146
|
+
finally:
|
|
147
|
+
cur.execute(f"DROP TABLE IF EXISTS {table}")
|
|
148
|
+
conn.commit()
|
|
149
|
+
conn.close()
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@pytest.mark.asyncio
|
|
153
|
+
async def test_multiple_data_types(env):
|
|
154
|
+
conn = _connect(env)
|
|
155
|
+
table = unique_table_name()
|
|
156
|
+
cur = conn.cursor()
|
|
157
|
+
try:
|
|
158
|
+
cur.execute(
|
|
159
|
+
f"CREATE TABLE {table} ("
|
|
160
|
+
"id SERIAL PRIMARY KEY, "
|
|
161
|
+
"text_col TEXT, "
|
|
162
|
+
"int_col INT, "
|
|
163
|
+
"float_col DOUBLE PRECISION, "
|
|
164
|
+
"bool_col BOOLEAN)"
|
|
165
|
+
)
|
|
166
|
+
cur.execute(
|
|
167
|
+
f"INSERT INTO {table} (text_col, int_col, float_col, bool_col) "
|
|
168
|
+
"VALUES (%s, %s, %s, %s)",
|
|
169
|
+
("hello", 42, 3.14, True),
|
|
170
|
+
)
|
|
171
|
+
conn.commit()
|
|
172
|
+
|
|
173
|
+
cur.execute(f"SELECT text_col, int_col, float_col, bool_col FROM {table}")
|
|
174
|
+
row = cur.fetchone()
|
|
175
|
+
assert row[0] == "hello"
|
|
176
|
+
assert row[1] == 42
|
|
177
|
+
assert abs(row[2] - 3.14) < 0.001
|
|
178
|
+
assert row[3] is True
|
|
179
|
+
finally:
|
|
180
|
+
cur.execute(f"DROP TABLE IF EXISTS {table}")
|
|
181
|
+
conn.commit()
|
|
182
|
+
conn.close()
|
{workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/wrangler.jsonc
RENAMED
|
@@ -49,6 +49,18 @@
|
|
|
49
49
|
"analytics_engine_datasets": [
|
|
50
50
|
{ "binding": "ANALYTICS", "dataset": "test-dataset" }
|
|
51
51
|
],
|
|
52
|
+
"hyperdrive": [
|
|
53
|
+
{
|
|
54
|
+
"binding": "HYPERDRIVE_PG",
|
|
55
|
+
"id": "00000000-0000-0000-0000-000000000001",
|
|
56
|
+
"localConnectionString": "postgres://testuser:testpass@127.0.0.1:5432/testdb"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"binding": "HYPERDRIVE_MYSQL",
|
|
60
|
+
"id": "00000000-0000-0000-0000-000000000002",
|
|
61
|
+
"localConnectionString": "mysql://testuser:testpass@127.0.0.1:3306/testdb"
|
|
62
|
+
}
|
|
63
|
+
],
|
|
52
64
|
"images": { "binding": "IMAGES" },
|
|
53
65
|
"ratelimits": [
|
|
54
66
|
{ "name": "RATE_LIMITER", "namespace_id": "1001", "simple": { "period": 60, "limit": 100 } }
|
|
@@ -24,6 +24,22 @@ DEV_POLL_INTERVAL: float = 0.5
|
|
|
24
24
|
SUITE_CONNECT_TIMEOUT: int = 10
|
|
25
25
|
SUITE_READ_TIMEOUT: int = 300
|
|
26
26
|
|
|
27
|
+
OPT_IN_MARKERS: tuple[str, ...] = ("hyperdrive",)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def pytest_collection_modifyitems(
|
|
31
|
+
config: pytest.Config, items: list[pytest.Item]
|
|
32
|
+
) -> None:
|
|
33
|
+
"""Skip opt-in suites unless the run explicitly asks for them via ``-m``."""
|
|
34
|
+
markexpr: str = config.getoption("markexpr")
|
|
35
|
+
for marker in OPT_IN_MARKERS:
|
|
36
|
+
if marker in markexpr:
|
|
37
|
+
continue
|
|
38
|
+
skip = pytest.mark.skip(reason=f"needs local services; run with -m {marker}")
|
|
39
|
+
for item in items:
|
|
40
|
+
if marker in item.keywords:
|
|
41
|
+
item.add_marker(skip)
|
|
42
|
+
|
|
27
43
|
|
|
28
44
|
@dataclass(frozen=True)
|
|
29
45
|
class CompatConfig:
|
|
@@ -256,12 +272,19 @@ def discover_suites(src_dir: Path) -> dict[str, list[str]]:
|
|
|
256
272
|
}
|
|
257
273
|
|
|
258
274
|
|
|
259
|
-
def register_in_worker_suites(
|
|
275
|
+
def register_in_worker_suites(
|
|
276
|
+
namespace: dict[str, Any],
|
|
277
|
+
src_dir: Path,
|
|
278
|
+
marks: dict[str, pytest.MarkDecorator] | None = None,
|
|
279
|
+
) -> None:
|
|
260
280
|
"""Define a ``TestXxx`` class in `namespace` for every suite found in `src_dir`.
|
|
261
281
|
|
|
262
282
|
Call with ``globals()`` from a test module so each in-worker test surfaces as
|
|
263
|
-
its own pytest case without manual registration.
|
|
283
|
+
its own pytest case without manual registration. `marks` applies a marker to
|
|
284
|
+
the class generated for the suite of the same name.
|
|
264
285
|
"""
|
|
265
286
|
for suite, test_names in discover_suites(src_dir).items():
|
|
266
287
|
suite_cls = make_suite_class(suite, test_names)
|
|
288
|
+
if marks and suite in marks:
|
|
289
|
+
suite_cls = marks[suite](suite_cls)
|
|
267
290
|
namespace[suite_cls.__name__] = suite_cls
|
|
@@ -18,10 +18,18 @@ from conftest import register_in_worker_suites
|
|
|
18
18
|
BINDINGS_TEST_DIR: Path = Path(__file__).parent / "bindings-test"
|
|
19
19
|
BINDINGS_SRC_DIR: Path = BINDINGS_TEST_DIR / "src"
|
|
20
20
|
|
|
21
|
+
SUITE_MARKS: dict[str, pytest.MarkDecorator] = {
|
|
22
|
+
# hyperdrive tests require external databases to be running
|
|
23
|
+
# so it is skipped by default.
|
|
24
|
+
# See each test file for details on how to run them.
|
|
25
|
+
"hyperdrive_postgresql": pytest.mark.hyperdrive,
|
|
26
|
+
"hyperdrive_mysql": pytest.mark.hyperdrive,
|
|
27
|
+
}
|
|
28
|
+
|
|
21
29
|
|
|
22
30
|
@pytest.fixture(scope="module")
|
|
23
31
|
def worker_project_dir() -> Path:
|
|
24
32
|
return BINDINGS_TEST_DIR
|
|
25
33
|
|
|
26
34
|
|
|
27
|
-
register_in_worker_suites(globals(), BINDINGS_SRC_DIR)
|
|
35
|
+
register_in_worker_suites(globals(), BINDINGS_SRC_DIR, marks=SUITE_MARKS)
|