From 39c573ab6c81ac6a70b4bb7d64e86ec8bc912283 Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Mon, 7 Sep 2026 20:18:50 +0900 Subject: [PATCH 1/4] test(runtime-sdk): test aiomysql and psycopg --- .github/workflows/tests.yml | 9 ++++++- packages/cli/src/pywrangler/utils.py | 2 +- .../tests/bindings-test/pyproject.toml | 13 +++++++++- .../src/test_hyperdrive_mysql.py | 21 ++++++++++++++++ .../src/test_hyperdrive_postgresql.py | 25 +++++++++++++++++++ 5 files changed, 67 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6d17138e..390810b7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -104,7 +104,14 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 with: - python-version: '3.13' + python-version: '3.14' + + # needed to build psycopg-c from source + # TODO(soon): maybe allow pywrangler to skip native package installs + # when not needed. Native installations are just for validation + type hints + # and are not used at all in the runtime. + - name: Install libpq development files + run: sudo apt-get update && sudo apt-get install --yes libpq-dev - name: Run Hyperdrive binding tests working-directory: packages/runtime-sdk diff --git a/packages/cli/src/pywrangler/utils.py b/packages/cli/src/pywrangler/utils.py index 5b65ead1..39c8dee3 100644 --- a/packages/cli/src/pywrangler/utils.py +++ b/packages/cli/src/pywrangler/utils.py @@ -424,7 +424,7 @@ def get_pyodide_index() -> str: case "3.13": v = "0.28.3" case "3.14": - v = "314.0.2" + v = "314.0.6" return "https://index.pyodide.org/" + v diff --git a/packages/runtime-sdk/tests/bindings-test/pyproject.toml b/packages/runtime-sdk/tests/bindings-test/pyproject.toml index ef5fbb6e..308e5f5b 100644 --- a/packages/runtime-sdk/tests/bindings-test/pyproject.toml +++ b/packages/runtime-sdk/tests/bindings-test/pyproject.toml @@ -2,7 +2,18 @@ name = "bindings-test" version = "0.1.0" requires-python = ">=3.12" -dependencies = ["pytest", "pytest-asyncio<1.2.0", "pg8000", "pymysql", "cryptography", "testlib"] +dependencies = [ + "pytest", + "pytest-asyncio<1.2.0", + # These are for hyperdrive tests + "pg8000; python_version >= '3.14'", + "pymysql; python_version >= '3.14'", + "aiomysql; python_version >= '3.14'", + "psycopg; python_version >= '3.14'", + "psycopg-c; python_version >= '3.14'", + "cryptography; python_version >= '3.14'", + "testlib", +] [tool.uv.sources] testlib = { path = "../testlib/dist/testlib-0.0.0-py3-none-any.whl" } diff --git a/packages/runtime-sdk/tests/bindings-test/src/test_hyperdrive_mysql.py b/packages/runtime-sdk/tests/bindings-test/src/test_hyperdrive_mysql.py index 8a318953..2b9e982d 100644 --- a/packages/runtime-sdk/tests/bindings-test/src/test_hyperdrive_mysql.py +++ b/packages/runtime-sdk/tests/bindings-test/src/test_hyperdrive_mysql.py @@ -24,6 +24,7 @@ import sys +import aiomysql import pymysql import pytest from conftest import unique_table_name @@ -58,6 +59,26 @@ async def test_connect(env): conn.close() +@pytest.mark.asyncio +async def test_connect_aiomysql(env): + hd = env.HYPERDRIVE_MYSQL + conn = await aiomysql.connect( + host=hd.host, + port=int(hd.port), + user=hd.user, + password=hd.password, + db=hd.database, + # Hyperdrive terminates TLS to the origin, so this hop is plaintext. + ssl=None, + ) + try: + async with conn.cursor() as cur: + await cur.execute("SELECT %s", (1,)) + assert await cur.fetchone() == (1,) + finally: + await conn.ensure_closed() + + @pytest.mark.asyncio async def test_create_insert_select(env): conn = _connect(env) diff --git a/packages/runtime-sdk/tests/bindings-test/src/test_hyperdrive_postgresql.py b/packages/runtime-sdk/tests/bindings-test/src/test_hyperdrive_postgresql.py index 3860aee4..615b0e2c 100644 --- a/packages/runtime-sdk/tests/bindings-test/src/test_hyperdrive_postgresql.py +++ b/packages/runtime-sdk/tests/bindings-test/src/test_hyperdrive_postgresql.py @@ -22,6 +22,11 @@ uv run pytest tests/test_bindings.py -m hyperdrive -k postgresql +Host prerequisites for the psycopg C extension: + +Ubuntu: sudo apt-get install libpq-dev +macOS: brew install libpq && export PATH="$(brew --prefix libpq)/bin:$PATH" + Note: "POSTGRES_HOST_AUTH_METHOD=md5" is required for PostgreSQL to work with pg8000, since the default `scram-sha-256` is not available in the pg8000 with Python workers (missing openssl) """ @@ -61,6 +66,26 @@ async def test_connect(env): conn.close() +@pytest.mark.asyncio +async def test_connect_psycopg(env): + import psycopg # noqa: PLC0415 + + assert psycopg.pq.__impl__ == "c" + hd = env.HYPERDRIVE_PG + with psycopg.connect( + host=hd.host, + port=int(hd.port), + user=hd.user, + password=hd.password, + dbname=hd.database, + # Hyperdrive terminates TLS to the origin, so this hop is plaintext. + sslmode="disable", + ) as conn: + with conn.cursor() as cur: + cur.execute("SELECT %s", (1,)) + assert cur.fetchone() == (1,) + + @pytest.mark.asyncio async def test_create_insert_select(env): conn = _connect(env) From c6d13c20f4275914b527ccdddbf6111a629c6b8c Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Tue, 8 Sep 2026 00:14:12 +0900 Subject: [PATCH 2/4] chore: Update packages/runtime-sdk/tests/bindings-test/pyproject.toml Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com> --- .../runtime-sdk/tests/bindings-test/pyproject.toml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/runtime-sdk/tests/bindings-test/pyproject.toml b/packages/runtime-sdk/tests/bindings-test/pyproject.toml index 308e5f5b..c8964a4d 100644 --- a/packages/runtime-sdk/tests/bindings-test/pyproject.toml +++ b/packages/runtime-sdk/tests/bindings-test/pyproject.toml @@ -6,12 +6,18 @@ dependencies = [ "pytest", "pytest-asyncio<1.2.0", # These are for hyperdrive tests - "pg8000; python_version >= '3.14'", - "pymysql; python_version >= '3.14'", - "aiomysql; python_version >= '3.14'", +dependencies = [ + "pytest", + "pytest-asyncio<1.2.0", + # These are for hyperdrive tests + "pg8000", + "pymysql", + "aiomysql", "psycopg; python_version >= '3.14'", "psycopg-c; python_version >= '3.14'", - "cryptography; python_version >= '3.14'", + "cryptography", + "testlib", +] "testlib", ] From a69fc55e77fbe8823b144a634308acfc5f7f498f Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Wed, 9 Sep 2026 14:11:24 +0900 Subject: [PATCH 3/4] chore: fix bad merge --- packages/runtime-sdk/tests/bindings-test/pyproject.toml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/runtime-sdk/tests/bindings-test/pyproject.toml b/packages/runtime-sdk/tests/bindings-test/pyproject.toml index c8964a4d..aff98dba 100644 --- a/packages/runtime-sdk/tests/bindings-test/pyproject.toml +++ b/packages/runtime-sdk/tests/bindings-test/pyproject.toml @@ -2,10 +2,6 @@ name = "bindings-test" version = "0.1.0" requires-python = ">=3.12" -dependencies = [ - "pytest", - "pytest-asyncio<1.2.0", - # These are for hyperdrive tests dependencies = [ "pytest", "pytest-asyncio<1.2.0", @@ -18,8 +14,6 @@ dependencies = [ "cryptography", "testlib", ] - "testlib", -] [tool.uv.sources] testlib = { path = "../testlib/dist/testlib-0.0.0-py3-none-any.whl" } From d01bc9440bcd9def0d147705c4a71740aafdc2dd Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Tue, 15 Sep 2026 14:38:38 +0900 Subject: [PATCH 4/4] test(runtime-sdk): use psycopg-binary and test asyncpg as well --- .github/workflows/tests.yml | 7 --- .../tests/bindings-test/pyproject.toml | 4 +- .../src/test_hyperdrive_postgresql.py | 52 ++++++++++++++++--- 3 files changed, 48 insertions(+), 15 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 390810b7..7d704e31 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -106,13 +106,6 @@ jobs: with: python-version: '3.14' - # needed to build psycopg-c from source - # TODO(soon): maybe allow pywrangler to skip native package installs - # when not needed. Native installations are just for validation + type hints - # and are not used at all in the runtime. - - name: Install libpq development files - run: sudo apt-get update && sudo apt-get install --yes libpq-dev - - name: Run Hyperdrive binding tests working-directory: packages/runtime-sdk run: | diff --git a/packages/runtime-sdk/tests/bindings-test/pyproject.toml b/packages/runtime-sdk/tests/bindings-test/pyproject.toml index aff98dba..0db629d3 100644 --- a/packages/runtime-sdk/tests/bindings-test/pyproject.toml +++ b/packages/runtime-sdk/tests/bindings-test/pyproject.toml @@ -9,8 +9,8 @@ dependencies = [ "pg8000", "pymysql", "aiomysql", - "psycopg; python_version >= '3.14'", - "psycopg-c; python_version >= '3.14'", + "psycopg[binary]; python_version >= '3.14'", + "asyncpg; python_version >= '3.14'", "cryptography", "testlib", ] diff --git a/packages/runtime-sdk/tests/bindings-test/src/test_hyperdrive_postgresql.py b/packages/runtime-sdk/tests/bindings-test/src/test_hyperdrive_postgresql.py index 615b0e2c..247dd57b 100644 --- a/packages/runtime-sdk/tests/bindings-test/src/test_hyperdrive_postgresql.py +++ b/packages/runtime-sdk/tests/bindings-test/src/test_hyperdrive_postgresql.py @@ -22,11 +22,6 @@ uv run pytest tests/test_bindings.py -m hyperdrive -k postgresql -Host prerequisites for the psycopg C extension: - -Ubuntu: sudo apt-get install libpq-dev -macOS: brew install libpq && export PATH="$(brew --prefix libpq)/bin:$PATH" - Note: "POSTGRES_HOST_AUTH_METHOD=md5" is required for PostgreSQL to work with pg8000, since the default `scram-sha-256` is not available in the pg8000 with Python workers (missing openssl) """ @@ -70,7 +65,7 @@ async def test_connect(env): async def test_connect_psycopg(env): import psycopg # noqa: PLC0415 - assert psycopg.pq.__impl__ == "c" + assert psycopg.pq.__impl__ == "binary" hd = env.HYPERDRIVE_PG with psycopg.connect( host=hd.host, @@ -86,6 +81,51 @@ async def test_connect_psycopg(env): assert cur.fetchone() == (1,) +@pytest.mark.asyncio +async def test_asyncpg_crud(env): + import asyncpg # noqa: PLC0415 + + hd = env.HYPERDRIVE_PG + conn = await asyncpg.connect( + host=hd.host, + port=int(hd.port), + user=hd.user, + password=hd.password, + database=hd.database, + # Hyperdrive terminates TLS to the origin, so this hop is plaintext. + ssl=False, + ) + table = unique_table_name() + try: + await conn.execute( + f"CREATE TABLE {table} (id SERIAL PRIMARY KEY, name TEXT, value INT)" + ) + await conn.execute( + f"INSERT INTO {table} (name, value) VALUES ($1, $2)", "alpha", 1 + ) + + row = await conn.fetchrow( + f"SELECT name, value FROM {table} WHERE name = $1", "alpha" + ) + assert tuple(row) == ("alpha", 1) + + await conn.execute( + f"UPDATE {table} SET value = $1 WHERE name = $2", 11, "alpha" + ) + assert ( + await conn.fetchval(f"SELECT value FROM {table} WHERE name = $1", "alpha") + == 11 + ) + + await conn.execute(f"DELETE FROM {table} WHERE name = $1", "alpha") + assert await conn.fetchval(f"SELECT COUNT(*) FROM {table}") == 0 + finally: + try: + await conn.execute(f"DROP TABLE IF EXISTS {table}") + finally: + await conn.close() + + @pytest.mark.asyncio async def test_create_insert_select(env): conn = _connect(env)