Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions pyathena/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from pyathena.error import * # noqa: F403
from pyathena.options import ExecuteOptions as ExecuteOptions
from pyathena.util import override

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent review (relayed): CLEAN

Reviewer: Codex CLI 0.159.3, model gpt-6-astra, reasoning effort high, sandbox read-only, session 01a0f874-9425-7850-9f24-b962e38f4a38. It did not author the change.
Scope: base 39f22dc1d711bdd460adbbfd1736d0bff347ec80, head 868baf5b985a9972f1cd9d688ac01a9cd6e566a0. The reviewer read a detached clean checkout of the head and the literal diff. Its prompt left out the PR number, description, commit message and earlier review findings. Constraints: no edits, builds, tests, type checkers, network, GitHub or memory. This is a static review.

Reviewer's result:

Surfaces covered: Full diff: 55 Python files and pyproject.toml. Runtime identity decorator, imports, descriptors, and decorator ordering. PyAthena inheritance across cursors, result sets, converters, readers, Spark, and filesystem helpers; fsspec overrides excluded. SQLAlchemy dialects, compilers, types, _HashableDict, and the local compliance-plugin loading path. All seven property suppressions and the mypy configuration comment.

Verdict: CLEAN

No actionable defect found in the inspected sources. DataError is exported before pyathena.util imports it back, so that dependency cycle does not access an undefined name. Decorator ordering preserves descriptors and cached wrappers. Removing the __hash__ type-ignore has no runtime effect. No missing markers were found along the traced PyAthena inheritance paths.

Coverage limitation: SQLAlchemy's upstream sources and mypy's implementation/issue text are absent from this checkout. Consequently, upstream override targets and the precise necessity of the suppressions remain unverified.

Author note on the stated limitation: the snapshot does not contain the SQLAlchemy and mypy sources. The author's evidence covers that gap. The 212 explicit-override sites are the overrides mypy itself reported, with the base class named. The 41 unannotated SQLAlchemy sites came from a runtime MRO scan that found the base attribute. The need for the seven suppressions was reproduced with a minimal example on mypy 1.14.0 through 2.3.1. The review snapshot and the PR worktree were unchanged afterward.


if TYPE_CHECKING:
from pyathena.aio.connection import AioConnection
Expand Down Expand Up @@ -34,16 +35,19 @@ class DBAPITypeObject(frozenset[str]):
https://www.python.org/dev/peps/pep-0249/#type-objects-and-constructors
"""

@override
def __eq__(self, other: object):
if isinstance(other, frozenset):
return frozenset.__eq__(self, other)
return other in self

@override
def __ne__(self, other: object):
if isinstance(other, frozenset):
return frozenset.__ne__(self, other)
return other not in self

@override
def __hash__(self):
return frozenset.__hash__(self)

Expand Down
1 change: 1 addition & 0 deletions pyathena/aio/arrow/cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def __init__(
self._result_set: AthenaArrowResultSet | None = None

@staticmethod
@override
def get_default_converter(
unload: bool = False,
) -> DefaultArrowTypeConverter | DefaultArrowUnloadTypeConverter | Any:
Expand Down
3 changes: 2 additions & 1 deletion pyathena/aio/cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ def __init__(
self._result_set: AthenaAioResultSet | None = None
self._result_set_class = AthenaAioResultSet

@property
@property # type: ignore[explicit-override] # python/mypy#15900
@override
def arraysize(self) -> int:
return self._arraysize

Expand Down
1 change: 1 addition & 0 deletions pyathena/aio/pandas/cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def __init__(
self._result_set: AthenaPandasResultSet | None = None

@staticmethod
@override
def get_default_converter(
unload: bool = False,
) -> DefaultPandasTypeConverter | Any:
Expand Down
1 change: 1 addition & 0 deletions pyathena/aio/polars/cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ def __init__(
self._result_set: AthenaPolarsResultSet | None = None

@staticmethod
@override
def get_default_converter(
unload: bool = False,
) -> DefaultPolarsTypeConverter | DefaultPolarsUnloadTypeConverter | Any:
Expand Down
1 change: 1 addition & 0 deletions pyathena/aio/s3fs/cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def __init__(
self._result_set: AthenaS3FSResultSet | None = None

@staticmethod
@override
def get_default_converter(
unload: bool = False,
) -> DefaultS3FSTypeConverter:
Expand Down
1 change: 1 addition & 0 deletions pyathena/aio/spark/cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class AioSparkCursor(SparkBaseCursor, WithCalculationExecution):
"""

@property
@override
def calculation_execution(self) -> AthenaCalculationExecution | None:
return self._calculation_execution

Expand Down
4 changes: 3 additions & 1 deletion pyathena/aio/sqlalchemy/arrow.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from typing import TYPE_CHECKING

