Skip to content

Commit 6f81da3

Browse files
authored
Enhance DB-API 2.0 (PEP 249) Conformance (#52)
This pull request follows #51 to introduce several improvements and updates to the `aurora_data_api` library by: ### Feature Enhancements: * Changed the `connect` function in async version to `async` function. Although PEP 249 does not define async specifications, it seems to be common that the function is async, including sqlalchemy's expectation ([link](https://github.com/sqlalchemy/sqlalchemy/blob/main/lib/sqlalchemy/ext/asyncio/engine.py#L207)) * Reintroduced and implemented the `TimeFromTicks` function in `aurora_data_api/base.py` to convert ticks into a `datetime.time` object. * Added imports for exception classes in `aurora_data_api/__init__.py`, making them directly accessible from the top-level module. ### Code Cleanup and Consistency: * Reorganized imports across files (`aurora_data_api/async_.py`, `aurora_data_api/sync.py`, `aurora_data_api/base.py`) to improve readability and removed unused imports. Added `# noqa` comments for selective imports to suppress linter warnings. This is required to expose certain elements as per PEP 249. [[1]](diffhunk://#diff-cfa8ccd793ae4442598cce8935ecde0a8dcb5e0b4818a6ae358ebd4120416768L4-R38) [[2]](diffhunk://#diff-bd02193473dcd006fd8f5a83881b0490fe387259fdf6fe2df2dabfa509b7a2a0L4-R39) [[3]](diffhunk://#diff-47197841e264cbaec56838025b342376104f7bb383617f816333b01904fdec68L4-L23) * Standardized string quoting style (single to double quotes) in `aurora_data_api/async_.py` for consistency. ### Test Infrastructure: * Introduced a new `test/base.py` file containing base test classes and a PEP 249 conformance test mixin for validating compliance with the Python DB-API 2.0 specification. * Updated `test/test_async.py` to import and utilize the new base test classes and conformance tests. ### Minor Adjustments (`ruff format` & `ruff check --fix`): * Simplified the `extras_require` dictionary in `setup.py` by formatting it as a single line. * Adjusted line breaks in logging statements and comments for better readability in `aurora_data_api/async_.py`.
1 parent 2d60d0c commit 6f81da3

File tree

9 files changed

+641
-250
lines changed

9 files changed

+641
-250
lines changed

aurora_data_api/__init__.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
Time,
1313
Timestamp,
1414
DateFromTicks,
15-
# TimeFromTicks,
15+
TimeFromTicks,
1616
TimestampFromTicks,
1717
Binary,
1818
STRING,
@@ -22,3 +22,17 @@
2222
ROWID,
2323
DECIMAL,
2424
)
25+
from .exceptions import (
26+
Warning,
27+
Error,
28+
InterfaceError,
29+
DatabaseError,
30+
DataError,
31+
OperationalError,
32+
IntegrityError,
33+
InternalError,
34+
ProgrammingError,
35+
NotSupportedError,
36+
MySQLError,
37+
PostgreSQLError,
38+
)

