workers-runtime-sdk 1.6.13__tar.gz → 1.6.15__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.13 → workers_runtime_sdk-1.6.15}/CHANGELOG.md +16 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/PKG-INFO +1 -1
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/pyproject.toml +4 -1
- workers_runtime_sdk-1.6.15/src/asgi.py +6 -0
- {workers_runtime_sdk-1.6.13/src → workers_runtime_sdk-1.6.15/src/workers}/asgi.py +38 -17
- workers_runtime_sdk-1.6.15/tests/bindings-test/pyproject.toml +5 -0
- {workers_runtime_sdk-1.6.13/tests/web-frameworks-test/fastapi-tests → workers_runtime_sdk-1.6.15/tests/bindings-test}/src/conftest.py +6 -0
- workers_runtime_sdk-1.6.15/tests/bindings-test/src/test_hyperdrive_mysql.py +208 -0
- workers_runtime_sdk-1.6.15/tests/bindings-test/src/test_hyperdrive_postgresql.py +182 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/bindings-test/wrangler.jsonc +12 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/conftest.py +25 -2
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/test_bindings.py +9 -1
- workers_runtime_sdk-1.6.15/tests/web-frameworks-test/fastapi-tests/src/test_error_handling.py +156 -0
- workers_runtime_sdk-1.6.15/tests/web-frameworks-test/fastapi-tests/src/test_platform_lifecycle.py +12 -0
- workers_runtime_sdk-1.6.15/tests/web-frameworks-test/fastapi-tests/src/test_response_types.py +183 -0
- workers_runtime_sdk-1.6.15/tests/web-frameworks-test/fastapi-tests/src/test_routing.py +190 -0
- workers_runtime_sdk-1.6.15/tests/web-frameworks-test/fastapi-tests/src/worker.py +560 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/asgi/tests/test_asgi.py +7 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/asgi-ws-disconnect/tests/test_ws_disconnect.py +19 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/asgi-ws-disconnect/worker.py +43 -1
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/uv.lock +1 -1
- workers_runtime_sdk-1.6.13/tests/bindings-test/pyproject.toml +0 -5
- workers_runtime_sdk-1.6.13/tests/web-frameworks-test/fastapi-tests/src/worker.py +0 -251
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/.gitignore +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/AGENTS.md +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/README.md +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/src/_cloudflare_compat_flags.pyi +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/src/_pyodide_entrypoint_helper.pyi +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/src/_workers_sdk_entropy_import_context.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/src/_workers_sdk_package_patches.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/src/_workers_sdk_patch_loader.pth +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/src/_workers_sdk_patch_loader.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/src/workers/__init__.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/src/workers/blob.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/src/workers/entrypoints.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/src/workers/fetch.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/src/workers/formdata.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/src/workers/py.typed +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/src/workers/request.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/src/workers/response.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/src/workers/rpc.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/src/workers/types.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/src/workers/utils.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/src/workers/workflows.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/src/workers/wsgi.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/bindings-test/public/data.json +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/bindings-test/public/hello.txt +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/bindings-test/src/test_analytics_engine.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/bindings-test/src/test_assets.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/bindings-test/src/test_d1.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/bindings-test/src/test_do.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/bindings-test/src/test_images.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/bindings-test/src/test_kv.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/bindings-test/src/test_mtls.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/bindings-test/src/test_queue.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/bindings-test/src/test_r2.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/bindings-test/src/test_ratelimit.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/bindings-test/src/test_vectorize.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/bindings-test/src/test_version_metadata.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/bindings-test/src/test_workflow.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/bindings-test/src/worker.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/bindings-test/src/worker_durable_object.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/bindings-test/src/worker_workflow.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/test_fastapi.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/test_in_workerd.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/test_web_frameworks.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/_client.py +0 -0
- {workers_runtime_sdk-1.6.13/tests/bindings-test → workers_runtime_sdk-1.6.15/tests/web-frameworks-test/django-async}/src/conftest.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/django_app/__init__.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/django_app/api_urls.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/django_app/auth_backend.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/django_app/drf_auth.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/django_app/drf_serializers.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/django_app/drf_throttling.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/django_app/drf_views.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/django_app/forms.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/django_app/middleware.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/django_app/templatetags/__init__.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/django_app/templatetags/custom_tags.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/django_app/urls.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/django_app/views.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/templates/base.html +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/templates/context_test.html +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/templates/csrf_form.html +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/templates/hello.html +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_async_utilities.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_auth.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_caching.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_cors.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_csrf.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_drf_auth_permissions.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_drf_content_negotiation.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_drf_serializers.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_drf_throttling.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_drf_versioning.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_drf_views.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_error_handling.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_file_uploads.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_forms.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_middleware.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_pagination.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_request_response.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_sessions.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_signals.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_streaming.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_templates.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_url_routing.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/test_views_basic.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/src/worker.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/django-async/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/fastapi-tests/public/data.json +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/fastapi-tests/public/index.html +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/fastapi-tests/public/style.css +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/fastapi-tests/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/fastapi-tests/src/_client.py +0 -0
- {workers_runtime_sdk-1.6.13/tests/web-frameworks-test/django-async → workers_runtime_sdk-1.6.15/tests/web-frameworks-test/fastapi-tests}/src/conftest.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/fastapi-tests/src/static/app.css +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/fastapi-tests/src/static/data.json +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/fastapi-tests/src/static/hello.txt +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/fastapi-tests/src/static/index.html +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/fastapi-tests/src/test_assets.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/fastapi-tests/src/test_staticfiles.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/fastapi-tests/src/test_sync_handlers.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/web-frameworks-test/fastapi-tests/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/asgi/asgi.wd-test +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/asgi/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/asgi/worker.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/asgi/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/asgi-ws-disconnect/asgi-ws-disconnect.wd-test +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/asgi-ws-disconnect/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/asgi-ws-disconnect/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/durable-object/durable-object.wd-test +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/durable-object/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/durable-object/worker.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/durable-object/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/durable-object-abort/durable-object-abort.wd-test +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/durable-object-abort/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/durable-object-abort/worker.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/durable-object-abort/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/durable-object-inheritance/durable-object-inheritance.wd-test +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/durable-object-inheritance/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/durable-object-inheritance/worker.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/durable-object-inheritance/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/durable-object-websocket/durable-object-websocket.wd-test +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/durable-object-websocket/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/durable-object-websocket/tester.js +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/durable-object-websocket/worker.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/durable-object-websocket/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/entropy-patches/entropy-patches.wd-test +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/entropy-patches/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/entropy-patches/tests/test_aiohttp_websocket.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/entropy-patches/tests/test_langsmith.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/entropy-patches/tests/test_numpy.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/entropy-patches/tests/test_pydantic.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/entropy-patches/tests/test_rust_packages.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/entropy-patches/tests/test_ssl_avoidance.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/entropy-patches/worker.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/entropy-patches/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/python-rpc/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/python-rpc/python-rpc.wd-test +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/python-rpc/worker.js +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/python-rpc/worker.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/python-rpc/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/sdk/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/sdk/sdk.wd-test +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/sdk/server.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/sdk/tests/test_sdk.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/sdk/worker.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/sdk/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/wsgi/pyproject.toml +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/wsgi/tests/test_wsgi.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/wsgi/worker.py +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/wsgi/wrangler.jsonc +0 -0
- {workers_runtime_sdk-1.6.13 → workers_runtime_sdk-1.6.15}/tests/workerd-test/wsgi/wsgi.wd-test +0 -0
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- version list -->
|
|
4
4
|
|
|
5
|
+
## v1.6.15 (2026-08-21)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- Handle ASGI WebSocket close events and environment propagation
|
|
10
|
+
([`47cc488`](https://github.com/cloudflare/workers-py/commit/47cc488e2bad1c9f3030a16d73301b80fca90f5c))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## v1.6.14 (2026-08-19)
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
- Fixes fastapi lifespan state not persisting
|
|
18
|
+
([`fadd19b`](https://github.com/cloudflare/workers-py/commit/fadd19bcb6ca47518d5e42562879164dd62ab404))
|
|
19
|
+
|
|
20
|
+
|
|
5
21
|
## v1.6.13 (2026-08-12)
|
|
6
22
|
|
|
7
23
|
### 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.15
|
|
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.15"
|
|
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"]
|
|
@@ -49,7 +49,7 @@ def acquire_js_buffer(pybuffer):
|
|
|
49
49
|
buf.release()
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
def request_to_scope(req, env, ws=False):
|
|
52
|
+
def request_to_scope(req, env, ws=False, state=None):
|
|
53
53
|
from js import URL
|
|
54
54
|
|
|
55
55
|
# @app.get("/example")
|
|
@@ -74,7 +74,7 @@ def request_to_scope(req, env, ws=False):
|
|
|
74
74
|
ty = "websocket"
|
|
75
75
|
else:
|
|
76
76
|
ty = "http"
|
|
77
|
-
|
|
77
|
+
scope = {
|
|
78
78
|
"asgi": ASGI,
|
|
79
79
|
"headers": headers,
|
|
80
80
|
"http_version": "1.1",
|
|
@@ -86,6 +86,10 @@ def request_to_scope(req, env, ws=False):
|
|
|
86
86
|
"type": ty,
|
|
87
87
|
"env": env,
|
|
88
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
|
|
89
93
|
|
|
90
94
|
|
|
91
95
|
async def start_application(app):
|
|
@@ -94,6 +98,7 @@ async def start_application(app):
|
|
|
94
98
|
# don't implement it and fall back to serving requests without lifespan.
|
|
95
99
|
# https://asgi.readthedocs.io/en/latest/specs/lifespan.html
|
|
96
100
|
receive_queue = Queue()
|
|
101
|
+
state = {}
|
|
97
102
|
await receive_queue.put({"type": "lifespan.startup"})
|
|
98
103
|
|
|
99
104
|
# `startup` resolves True on `lifespan.startup.complete`, False when the app
|
|
@@ -141,7 +146,7 @@ async def start_application(app):
|
|
|
141
146
|
await app(
|
|
142
147
|
{
|
|
143
148
|
"asgi": ASGI,
|
|
144
|
-
"state":
|
|
149
|
+
"state": state,
|
|
145
150
|
"type": "lifespan",
|
|
146
151
|
},
|
|
147
152
|
receive,
|
|
@@ -168,17 +173,18 @@ async def start_application(app):
|
|
|
168
173
|
run_in_background(run_lifespan())
|
|
169
174
|
supported = await startup
|
|
170
175
|
if not supported:
|
|
171
|
-
return no_lifespan_shutdown
|
|
172
|
-
return shutdown
|
|
176
|
+
return no_lifespan_shutdown, state
|
|
177
|
+
return shutdown, state
|
|
173
178
|
|
|
174
179
|
|
|
175
|
-
async def process_request(
|
|
180
|
+
async def process_request( # noqa: PLR0913
|
|
176
181
|
app: Any,
|
|
177
182
|
req: "Request | js.Request",
|
|
178
183
|
env: Any,
|
|
179
184
|
# added for waitUntil, but not used anymore
|
|
180
185
|
# TODO(later): remove this parameter after unvendoring Python SDK from workerd
|
|
181
186
|
ctx: Context | None,
|
|
187
|
+
state: dict[str, Any] | None = None,
|
|
182
188
|
) -> js.Response:
|
|
183
189
|
from js import Response, TransformStream
|
|
184
190
|
from pyodide.ffi import create_proxy
|
|
@@ -273,7 +279,7 @@ async def process_request(
|
|
|
273
279
|
# Run the application in the background
|
|
274
280
|
async def run_app():
|
|
275
281
|
try:
|
|
276
|
-
await app(request_to_scope(req, env), receive, send)
|
|
282
|
+
await app(request_to_scope(req, env, state=state), receive, send)
|
|
277
283
|
|
|
278
284
|
# If we get here and no response has been set yet, the app didn't generate a response
|
|
279
285
|
if not result.done():
|
|
@@ -311,7 +317,9 @@ async def process_request(
|
|
|
311
317
|
app_task.destroy()
|
|
312
318
|
|
|
313
319
|
|
|
314
|
-
async def process_websocket(
|
|
320
|
+
async def process_websocket(
|
|
321
|
+
app: Any, req: "Request | js.Request", env: Any = None
|
|
322
|
+
) -> js.Response:
|
|
315
323
|
from js import Response, WebSocketPair
|
|
316
324
|
|
|
317
325
|
client, server = WebSocketPair.new().object_values()
|
|
@@ -352,6 +360,10 @@ async def process_websocket(app: Any, req: "Request | js.Request") -> js.Respons
|
|
|
352
360
|
server.onmessage = onmessage
|
|
353
361
|
|
|
354
362
|
async def ws_send(got):
|
|
363
|
+
if got["type"] == "websocket.accept":
|
|
364
|
+
# The Workers WebSocketPair is accepted before the upgrade response
|
|
365
|
+
# is returned, so there is no deferred accept operation here.
|
|
366
|
+
return
|
|
355
367
|
if got["type"] == "websocket.send":
|
|
356
368
|
b = got.get("bytes", None)
|
|
357
369
|
s = got.get("text", None)
|
|
@@ -362,16 +374,23 @@ async def process_websocket(app: Any, req: "Request | js.Request") -> js.Respons
|
|
|
362
374
|
server.send(jsbytes)
|
|
363
375
|
if s is not None:
|
|
364
376
|
server.send(s)
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
377
|
+
return
|
|
378
|
+
if got["type"] == "websocket.close":
|
|
379
|
+
server.close(got.get("code", 1000), got.get("reason", ""))
|
|
380
|
+
return
|
|
381
|
+
logger.warning(" == Not implemented %s", got["type"])
|
|
368
382
|
|
|
369
383
|
async def ws_receive():
|
|
370
384
|
received = await queue.get()
|
|
371
385
|
return received
|
|
372
386
|
|
|
373
|
-
|
|
374
|
-
|
|
387
|
+
run_in_background(
|
|
388
|
+
app(
|
|
389
|
+
request_to_scope(req, env if env is not None else {}, ws=True),
|
|
390
|
+
ws_receive,
|
|
391
|
+
ws_send,
|
|
392
|
+
)
|
|
393
|
+
)
|
|
375
394
|
|
|
376
395
|
return Response.new(None, status=101, webSocket=client)
|
|
377
396
|
|
|
@@ -380,9 +399,9 @@ async def fetch(
|
|
|
380
399
|
app: Any, req: "Request | js.Request", env: Any, ctx: Context | None = None
|
|
381
400
|
) -> js.Response:
|
|
382
401
|
logger.debug("ASGI request: %s %s", req.method, req.url)
|
|
383
|
-
shutdown = await start_application(app)
|
|
402
|
+
shutdown, state = await start_application(app)
|
|
384
403
|
try:
|
|
385
|
-
result = await process_request(app, req, env, ctx)
|
|
404
|
+
result = await process_request(app, req, env, ctx, state=state)
|
|
386
405
|
except Exception:
|
|
387
406
|
logger.exception("ASGI request failed")
|
|
388
407
|
raise
|
|
@@ -390,8 +409,10 @@ async def fetch(
|
|
|
390
409
|
return result
|
|
391
410
|
|
|
392
411
|
|
|
393
|
-
async def websocket(
|
|
394
|
-
|
|
412
|
+
async def websocket(
|
|
413
|
+
app: Any, req: "Request | js.Request", env: Any = None
|
|
414
|
+
) -> js.Response:
|
|
415
|
+
return await process_websocket(app, req, env)
|
|
395
416
|
|
|
396
417
|
|
|
397
418
|
def __getattr__(name):
|
|
@@ -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.13 → workers_runtime_sdk-1.6.15}/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
|