From 14f636c3a7858ead1201a6d5894ea01e1923bae5 Mon Sep 17 00:00:00 2001 From: laughingman7743 Date: Mon, 12 Oct 2026 20:52:55 +0900 Subject: [PATCH] Rename the asyncio SQLAlchemy adapter classes to CapWords Rename AsyncAdapt_pyathena_cursor, AsyncAdapt_pyathena_connection, and AsyncAdapt_pyathena_dbapi to AthenaAioAdaptedCursor, AthenaAioAdaptedConnection, and AthenaAioAdaptedDBAPI without keeping the old names, and remove the N801 exclusion for pyathena/aio/sqlalchemy/base.py so that ruff rejects such names. Closes #1147 Co-Authored-By: Claude Opus 5.5 --- pyathena/aio/sqlalchemy/base.py | 34 +++++++++++----------- pyproject.toml | 3 -- tests/pyathena/aio/sqlalchemy/test_base.py | 8 ++--- tests/pyathena/sqlalchemy/test_base.py | 4 +-- 4 files changed, 23 insertions(+), 26 deletions(-) diff --git a/pyathena/aio/sqlalchemy/base.py b/pyathena/aio/sqlalchemy/base.py index f2f963dc0..d12658344 100644 --- a/pyathena/aio/sqlalchemy/base.py +++ b/pyathena/aio/sqlalchemy/base.py @@ -49,7 +49,7 @@ _ASYNC_CURSOR_CLASSES: dict[Any, Any] = {Cursor: AioCursor} -class AsyncAdapt_pyathena_cursor: +class AthenaAioAdaptedCursor: """Wraps any async PyAthena cursor with a sync DBAPI interface. SQLAlchemy's async engine uses greenlet-based ``await_only()`` to call @@ -221,24 +221,24 @@ def list_table_metadata(self, *args: Any, **kwargs: Any) -> Any: """ return await_only(self._cursor.list_table_metadata(*args, **kwargs)) - def __enter__(self) -> AsyncAdapt_pyathena_cursor: + def __enter__(self) -> AthenaAioAdaptedCursor: return self def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: self.close() -class AsyncAdapt_pyathena_connection(AdaptedConnection): +class AthenaAioAdaptedConnection(AdaptedConnection): """Wraps ``AioConnection`` with a sync DBAPI interface. This adapted connection delegates ``cursor()`` to the underlying ``AioConnection`` and wraps each returned async cursor with - ``AsyncAdapt_pyathena_cursor``. + ``AthenaAioAdaptedCursor``. """ __slots__ = ("_connection", "dbapi") - def __init__(self, dbapi: AsyncAdapt_pyathena_dbapi, connection: AioConnection) -> None: + def __init__(self, dbapi: AthenaAioAdaptedDBAPI, connection: AioConnection) -> None: """Initialize the adapted connection. Args: @@ -273,7 +273,7 @@ def retry_config(self) -> RetryConfig: """The retry configuration of the wrapped connection.""" return self._connection.retry_config # type: ignore[no-any-return] - def cursor(self, cursor: Any = None, **kwargs: Any) -> AsyncAdapt_pyathena_cursor: + def cursor(self, cursor: Any = None, **kwargs: Any) -> AthenaAioAdaptedCursor: """Create an async cursor on the wrapped connection and adapt it. A synchronous cursor class that has a registered async counterpart is @@ -286,14 +286,14 @@ def cursor(self, cursor: Any = None, **kwargs: Any) -> AsyncAdapt_pyathena_curso ``cursor()``. Returns: - The created cursor wrapped in ``AsyncAdapt_pyathena_cursor``. + The created cursor wrapped in ``AthenaAioAdaptedCursor``. """ # The shared dialect names a cursor class in its synchronous form; this # connection can only drive the async counterpart. raw_cursor = self._connection.cursor(_ASYNC_CURSOR_CLASSES.get(cursor, cursor), **kwargs) - return AsyncAdapt_pyathena_cursor(raw_cursor) + return AthenaAioAdaptedCursor(raw_cursor) - def _internal_cursor(self, cursor: Any) -> AsyncAdapt_pyathena_cursor: + def _internal_cursor(self, cursor: Any) -> AthenaAioAdaptedCursor: """Create a cursor with the wrapped connection's ``_internal_cursor()`` and adapt it. A synchronous cursor class is replaced as in ``cursor()``. @@ -302,10 +302,10 @@ def _internal_cursor(self, cursor: Any) -> AsyncAdapt_pyathena_cursor: cursor: The cursor class to create. Returns: - The created cursor wrapped in ``AsyncAdapt_pyathena_cursor``. + The created cursor wrapped in ``AthenaAioAdaptedCursor``. """ raw_cursor = self._connection._internal_cursor(_ASYNC_CURSOR_CLASSES.get(cursor, cursor)) - return AsyncAdapt_pyathena_cursor(raw_cursor) + return AthenaAioAdaptedCursor(raw_cursor) def close(self) -> None: """Close the wrapped connection.""" @@ -319,7 +319,7 @@ def rollback(self) -> None: """Do nothing, because Athena does not support transactions.""" -class AsyncAdapt_pyathena_dbapi: +class AthenaAioAdaptedDBAPI: """Fake DBAPI module for the async SQLAlchemy engine. SQLAlchemy expects ``import_dbapi()`` to return a module-like object @@ -344,17 +344,17 @@ class AsyncAdapt_pyathena_dbapi: DataError = DataError NotSupportedError = NotSupportedError - def connect(self, **kwargs: Any) -> AsyncAdapt_pyathena_connection: + def connect(self, **kwargs: Any) -> AthenaAioAdaptedConnection: """Create an ``AioConnection`` and wrap it in an adapted connection. Args: **kwargs: Keyword arguments forwarded to ``AioConnection.create()``. Returns: - The new connection wrapped in ``AsyncAdapt_pyathena_connection``. + The new connection wrapped in ``AthenaAioAdaptedConnection``. """ connection = await_only(AioConnection.create(**kwargs)) - return AsyncAdapt_pyathena_connection(self, connection) + return AthenaAioAdaptedConnection(self, connection) class AthenaAioDialect(AthenaDialect): @@ -384,12 +384,12 @@ def get_pool_class(cls, url: URL) -> type: @classmethod @override def import_dbapi(cls) -> ModuleType: - return AsyncAdapt_pyathena_dbapi() # type: ignore[return-value] + return AthenaAioAdaptedDBAPI() # type: ignore[return-value] @classmethod @override def dbapi(cls) -> ModuleType: # type: ignore[override] - return AsyncAdapt_pyathena_dbapi() # type: ignore[return-value] + return AthenaAioAdaptedDBAPI() # type: ignore[return-value] @override def create_connect_args(self, url: URL) -> tuple[tuple[str], MutableMapping[str, Any]]: diff --git a/pyproject.toml b/pyproject.toml index 2973b2913..1e74fd0f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -199,9 +199,6 @@ ignore-decorators = ["pyathena.util.override"] "pyathena/sqlalchemy/compiler.py" = [ "N802", # SQLAlchemy TypeCompiler requires visit_UPPERCASE method names ] -"pyathena/aio/sqlalchemy/base.py" = [ - "N801", # SQLAlchemy async adapter naming convention (AsyncAdapt_*) -] [tool.mypy] follow_imports = "silent" diff --git a/tests/pyathena/aio/sqlalchemy/test_base.py b/tests/pyathena/aio/sqlalchemy/test_base.py index 67acf61f6..77f463975 100644 --- a/tests/pyathena/aio/sqlalchemy/test_base.py +++ b/tests/pyathena/aio/sqlalchemy/test_base.py @@ -10,13 +10,13 @@ from sqlalchemy.sql.schema import Column, MetaData, Table from sqlalchemy.util.concurrency import greenlet_spawn -from pyathena.aio.sqlalchemy.base import AsyncAdapt_pyathena_cursor +from pyathena.aio.sqlalchemy.base import AthenaAioAdaptedCursor from pyathena.sqlalchemy.types import AthenaArray from tests import ENV from tests.pyathena.util import throttle_metadata_api -class TestAsyncAdaptPyathenaCursor: +class TestAthenaAioAdaptedCursor: @pytest.mark.parametrize( "error", [ValueError("conversion failed"), asyncio.CancelledError()], @@ -29,7 +29,7 @@ async def test_execute_closes_cursor_off_event_loop_when_fetch_fails(self, error cursor.fetchall = AsyncMock(side_effect=error) closes = [] cursor.close.side_effect = lambda: closes.append(threading.get_ident()) - adapted = AsyncAdapt_pyathena_cursor(cursor) + adapted = AthenaAioAdaptedCursor(cursor) with pytest.raises(type(error)): await greenlet_spawn(adapted.execute, "SELECT 1") @@ -48,7 +48,7 @@ async def test_execute_keeps_fetch_error_when_close_fails(self, error, caplog): cursor.description = [("col", "varchar", None, None, None, None, None)] cursor.fetchall = AsyncMock(side_effect=error) cursor.close.side_effect = ValueError("generator already executing") - adapted = AsyncAdapt_pyathena_cursor(cursor) + adapted = AthenaAioAdaptedCursor(cursor) with pytest.raises(type(error)) as raised: await greenlet_spawn(adapted.execute, "SELECT 1") diff --git a/tests/pyathena/sqlalchemy/test_base.py b/tests/pyathena/sqlalchemy/test_base.py index ff7e39724..f8ae551f7 100644 --- a/tests/pyathena/sqlalchemy/test_base.py +++ b/tests/pyathena/sqlalchemy/test_base.py @@ -28,7 +28,7 @@ from pyathena.aio.connection import AioConnection from pyathena.aio.cursor import AioCursor from pyathena.aio.pandas.cursor import AioPandasCursor -from pyathena.aio.sqlalchemy.base import AsyncAdapt_pyathena_connection, AthenaAioDialect +from pyathena.aio.sqlalchemy.base import AthenaAioAdaptedConnection, AthenaAioDialect from pyathena.connection import Connection from pyathena.cursor import Cursor from pyathena.error import DatabaseError, OperationalError @@ -208,7 +208,7 @@ def test_internal_cursor_leaves_out_default_cursor_options( cursor_kwargs={"unload": True, "chunksize": 10, "arraysize": 5000}, ) driver_connection = ( - AsyncAdapt_pyathena_connection(MagicMock(), conn) + AthenaAioAdaptedConnection(MagicMock(), conn) if connection_class is AioConnection else conn )