aurora_data_api/async_.py

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,41 @@
11
"""
22
aurora-data-api - A Python DB-API 2.0 client for the AWS Aurora Serverless Data API (Async version)
33
"""
4-
import os, datetime, ipaddress, uuid, time, random, string, logging, itertools, reprlib, json, re, asyncio
5-
from decimal import Decimal
6-
from collections import namedtuple
7-
from collections.abc import Mapping
4+
5+
import time
6+
import random
7+
import string
8+
import reprlib
89
from .base import BaseAuroraDataAPIClient, BaseAuroraDataAPICursor, logger
910
from .base import (
10-
apilevel, threadsafety, paramstyle, Date, Time, Timestamp, DateFromTicks,
11-
TimestampFromTicks, Binary, STRING, BINARY, NUMBER, DATETIME, ROWID, DECIMAL,
12-
ColumnDescription
11+
apilevel, # noqa: F401
12+
threadsafety, # noqa: F401
13+
paramstyle, # noqa: F401
14+
Date, # noqa: F401
15+
Time, # noqa: F401
16+
Timestamp, # noqa: F401
17+
DateFromTicks, # noqa: F401
18+
TimeFromTicks, # noqa: F401
19+
TimestampFromTicks, # noqa: F401
20+
Binary, # noqa: F401
21+
STRING, # noqa: F401
22+
BINARY, # noqa: F401
23+
NUMBER, # noqa: F401
24+
DATETIME, # noqa: F401
25+
ROWID, # noqa: F401
26+
DECIMAL, # noqa: F401
1327
)
1428
from .exceptions import (
15-
Warning,
16-
Error,
17-
InterfaceError,
18-
DatabaseError,
19-
DataError,
20-
OperationalError,
21-
IntegrityError,
22-
InternalError,
23-
ProgrammingError,
24-
NotSupportedError,
25-
MySQLError,
26-
PostgreSQLError,
29+
Warning, # noqa: F401
30+
Error, # noqa: F401
31+
InterfaceError, # noqa: F401
32+
DatabaseError, # noqa: F401
33+
DataError, # noqa: F401
34+
OperationalError, # noqa: F401
35+
IntegrityError, # noqa: F401
36+
InternalError, # noqa: F401
37+
ProgrammingError, # noqa: F401
38+
NotSupportedError, # noqa: F401
2739
)
2840
import aiobotocore.session
2941

@@ -184,14 +196,14 @@ def __aiter__(self):
184196

185197
async def __anext__(self):
186198
if self._paging_state:
187-
if not hasattr(self, '_page_iterator'):
199+
if not hasattr(self, "_page_iterator"):
188200
self._page_iterator = self._fetch_paginated_records()
189201
try:
190202
return await self._page_iterator.__anext__()
191203
except StopAsyncIteration:
192204
raise StopAsyncIteration
193205
else:
194-
if not hasattr(self, '_record_index'):
206+
if not hasattr(self, "_record_index"):
195207
self._record_index = 0
196208
records = self._current_response.get("records", [])
197209
if self._record_index >= len(records):
@@ -203,16 +215,16 @@ async def __anext__(self):
203215
async def _fetch_paginated_records(self):
204216
next_page_args = self._paging_state["execute_statement_args"]
205217
while True:
206-
logger.debug(
207-
"Fetching page of %d records for auto-paginated query", self._paging_state["records_per_page"]
208-
)
218+
logger.debug("Fetching page of %d records for auto-paginated query", self._paging_state["records_per_page"])
209219
next_page_args["sql"] = "FETCH {records_per_page} FROM {pg_cursor_name}".format(**self._paging_state)
210220
try:
211221
page = await self._client.execute_statement(**next_page_args)
212222
except self._client.exceptions.BadRequestException as e:
213223
cur_rpp = self._paging_state["records_per_page"]
214224
if "Database returned more than the allowed response size limit" in str(e) and cur_rpp > 1:
215-
await self.scroll(-self._paging_state["records_per_page"]) # Rewind the cursor to read the page again
225+
await self.scroll(
226+
-self._paging_state["records_per_page"]
227+
) # Rewind the cursor to read the page again
216228
logger.debug("Halving records per page")
217229
self._paging_state["records_per_page"] //= 2
218230
continue
@@ -262,7 +274,7 @@ async def __aexit__(self, err_type, value, traceback):
262274
self._current_response = None
263275

264276

