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.
Files changed (176) hide show
  1. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/CHANGELOG.md +17 -0
  2. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/PKG-INFO +1 -1
  3. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/pyproject.toml +4 -1
  4. workers_runtime_sdk-1.6.14/src/asgi.py +6 -0
  5. {workers_runtime_sdk-1.6.12/src → workers_runtime_sdk-1.6.14/src/workers}/asgi.py +22 -13
  6. workers_runtime_sdk-1.6.14/tests/bindings-test/pyproject.toml +5 -0
  7. {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
  8. workers_runtime_sdk-1.6.14/tests/bindings-test/src/test_hyperdrive_mysql.py +208 -0
  9. workers_runtime_sdk-1.6.14/tests/bindings-test/src/test_hyperdrive_postgresql.py +182 -0
  10. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/wrangler.jsonc +12 -0
  11. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/conftest.py +25 -2
  12. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/test_bindings.py +9 -1
  13. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/pyproject.toml +1 -0
  14. workers_runtime_sdk-1.6.14/tests/web-frameworks-test/fastapi-tests/src/_client.py +66 -0
  15. workers_runtime_sdk-1.6.14/tests/web-frameworks-test/fastapi-tests/src/test_error_handling.py +156 -0
  16. workers_runtime_sdk-1.6.14/tests/web-frameworks-test/fastapi-tests/src/test_platform_lifecycle.py +12 -0
  17. workers_runtime_sdk-1.6.14/tests/web-frameworks-test/fastapi-tests/src/test_response_types.py +183 -0
  18. workers_runtime_sdk-1.6.14/tests/web-frameworks-test/fastapi-tests/src/test_routing.py +190 -0
  19. workers_runtime_sdk-1.6.14/tests/web-frameworks-test/fastapi-tests/src/test_sync_handlers.py +128 -0
  20. workers_runtime_sdk-1.6.14/tests/web-frameworks-test/fastapi-tests/src/worker.py +560 -0
  21. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/asgi/tests/test_asgi.py +16 -0
  22. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/asgi/worker.py +26 -0
  23. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/uv.lock +1 -1
  24. workers_runtime_sdk-1.6.12/tests/bindings-test/pyproject.toml +0 -5
  25. workers_runtime_sdk-1.6.12/tests/web-frameworks-test/fastapi-tests/src/_client.py +0 -26
  26. workers_runtime_sdk-1.6.12/tests/web-frameworks-test/fastapi-tests/src/worker.py +0 -159
  27. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/.gitignore +0 -0
  28. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/AGENTS.md +0 -0
  29. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/README.md +0 -0
  30. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/_cloudflare_compat_flags.pyi +0 -0
  31. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/_pyodide_entrypoint_helper.pyi +0 -0
  32. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/_workers_sdk_entropy_import_context.py +0 -0
  33. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/_workers_sdk_package_patches.py +0 -0
  34. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/_workers_sdk_patch_loader.pth +0 -0
  35. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/_workers_sdk_patch_loader.py +0 -0
  36. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/__init__.py +0 -0
  37. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/blob.py +0 -0
  38. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/entrypoints.py +0 -0
  39. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/fetch.py +0 -0
  40. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/formdata.py +0 -0
  41. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/py.typed +0 -0
  42. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/request.py +0 -0
  43. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/response.py +0 -0
  44. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/rpc.py +0 -0
  45. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/types.py +0 -0
  46. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/utils.py +0 -0
  47. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/workflows.py +0 -0
  48. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/src/workers/wsgi.py +0 -0
  49. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/public/data.json +0 -0
  50. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/public/hello.txt +0 -0
  51. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_analytics_engine.py +0 -0
  52. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_assets.py +0 -0
  53. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_d1.py +0 -0
  54. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_do.py +0 -0
  55. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_images.py +0 -0
  56. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_kv.py +0 -0
  57. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_mtls.py +0 -0
  58. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_queue.py +0 -0
  59. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_r2.py +0 -0
  60. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_ratelimit.py +0 -0
  61. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_vectorize.py +0 -0
  62. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_version_metadata.py +0 -0
  63. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/test_workflow.py +0 -0
  64. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/worker.py +0 -0
  65. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/worker_durable_object.py +0 -0
  66. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/bindings-test/src/worker_workflow.py +0 -0
  67. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/test_fastapi.py +0 -0
  68. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/test_in_workerd.py +0 -0
  69. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/test_web_frameworks.py +0 -0
  70. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/pyproject.toml +0 -0
  71. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/_client.py +0 -0
  72. {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
  73. {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
  74. {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
  75. {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
  76. {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
  77. {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
  78. {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
  79. {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
  80. {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
  81. {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
  82. {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
  83. {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
  84. {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
  85. {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
  86. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/templates/base.html +0 -0
  87. {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
  88. {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
  89. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/templates/hello.html +0 -0
  90. {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
  91. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_auth.py +0 -0
  92. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_caching.py +0 -0
  93. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_cors.py +0 -0
  94. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_csrf.py +0 -0
  95. {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
  96. {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
  97. {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
  98. {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
  99. {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
  100. {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
  101. {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
  102. {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
  103. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_forms.py +0 -0
  104. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_middleware.py +0 -0
  105. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_pagination.py +0 -0
  106. {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
  107. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_sessions.py +0 -0
  108. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_signals.py +0 -0
  109. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_streaming.py +0 -0
  110. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/test_templates.py +0 -0
  111. {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
  112. {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
  113. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/src/worker.py +0 -0
  114. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/django-async/wrangler.jsonc +0 -0
  115. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/public/data.json +0 -0
  116. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/public/index.html +0 -0
  117. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/public/style.css +0 -0
  118. {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
  119. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/src/static/app.css +0 -0
  120. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/src/static/data.json +0 -0
  121. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/src/static/hello.txt +0 -0
  122. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/src/static/index.html +0 -0
  123. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/src/test_assets.py +0 -0
  124. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/src/test_staticfiles.py +0 -0
  125. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/web-frameworks-test/fastapi-tests/wrangler.jsonc +0 -0
  126. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/asgi/asgi.wd-test +0 -0
  127. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/asgi/pyproject.toml +0 -0
  128. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/asgi/wrangler.jsonc +0 -0
  129. {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
  130. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/asgi-ws-disconnect/pyproject.toml +0 -0
  131. {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
  132. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/asgi-ws-disconnect/worker.py +0 -0
  133. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/asgi-ws-disconnect/wrangler.jsonc +0 -0
  134. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object/durable-object.wd-test +0 -0
  135. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object/pyproject.toml +0 -0
  136. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object/worker.py +0 -0
  137. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object/wrangler.jsonc +0 -0
  138. {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
  139. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-abort/pyproject.toml +0 -0
  140. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-abort/worker.py +0 -0
  141. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-abort/wrangler.jsonc +0 -0
  142. {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
  143. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-inheritance/pyproject.toml +0 -0
  144. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-inheritance/worker.py +0 -0
  145. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-inheritance/wrangler.jsonc +0 -0
  146. {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
  147. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-websocket/pyproject.toml +0 -0
  148. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-websocket/tester.js +0 -0
  149. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-websocket/worker.py +0 -0
  150. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/durable-object-websocket/wrangler.jsonc +0 -0
  151. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/entropy-patches.wd-test +0 -0
  152. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/pyproject.toml +0 -0
  153. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/tests/test_aiohttp_websocket.py +0 -0
  154. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/tests/test_langsmith.py +0 -0
  155. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/tests/test_numpy.py +0 -0
  156. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/tests/test_pydantic.py +0 -0
  157. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/tests/test_rust_packages.py +0 -0
  158. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/tests/test_ssl_avoidance.py +0 -0
  159. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/worker.py +0 -0
  160. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/entropy-patches/wrangler.jsonc +0 -0
  161. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/python-rpc/pyproject.toml +0 -0
  162. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/python-rpc/python-rpc.wd-test +0 -0
  163. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/python-rpc/worker.js +0 -0
  164. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/python-rpc/worker.py +0 -0
  165. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/python-rpc/wrangler.jsonc +0 -0
  166. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/sdk/pyproject.toml +0 -0
  167. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/sdk/sdk.wd-test +0 -0
  168. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/sdk/server.py +0 -0
  169. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/sdk/tests/test_sdk.py +0 -0
  170. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/sdk/worker.py +0 -0
  171. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/sdk/wrangler.jsonc +0 -0
  172. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/wsgi/pyproject.toml +0 -0
  173. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/wsgi/tests/test_wsgi.py +0 -0
  174. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/wsgi/worker.py +0 -0
  175. {workers_runtime_sdk-1.6.12 → workers_runtime_sdk-1.6.14}/tests/workerd-test/wsgi/wrangler.jsonc +0 -0
  176. {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.12
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.12"
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"]
@@ -0,0 +1,6 @@
1
+ # Compatibility module for existing code that imports `asgi` directly.
2
+ import sys
3
+
4
+ from workers import asgi as _asgi
5
+
6
+ sys.modules[__name__] = _asgi
@@ -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
- return {
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 Object, Response, TransformStream
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=Object.fromEntries(headers), status=status
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=Object.fromEntries(headers), status=status
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=Object.fromEntries(headers), status=status
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
@@ -0,0 +1,5 @@
1
+ [project]
2
+ name = "bindings-test"
3
+ version = "0.1.0"
4
+ requires-python = ">=3.12"
5
+ dependencies = ["pytest", "pytest-asyncio<1.2.0", "pg8000", "pymysql", "cryptography"]
@@ -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()
@@ -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(namespace: dict[str, Any], src_dir: Path) -> None:
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)