from pyathena.aio.sqlalchemy.base import AthenaAioDialect
from pyathena.util import strtobool
from pyathena.util import override, strtobool

if TYPE_CHECKING:
from types import ModuleType
Expand Down Expand Up @@ -43,6 +43,7 @@ class AthenaAioArrowDialect(AthenaAioDialect):
driver = "aioarrow"
supports_statement_cache = True

@override
def create_connect_args(self, url):
from pyathena.aio.arrow.cursor import AioArrowCursor

Expand All @@ -57,5 +58,6 @@ def create_connect_args(self, url):
return [[], opts]

@classmethod
@override
def import_dbapi(cls) -> "ModuleType":
return super().import_dbapi()
8 changes: 7 additions & 1 deletion pyathena/aio/sqlalchemy/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
ProgrammingError,
)
from pyathena.sqlalchemy.base import AthenaDialect
from pyathena.util import RetryConfig
from pyathena.util import RetryConfig, override

if TYPE_CHECKING:
from types import ModuleType
Expand Down Expand Up @@ -140,6 +140,7 @@ def __init__(self, dbapi: AsyncAdapt_pyathena_dbapi, connection: AioConnection)
self._connection = connection # type: ignore[assignment]

@property
@override
def driver_connection(self) -> AioConnection:
return self._connection # type: ignore[return-value]

Expand Down Expand Up @@ -225,21 +226,26 @@ class AthenaAioDialect(AthenaDialect):
supports_statement_cache = True

@classmethod
@override
def get_pool_class(cls, url: URL) -> type:
return pool.AsyncAdaptedQueuePool

@classmethod
@override
def import_dbapi(cls) -> ModuleType:
return AsyncAdapt_pyathena_dbapi() # type: ignore[return-value]

@classmethod
@override
def dbapi(cls) -> ModuleType: # type: ignore[override]
return AsyncAdapt_pyathena_dbapi() # type: ignore[return-value]

@override
def create_connect_args(self, url: URL) -> tuple[tuple[str], MutableMapping[str, Any]]:
opts = self._create_connect_args(url)
self._connect_options = opts
return cast(tuple[str], ()), opts

@override
def get_driver_connection(self, connection: Any) -> Any:
return connection
4 changes: 3 additions & 1 deletion pyathena/aio/sqlalchemy/pandas.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from typing import TYPE_CHECKING

from pyathena.aio.sqlalchemy.base import AthenaAioDialect
from pyathena.util import strtobool
from pyathena.util import override, strtobool

if TYPE_CHECKING:
from types import ModuleType
Expand Down Expand Up @@ -45,6 +45,7 @@ class AthenaAioPandasDialect(AthenaAioDialect):
driver = "aiopandas"
supports_statement_cache = True

@override
def create_connect_args(self, url):
from pyathena.aio.pandas.cursor import AioPandasCursor

Expand All @@ -63,5 +64,6 @@ def create_connect_args(self, url):
return [[], opts]

@classmethod
@override
def import_dbapi(cls) -> "ModuleType":
return super().import_dbapi()
4 changes: 3 additions & 1 deletion pyathena/aio/sqlalchemy/polars.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from typing import TYPE_CHECKING

from pyathena.aio.sqlalchemy.base import AthenaAioDialect
from pyathena.util import strtobool
from pyathena.util import override, strtobool

if TYPE_CHECKING:
from types import ModuleType
Expand Down Expand Up @@ -43,6 +43,7 @@ class AthenaAioPolarsDialect(AthenaAioDialect):
driver = "aiopolars"
supports_statement_cache = True

@override
def create_connect_args(self, url):
from pyathena.aio.polars.cursor import AioPolarsCursor

Expand All @@ -57,5 +58,6 @@ def create_connect_args(self, url):
return [[], opts]

@classmethod
@override
def import_dbapi(cls) -> "ModuleType":
return super().import_dbapi()
2 changes: 2 additions & 0 deletions pyathena/aio/sqlalchemy/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from typing import TYPE_CHECKING

from pyathena.aio.sqlalchemy.base import AthenaAioDialect
from pyathena.util import override

if TYPE_CHECKING:
from types import ModuleType
Expand Down Expand Up @@ -39,5 +40,6 @@ class AthenaAioRestDialect(AthenaAioDialect):
supports_statement_cache = True

@classmethod
@override
def import_dbapi(cls) -> "ModuleType":
return super().import_dbapi()
3 changes: 3 additions & 0 deletions pyathena/aio/sqlalchemy/s3fs.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from typing import TYPE_CHECKING

from pyathena.aio.sqlalchemy.base import AthenaAioDialect
from pyathena.util import override

if TYPE_CHECKING:
from types import ModuleType
Expand Down Expand Up @@ -37,6 +38,7 @@ class AthenaAioS3FSDialect(AthenaAioDialect):
driver = "aios3fs"
supports_statement_cache = True

