Skip to content

Commit 05f9648

Browse files
authored
drop supporting sqlalchemy object (#70)
* drop supporting sqlalchemy object * fix format * update isort * fix isort option * remove unused test * remove comment * update documents
1 parent 670c845 commit 05f9648

9 files changed

Lines changed: 37 additions & 402 deletions

File tree

README.md

Lines changed: 9 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@
77
![license](https://img.shields.io/github/license/koxudaxi/py-data-api.svg)
88
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
99

10-
py-data-api is a user-friendly client which supports SQLAlchemy models.
11-
Also, the package includes DB API 2.0 Client and SQLAlchemy Dialects.
10+
py-data-api is a client for Data API of [Aurora Serverless](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/data-api.html).
11+
Also, the package includes SQLAlchemy Dialects and DB API 2.0 Client.
1212

1313
## Features
14-
- A user-friendly client which supports SQLAlchemy models
1514
- SQLAlchemy Dialects
1615
- DB API 2.0 compatible client [PEP 249](https://www.python.org/dev/peps/pep-0249/)
1716

@@ -38,10 +37,8 @@ from typing import List
3837

3938
from sqlalchemy import Column, Integer, String
4039
from sqlalchemy.ext.declarative import declarative_base
41-
from sqlalchemy.orm import Query
42-
from sqlalchemy.sql import Insert
4340

44-
from pydataapi import DataAPI, transaction, Result, Record
41+
from pydataapi import DataAPI, Result
4542

4643

4744
class Pets(declarative_base()):
@@ -54,115 +51,6 @@ database: str = 'test'
5451
resource_arn: str = 'arn:aws:rds:us-east-1:123456789012:cluster:serverless-test-1'
5552
secret_arn: str = 'arn:aws:secretsmanager:us-east-1:123456789012:secret:serverless-test1'
5653

57-
58-
def example_with_statement():
59-
# DataAPI supports with statement for handling transaction
60-
with DataAPI(database=database, resource_arn=resource_arn, secret_arn=secret_arn) as data_api:
61-
62-
# start transaction
63-
64-
insert: Insert = Insert(Pets, {'name': 'dog'})
65-
# INSERT INTO pets (name) VALUES ('dog')
66-
67-
# `execute` accepts SQL statement as str or SQL Alchemy SQL objects
68-
result: Result = data_api.execute(insert)
69-
print(result.number_of_records_updated)
70-
# 1
71-
72-
query = Query(Pets).filter(Pets.id == 1)
73-
result: Result = data_api.execute(query) # or data_api.execute('select id, name from pets')
74-
# SELECT pets.id, pets.name FROM pets WHERE pets.id = 1
75-
76-
# `Result` like a Result object in SQL Alchemy
77-
print(result.scalar())
78-
# 1
79-
80-
print(result.one())
81-
# [Record<id=1, name='dog'>]
82-
83-
# `Result` is Sequence[Record]
84-
records: List[Record] = list(result)
85-
print(records)
86-
# [Record<id=1, name='dog'>]
87-
88-
# Record is Sequence and Iterator
89-
record = records[0]
90-
print(record[0])
91-
# 1
92-
print(record[1])
93-
# dog
94-
95-
for column in record:
96-
print(column)
97-
# 1 ...
98-
99-
# show record as dict()
100-
print(record.dict())
101-
# {'id': 1, 'name': 'dog'}
102-
103-
# batch insert
104-
insert: Insert = Insert(Pets)
105-
data_api.batch_execute(insert, [
106-
{'id': 2, 'name': 'cat'},
107-
{'id': 3, 'name': 'snake'},
108-
{'id': 4, 'name': 'rabbit'},
109-
])
110-
111-
result = data_api.execute('select * from pets')
112-
print(list(result))
113-
# [Record<id=1, name='dog'>, Record<id=2, name='cat'>, Record<id=3, name='snake'>, Record<id=4, name='rabbit'>]
114-
115-
# result is a sequence object
116-
for record in result:
117-
print(record)
118-
# Record<id=1, name='dog'> ...
119-
120-
# commit
121-
122-
123-
def example_decorator():
124-
pet_names: List[str] = ['dog', 'cat', 'snake']
125-
add_pets(pet_names)
126-
127-
128-
@transaction(database=database, resource_arn=resource_arn, secret_arn=secret_arn)
129-
def add_pets(data_api: DataAPI, pet_names: List[str]) -> None:
130-
# start transaction
131-
for pet_name in pet_names:
132-
data_api.execute(Insert(Pets, {'name': pet_name}))
133-
# some logic ...
134-
135-
# commit
136-
137-
138-
def example_simple_execute():
139-
data_api = DataAPI(resource_arn=resource_arn, secret_arn=secret_arn, database=database)
140-
result: Result = data_api.execute('show tables')
141-
print(result.scalar())
142-
# Pets
143-
144-
145-
def example_rollback():
146-
with DataAPI(resource_arn=resource_arn, secret_arn=secret_arn) as data_api:
147-
data_api.execute(Insert(Pets, {'name': 'dog'}))
148-
# you can rollback by Exception
149-
raise Exception
150-
151-
152-
def example_rollback_with_custom_exception():
153-
class OriginalError(Exception):
154-
pass
155-
156-
with DataAPI(resource_arn=resource_arn, secret_arn=secret_arn, rollback_exception=OriginalError) as data_api:
157-
158-
data_api.execute(Insert(Pets, {'name': 'dog'}))
159-
# some logic ...
160-
161-
# rollback when happen `rollback_exception`
162-
raise OriginalError # rollback
163-
164-
# raise Exception <- DataAPI don't rollback
165-
16654
def example_driver_for_sqlalchemy():
16755
from sqlalchemy.engine import create_engine
16856
engine = create_engine(
@@ -173,9 +61,14 @@ def example_driver_for_sqlalchemy():
17361
'database': 'test'}
17462
)
17563

176-
result: ResultProxy = engine.execute("select * from pets")
64+
result = engine.execute("select * from pets")
17765
print(result.fetchall())
17866

67+
def example_simple_execute():
68+
data_api = DataAPI(resource_arn=resource_arn, secret_arn=secret_arn, database=database)
69+
result: Result = data_api.execute('show tables')
70+
print(result.scalar())
71+
# Pets
17972
```
18073

18174
## Contributing to pydataapi

example.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
from sqlalchemy import Column, Integer, String
44
from sqlalchemy.engine import ResultProxy
55
from sqlalchemy.ext.declarative import declarative_base
6-
from sqlalchemy.orm import Query
7-
from sqlalchemy.sql import Insert
86

97
from pydataapi import DataAPI, Record, Result, transaction
108

pydataapi/pydataapi.py

Lines changed: 7 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
from contextlib import AbstractContextManager
21
from datetime import date, datetime, time
32
from decimal import Decimal
43
from functools import wraps
54
from typing import (
65
Any,
76
Callable,
8-
ContextManager,
97
Dict,
108
Iterator,
119
List,
@@ -23,8 +21,6 @@
2321
from sqlalchemy import Column
2422
from sqlalchemy.dialects import mysql
2523
from sqlalchemy.engine import Dialect, default
26-
from sqlalchemy.orm.query import Query
27-
from sqlalchemy.sql import Delete, Insert, Select, Update
2824

2925
from pydataapi.exceptions import DataAPIError, MultipleResultsFound, NoResultFound
3026

@@ -57,69 +53,6 @@
5753
DATE_TYPE_HINT: str = "DATE"
5854

5955

60-
def generate_sql(query: Union[Query, Insert, Update, Delete, Select]) -> str:
61-
if hasattr(query, "statement"):
62-
sql: str = query.statement.compile(**QUERY_STATEMENT_COMPILE_PARAMS)
63-
else:
64-
sql = query.compile(**QUERY_STATEMENT_COMPILE_PARAMS)
65-
return str(sql)
66-
67-
68-
def wrap_process_result_value_function(
69-
process_result_value: Callable[..., Any], dialect: default.DefaultDialect
70-
) -> Callable[..., Any]:
71-
@wraps(process_result_value)
72-
def wrapped(value: Any) -> Callable[..., Any]:
73-
return process_result_value(value, dialect)
74-
75-
return wrapped
76-
77-
78-
def get_process_result_value_function(
79-
table_name: str,
80-
column_name: str,
81-
query: Union[Select, Query],
82-
dialect: default.DefaultDialect,
83-
) -> Callable[..., Any]:
84-
process_result_value: Optional[Callable[..., Any]] = None
85-
if isinstance(query, Select): # pragma: no cover
86-
for column in query.columns:
87-
if column.name == column_name:
88-
process_result_value = getattr(
89-
column.type, "process_result_value", None
90-
)
91-
break
92-
elif isinstance(query, Query): # pragma: no cover
93-
for column_description in query.column_descriptions:
94-
type_ = column_description["type"]
95-
if type_.__tablename__ == table_name:
96-
column = getattr(type_, column_name, None)
97-
if column:
98-
expression = getattr(column, "expression", None)
99-
if (
100-
isinstance(expression, Column)
101-
and expression.name == column_name
102-
):
103-
process_result_value = getattr(
104-
expression.type, "process_result_value", None
105-
)
106-
break
107-
if process_result_value:
108-
return wrap_process_result_value_function(process_result_value, dialect)
109-
return lambda v: v
110-
111-
112-
def create_process_result_value_function_list(
113-
column_metadata: List[Dict[str, Any]],
114-
query: Union[Select, Query],
115-
dialect: default.DefaultDialect,
116-
) -> List[Callable[..., Any]]:
117-
return [
118-
get_process_result_value_function(cm["tableName"], cm["name"], query, dialect)
119-
for cm in column_metadata
120-
]
121-
122-
12356
def convert_array_value(value: Union[List[Any], Tuple[Any, ...]]) -> Dict[str, Any]:
12457
first_value: Any = value[0]
12558
if isinstance(first_value, (list, tuple)):
@@ -312,27 +245,12 @@ def __getitem__(self, i: Union[int, slice]) -> Union["Record", List["Record"]]:
312245
def __len__(self) -> int:
313246
return len(self._rows)
314247

315-
def __init__(
316-
self,
317-
response: Dict[Any, Any],
318-
process_result_value_function_list: Optional[List[Callable[..., Any]]] = None,
319-
) -> None:
248+
def __init__(self, response: Dict[Any, Any],) -> None:
320249
self._response = response
321-
if process_result_value_function_list:
322-
self._rows: Sequence[List[Any]] = [
323-
[
324-
process_result_value(_get_value_from_row(column))
325-
for column, process_result_value in zip(
326-
row, process_result_value_function_list
327-
)
328-
]
329-
for row in response.get("records", [])
330-
]
331-
else:
332-
self._rows = [
333-
[_get_value_from_row(column) for column in row]
334-
for row in response.get("records", [])
335-
]
250+
self._rows = [
251+
[_get_value_from_row(column) for column in row]
252+
for row in response.get("records", [])
253+
]
336254
self._column_metadata: List[Dict[str, Any]] = response.get("columnMetadata", [])
337255
self._headers: Optional[List[str]] = None
338256
self._index: int = -1
@@ -418,12 +336,6 @@ def convert_parameter_sets(cls, v: Any) -> Any:
418336
return [create_sql_parameters(parameter) for parameter in v]
419337
return v # pragma: no cover
420338

421-
@validator("sql", pre=True)
422-
def validate_sql(cls, v: Any) -> Any:
423-
if isinstance(v, str):
424-
return v
425-
return generate_sql(v)
426-
427339
def build(self) -> Dict[str, Any]:
428340
return self.dict(exclude_unset=True, by_alias=True)
429341

@@ -547,7 +459,7 @@ def rollback(self, transaction_id: Optional[str] = None) -> str:
547459

548460
def execute(
549461
self,
550-
query: Union[Query, Insert, Update, Delete, Select, str],
462+
query: str,
551463
parameters: Optional[Dict[str, Any]] = None,
552464
transaction_id: Optional[str] = None,
553465
continue_after_timeout: bool = True,
@@ -568,18 +480,11 @@ def execute(
568480
includeResultMetadata=True, **options.build()
569481
)
570482

571-
if isinstance(query, (Query, Select)):
572-
process_result_value_function_list = create_process_result_value_function_list(
573-
response.get("columnMetadata", []),
574-
query,
575-
QUERY_STATEMENT_COMPILE_PARAMS["dialect"],
576-
)
577-
return Result(response, process_result_value_function_list)
578483
return Result(response)
579484

580485
def batch_execute(
581486
self,
582-
query: Union[Query, Insert, Update, Delete, Select, str],
487+
query: str,
583488
parameter_sets: Optional[List[Dict[str, Any]]],
584489
transaction_id: Optional[str] = None,
585490
database: Optional[str] = None,

scripts/format.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
set -e
33

44
black pydataapi tests
5-
isort --recursive pydataapi tests
5+
isort pydataapi tests

scripts/lint.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
set -e
33

44
black pydataapi tests --check
5-
isort --recursive --check-only pydataapi tests
5+
isort --check-only pydataapi tests
66
mypy pydataapi

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ tests_require =
3939
pytest-docker-compose
4040
mypy
4141
black
42-
isort
42+
isort>=5.0.7
4343
PyMySQL
4444
docker-compose
4545
requests == 2.20.1

0 commit comments

Comments
 (0)