265-
def connect(
277+
async def connect(
266278
aurora_cluster_arn=None,
267279
secret_arn=None,
268280
rds_data_client=None,

aurora_data_api/base.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,24 @@
11
"""
22
Base classes for Aurora Data API clients and cursors
33
"""
4-
import os, datetime, ipaddress, uuid, time, random, string, logging, itertools, reprlib, json, re
4+
5+
import os
6+
import datetime
7+
import ipaddress
8+
import uuid
9+
import time
10+
import logging
11+
import itertools
12+
import re
513
from decimal import Decimal
614
from collections import namedtuple
715
from collections.abc import Mapping
816
from .exceptions import (
9-
Warning,
10-
Error,
11-
InterfaceError,
1217
DatabaseError,
13-
DataError,
14-
OperationalError,
15-
IntegrityError,
16-
InternalError,
17-
ProgrammingError,
1818
NotSupportedError,
1919
MySQLError,
2020
PostgreSQLError,
2121
)
22-
from .error_codes_mysql import MySQLErrorCodes
23-
from .error_codes_postgresql import PostgreSQLErrorCodes
2422

2523
apilevel = "2.0"
2624

@@ -32,7 +30,12 @@
3230
Time = datetime.time
3331
Timestamp = datetime.datetime
3432
DateFromTicks = datetime.date.fromtimestamp
35-
# TimeFromTicks = datetime.time.fromtimestamp TODO
33+
34+
35+
def TimeFromTicks(ticks):
36+
return Time(*time.localtime(ticks)[3:6])
37+
38+
3639
TimestampFromTicks = datetime.datetime.fromtimestamp
3740
Binary = bytes
3841
STRING = str
@@ -50,7 +53,7 @@
5053

5154
class BaseAuroraDataAPIClient:
5255
"""Base class for Aurora Data API clients"""
53-
56+
5457
def __init__(
5558
self,
5659
dbname=None,
@@ -74,7 +77,7 @@ def close(self):
7477

7578
class BaseAuroraDataAPICursor:
7679
"""Base class for Aurora Data API cursors"""
77-
80+
7881
_pg_type_map = {
7982
"int": int,
8083
"int2": int,

aurora_data_api/sync.py

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,42 @@
11
"""
22
aurora-data-api - A Python DB-API 2.0 client for the AWS Aurora Serverless Data API
33
"""
4-
import os, datetime, ipaddress, uuid, time, random, string, logging, itertools, reprlib, json, re, threading
5-
from decimal import Decimal
6-
from collections import namedtuple
7-
from collections.abc import Mapping
4+
5+
import time
6+
import random
7+
import string
8+
import reprlib
9+
import threading
810
from .base import BaseAuroraDataAPIClient, BaseAuroraDataAPICursor, logger
911
from .base import (
10-
apilevel, threadsafety, paramstyle, Date, Time, Timestamp, DateFromTicks,
11-
TimestampFromTicks, Binary, STRING, BINARY, NUMBER, DATETIME, ROWID, DECIMAL,
12-
ColumnDescription
12+
apilevel, # noqa: F401
13+
threadsafety, # noqa: F401
14+
paramstyle, # noqa: F401
15+
Date, # noqa: F401
16+
Time, # noqa: F401
17+
Timestamp, # noqa: F401
18+
DateFromTicks, # noqa: F401
19+
TimeFromTicks, # noqa: F401
20+
TimestampFromTicks, # noqa: F401
21+
Binary, # noqa: F401
22+
STRING, # noqa: F401
23+
BINARY, # noqa: F401
24+
NUMBER, # noqa: F401
25+
DATETIME, # noqa: F401
26+
ROWID, # noqa: F401
27+
DECIMAL, # noqa: F401
1328
)
1429
from .exceptions import (
15-
Warning,
16-
Error,
17-
InterfaceError,
18-
DatabaseError,
19-
DataError,
20-
OperationalError,
21-
IntegrityError,
22-
InternalError,
23-
ProgrammingError,
24-
NotSupportedError,
25-
MySQLError,
26-
PostgreSQLError,
30+
Warning, # noqa: F401
31+
Error, # noqa: F401
32+
InterfaceError, # noqa: F401
33+
DatabaseError, # noqa: F401
34+
DataError, # noqa: F401
35+
OperationalError, # noqa: F401
36+
IntegrityError, # noqa: F401
37+
InternalError, # noqa: F401
38+
ProgrammingError, # noqa: F401
39+
NotSupportedError, # noqa: F401
2740
)
2841
import botocore.session
2942

setup.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@
1212
description="A Python DB-API 2.0 client for the AWS Aurora Serverless Data API",
1313
long_description=open("README.rst").read(),
1414
install_requires=["botocore >= 1.38.40, < 2"],
15-
extras_require={
16-
"async": "aiobotocore >= 2.23.1, < 3"
17-
},
15+
extras_require={"async": "aiobotocore >= 2.23.1, < 3"},
1816
packages=find_packages(exclude=["test"]),
1917
platforms=["MacOS X", "Posix"],
2018
test_suite="test",

0 commit comments

Comments
 (0)