From afd3d756205e00d0478106de7d3af44239c47bfe Mon Sep 17 00:00:00 2001 From: Amin Ghadersohi Date: Sat, 26 Sep 2026 06:15:53 +0000 Subject: [PATCH 1/3] Default the base SQLAlchemy dialect to the REST driver The bare awsathena:// scheme resolves to AthenaDialect, which did not define a driver. SQLAlchemy reads the driver when describing a dialect, so Engine.driver, URL.get_driver_name() and dialect_description raised AttributeError for bare URLs, and tools enumerating dialect entry points could not use it. Give the base dialect the REST driver, which is what awsathena+rest:// already selects; the concrete dialects keep their own driver values. --- pyathena/sqlalchemy/base.py | 5 +++++ tests/pyathena/sqlalchemy/test_base.py | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/pyathena/sqlalchemy/base.py b/pyathena/sqlalchemy/base.py index e667b9288..44ef14b65 100644 --- a/pyathena/sqlalchemy/base.py +++ b/pyathena/sqlalchemy/base.py @@ -141,6 +141,11 @@ class AthenaDialect(DefaultDialect): """ name: str = "awsathena" + # The bare ``awsathena://`` scheme resolves to this class. SQLAlchemy reads + # ``driver`` when describing a dialect (for example ``Engine.driver`` and + # ``URL.get_driver_name()``), so default it to the REST cursor, which is + # what ``awsathena+rest://`` selects. + driver: str = "rest" preparer: type[IdentifierPreparer] = AthenaDMLIdentifierPreparer statement_compiler: type[SQLCompiler] = AthenaStatementCompiler ddl_compiler: type[DDLCompiler] = AthenaDDLCompiler diff --git a/tests/pyathena/sqlalchemy/test_base.py b/tests/pyathena/sqlalchemy/test_base.py index f198fd4bb..243c7f04e 100644 --- a/tests/pyathena/sqlalchemy/test_base.py +++ b/tests/pyathena/sqlalchemy/test_base.py @@ -61,6 +61,20 @@ def unique_s3tables_table_name(base: str) -> str: class TestAthenaDialect: + def test_bare_scheme_uses_rest_driver(self): + # The bare scheme must describe itself exactly like awsathena+rest; a + # missing driver made Engine.driver raise AttributeError. + url = "awsathena://athena.us-west-2.amazonaws.com:443/default?s3_staging_dir=s3://bucket/path/" + bare = create_engine(url) + rest = create_engine(url.replace("awsathena://", "awsathena+rest://")) + assert type(bare.dialect) is AthenaDialect + assert bare.driver == rest.driver == "rest" + assert bare.url.get_driver_name() == "rest" + assert bare.dialect.dialect_description == "awsathena+rest" + assert bare.dialect.create_connect_args(bare.url) == rest.dialect.create_connect_args( + rest.url + ) + def test_columns_from_information_schema(self): # Rows arrive unordered, and Athena reports a missing comment as NULL. # An API cursor hands that over as None or as an empty string; a From 91fd1755e93cecda87f1d5e49529381126d37a7b Mon Sep 17 00:00:00 2001 From: Amin Ghadersohi Date: Sat, 26 Sep 2026 23:30:06 +0000 Subject: [PATCH 2/3] Point the bare awsathena entry point at AthenaRestDialect Revert the default driver on the base AthenaDialect, which stays the shared base for the concrete dialects. Resolve the bare awsathena:// scheme to AthenaRestDialect through the entry point instead, and assert the resolved dialect type in the test. --- pyathena/sqlalchemy/base.py | 5 ----- pyproject.toml | 2 +- tests/pyathena/sqlalchemy/test_base.py | 14 ++++++-------- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/pyathena/sqlalchemy/base.py b/pyathena/sqlalchemy/base.py index 44ef14b65..e667b9288 100644 --- a/pyathena/sqlalchemy/base.py +++ b/pyathena/sqlalchemy/base.py @@ -141,11 +141,6 @@ class AthenaDialect(DefaultDialect): """ name: str = "awsathena" - # The bare ``awsathena://`` scheme resolves to this class. SQLAlchemy reads - # ``driver`` when describing a dialect (for example ``Engine.driver`` and - # ``URL.get_driver_name()``), so default it to the REST cursor, which is - # what ``awsathena+rest://`` selects. - driver: str = "rest" preparer: type[IdentifierPreparer] = AthenaDMLIdentifierPreparer statement_compiler: type[SQLCompiler] = AthenaStatementCompiler ddl_compiler: type[DDLCompiler] = AthenaDDLCompiler diff --git a/pyproject.toml b/pyproject.toml index 755591dce..0d79cfafd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,7 @@ documentation = "https://pyathena.dev/" issues = "https://github.com/pyathena-dev/PyAthena/issues" [project.entry-points."sqlalchemy.dialects"] -awsathena = "pyathena.sqlalchemy.base:AthenaDialect" +awsathena = "pyathena.sqlalchemy.rest:AthenaRestDialect" "awsathena.rest" = "pyathena.sqlalchemy.rest:AthenaRestDialect" "awsathena.pandas" = "pyathena.sqlalchemy.pandas:AthenaPandasDialect" "awsathena.arrow" = "pyathena.sqlalchemy.arrow:AthenaArrowDialect" diff --git a/tests/pyathena/sqlalchemy/test_base.py b/tests/pyathena/sqlalchemy/test_base.py index 243c7f04e..26895106d 100644 --- a/tests/pyathena/sqlalchemy/test_base.py +++ b/tests/pyathena/sqlalchemy/test_base.py @@ -23,6 +23,7 @@ from pyathena.cursor import Cursor from pyathena.error import DatabaseError, OperationalError from pyathena.sqlalchemy.base import AthenaDialect +from pyathena.sqlalchemy.rest import AthenaRestDialect from pyathena.sqlalchemy.types import ( TINYINT, AthenaArray, @@ -62,18 +63,15 @@ def unique_s3tables_table_name(base: str) -> str: class TestAthenaDialect: def test_bare_scheme_uses_rest_driver(self): - # The bare scheme must describe itself exactly like awsathena+rest; a - # missing driver made Engine.driver raise AttributeError. + # The bare awsathena entry point resolves to the REST dialect, like + # awsathena+rest. Requires the package to be reinstalled (uv sync) so the + # installed entry point metadata matches pyproject.toml. url = "awsathena://athena.us-west-2.amazonaws.com:443/default?s3_staging_dir=s3://bucket/path/" bare = create_engine(url) - rest = create_engine(url.replace("awsathena://", "awsathena+rest://")) - assert type(bare.dialect) is AthenaDialect - assert bare.driver == rest.driver == "rest" + assert type(bare.dialect) is AthenaRestDialect + assert bare.driver == "rest" assert bare.url.get_driver_name() == "rest" assert bare.dialect.dialect_description == "awsathena+rest" - assert bare.dialect.create_connect_args(bare.url) == rest.dialect.create_connect_args( - rest.url - ) def test_columns_from_information_schema(self): # Rows arrive unordered, and Athena reports a missing comment as NULL. From 257f3c3d7bd564e45421b30c2a9ab4269bb6a711 Mon Sep 17 00:00:00 2001 From: Amin Ghadersohi Date: Wed, 30 Sep 2026 04:15:05 +0000 Subject: [PATCH 3/3] Document the bare awsathena schema as the REST driver The bare awsathena entry point now resolves to AthenaRestDialect, so the Dialect & driver table lists rest for it. --- docs/sqlalchemy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sqlalchemy.md b/docs/sqlalchemy.md index 71b41c906..533639012 100644 --- a/docs/sqlalchemy.md +++ b/docs/sqlalchemy.md @@ -180,7 +180,7 @@ Column definitions in `CREATE TABLE` render `TIMESTAMP` for `DateTime` and for ` | Dialect | Driver | Schema | Cursor | |-----------|--------|------------------|------------------------| -| awsathena | | awsathena | DefaultCursor | +| awsathena | rest | awsathena | DefaultCursor | | awsathena | rest | awsathena+rest | DefaultCursor | | awsathena | pandas | awsathena+pandas | {ref}`pandas-cursor` | | awsathena | arrow | awsathena+arrow | {ref}`arrow-cursor` |