@override
def create_connect_args(self, url):
from pyathena.aio.s3fs.cursor import AioS3FSCursor

Expand All @@ -46,5 +48,6 @@ def create_connect_args(self, url):
return [[], opts]

@classmethod
@override
def import_dbapi(cls) -> "ModuleType":
return super().import_dbapi()
7 changes: 6 additions & 1 deletion pyathena/arrow/async_cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from pyathena.common import CursorIterator
from pyathena.model import AthenaQueryExecution
from pyathena.options import ExecuteOptions
from pyathena.util import override

_logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -130,14 +131,16 @@ def __init__(
self._request_timeout = request_timeout

@staticmethod
@override
def get_default_converter(
unload: bool = False,
) -> DefaultArrowTypeConverter | DefaultArrowUnloadTypeConverter | Any:
if unload:
return DefaultArrowUnloadTypeConverter()
return DefaultArrowTypeConverter()

@property
@property # type: ignore[explicit-override] # python/mypy#15900
@override
def arraysize(self) -> int:
return self._arraysize

Expand All @@ -147,6 +150,7 @@ def arraysize(self, value: int) -> None:
raise ProgrammingError("arraysize must be a positive integer value.")
self._arraysize = value

@override
def _collect_result_set(
self,
query_id: str,
Expand All @@ -171,6 +175,7 @@ def _collect_result_set(
**kwargs,
)

@override
def execute(
self,
operation: str,
Expand Down
3 changes: 3 additions & 0 deletions pyathena/arrow/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
_to_json,
_to_time,
)
from pyathena.util import override

_logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -90,6 +91,7 @@ def _dtypes(self) -> dict[str, type[Any]]:
}
return self.__dtypes

@override
def convert(self, type_: str, value: str | None, type_hint: str | None = None) -> Any | None:
converter = self.get(type_)
return converter(value)
Expand All @@ -114,6 +116,7 @@ def __init__(self) -> None:
default=_to_default,
)

@override
def convert(self, type_: str, value: str | None, type_hint: str | None = None) -> Any | None:
converter = self.get(type_)
return converter(value)
3 changes: 3 additions & 0 deletions pyathena/arrow/cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from pyathena.model import AthenaQueryExecution
from pyathena.options import ExecuteOptions
from pyathena.result_set import WithFetch
from pyathena.util import override

if TYPE_CHECKING:
import polars as pl
Expand Down Expand Up @@ -116,13 +117,15 @@ def __init__(
self._request_timeout = request_timeout

@staticmethod
@override
def get_default_converter(
unload: bool = False,
) -> DefaultArrowTypeConverter | DefaultArrowUnloadTypeConverter | Any:
if unload:
return DefaultArrowUnloadTypeConverter()
return DefaultArrowTypeConverter()

@override
def execute(
self,
operation: str,
Expand Down
5 changes: 4 additions & 1 deletion pyathena/arrow/result_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from pyathena.error import ProgrammingError
from pyathena.model import AthenaQueryExecution
from pyathena.result_set import AthenaResultSet
from pyathena.util import RetryConfig, parse_output_location
from pyathena.util import RetryConfig, override, parse_output_location

if TYPE_CHECKING:
import polars as pl
Expand Down Expand Up @@ -213,6 +213,7 @@ def converters(self) -> dict[str, Callable[[str | None], Any | None]]:
description = self.description if self.description else []
return {d[0]: self._converter.get(d[1]) for d in description}

@override
def _fetch(self) -> None:
try:
rows = next(self._batches)
Expand All @@ -227,6 +228,7 @@ def _fetch(self) -> None:
]
self._rows.extend(processed_rows)

@override
def fetchone(
self,
) -> tuple[Any | None, ...] | dict[Any, Any | None] | None:
Expand Down Expand Up @@ -383,6 +385,7 @@ def as_polars(self) -> pl.DataFrame:
"polars is required for as_polars(). Install it with: pip install polars"
) from e

@override
def close(self) -> None:
import pyarrow as pa

Expand Down
4 changes: 4 additions & 0 deletions pyathena/async_cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from pyathena.model import AthenaQueryExecution
from pyathena.options import ExecuteOptions
from pyathena.result_set import AthenaDictResultSet, AthenaResultSet
from pyathena.util import override

_logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -98,6 +99,7 @@ def arraysize(self, value: int) -> None:
)
self._arraysize = value

@override
def close(self, wait: bool = False) -> None:
self._executor.shutdown(wait=wait)

Expand Down Expand Up @@ -160,6 +162,7 @@ def _collect_result_set(
result_set_type_hints=result_set_type_hints,
)

@override
def execute(
self,
operation: str,
Expand Down Expand Up @@ -231,6 +234,7 @@ def execute(
self._collect_result_set, query_id, options.result_set_type_hints
)

@override
def executemany(
self,
operation: str,
Expand Down
Loading
Loading