Skip to content

Commit d567610

Browse files
authored
Merge pull request #76 from passren/0.7.6
0.7.6
2 parents e6a0887 + 2e47029 commit d567610

File tree

5 files changed

+83
-9
lines changed

5 files changed

+83
-9
lines changed

.github/workflows/run-test.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ on:
55
branches:
66
- "*"
77

8+
schedule:
9+
- cron: '0 3 * * 1'
10+
811
jobs:
912
pytest:
1013
runs-on: ubuntu-latest
@@ -56,7 +59,7 @@ jobs:
5659
run: |
5760
pip install pytest
5861
pip install pytest-cov
59-
pip install moto[sts]==4.2.0
62+
pip install moto[sts]
6063
pytest --cov=pydynamodb --cov-report=xml tests/
6164
- name: Install sqlalchemy 2.x
6265
run: |

pydynamodb/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
if TYPE_CHECKING:
77
from .connection import Connection
88

9-
__version__: str = "0.7.5"
9+
__version__: str = "0.7.6"
1010

1111
# Globals https://www.python.org/dev/peps/pep-0249/#globals
1212
apilevel: str = "2.0"

pydynamodb/sqlalchemy_dynamodb/pydynamodb.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# -*- coding: utf-8 -*-
22
import re
3+
import json
34
from ..util import strtobool
45
from typing import TYPE_CHECKING, cast, Optional, Sequence, List
56

@@ -36,6 +37,30 @@ def _check_sqla_major_ver(): # pragma: no cover
3637
from sqlalchemy.sql.compiler import InsertmanyvaluesSentinelOpts, _InsertManyValues
3738

3839

40+
class DynamoDBJSON(types.JSON):
41+
"""Custom JSON type for DynamoDB that handles SQLAlchemy 1.x compatibility."""
42+
43+
def result_processor(self, dialect, coltype):
44+
string_process = self._str_impl.result_processor(dialect, coltype)
45+
json_deserializer = dialect._json_deserializer or json.loads
46+
47+
def process(value):
48+
if value is None:
49+
return None
50+
51+
if isinstance(value, dict):
52+
return value
53+
54+
if isinstance(value, str) and _SQLALCHEMY_MAJOR_VERSION < 2:
55+
value = value.encode("utf-8")
56+
57+
if string_process:
58+
value = string_process(value)
59+
return json_deserializer(value)
60+
61+
return process
62+
63+
3964
class DynamoDBIdentifierPreparer(IdentifierPreparer):
4065
reserved_words = RESERVED_WORDS
4166

@@ -621,8 +646,29 @@ class DynamoDBDialect(DefaultDialect):
621646
description_encoding = None
622647
postfetch_lastrowid = False
623648

649+
# Custom type mapping for SQLAlchemy compatibility
650+
colspecs = {
651+
types.JSON: DynamoDBJSON,
652+
}
653+
624654
_connect_options = dict() # type: ignore
625655

656+
def __init__(
657+
self,
658+
json_serializer=None,
659+
json_deserializer=None,
660+
_json_serializer=None,
661+
_json_deserializer=None,
662+
**kwargs,
663+
):
664+
DefaultDialect.__init__(self, **kwargs)
665+
if _json_serializer:
666+
json_serializer = _json_serializer
667+
if _json_deserializer:
668+
json_deserializer = _json_deserializer
669+
self._json_serializer = json_serializer
670+
self._json_deserializer = json_deserializer
671+
626672
@classmethod
627673
def dbapi(cls):
628674
return pydynamodb

tests/test_connection.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: utf-8 -*-
22
import contextlib
3-
from moto import mock_sts
3+
from moto import mock_aws
44

55
TESTCASE01_TABLE = "pydynamodb_test_case01"
66

@@ -192,7 +192,7 @@ class TestConnectionWithSTS:
192192
TEST_WEB_IDENTITY_TOKEN = "Atza%7CIQEBLjAsAhRFiXuWpUXuRvQ9PZL3GMFcYevydwIUFAHZwXZXXXXXXXXJnrulxKDHwy87oGKPznh0D6bEQZTSCzyoCtL_8S07pLpr0zMbn6w1lfVZKNTBdDansFBmtGnIsIapjI6xKR02Yc_2bQ8LZbUXSGm6Ry6_BG7PrtLZtj_dfCTj92xNGed-CrKqjG7nPBjNIL016GGvuS5gSvPRUxWES3VYfm1wl7WTI7jn-Pcb6M-buCgHhFOzTQxod27L9CqnOLio7N3gZAGpsp6n1-AJBOCJckcyXe2c6uD0srOJeZlKUm2eTDVMf8IehDVI0r1QOnTV6KzzAI3OY87Vd_cVMQ"
193193
TEST_PROVIDER_ID = "www.amazon.com"
194194

195-
@mock_sts
195+
@mock_aws
196196
def test_conn_with_credentials(self):
197197
from pydynamodb import connect
198198

@@ -258,7 +258,7 @@ def test_conn_with_credentials(self):
258258
assert conn._session_kwargs["aws_secret_access_key"] is not None
259259
assert conn._session_kwargs["aws_session_token"] is not None
260260

261-
@mock_sts
261+
@mock_aws
262262
def test_conn_with_sqlalchemy(self):
263263
from pydynamodb import sqlalchemy_dynamodb # noqa
264264
import sqlalchemy # noqa

tests/test_sqlalchemy_dynamodb.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- coding: utf-8 -*-
22
from sqlalchemy.sql import text, select
33
from sqlalchemy.sql.schema import Column, MetaData, Table
4-
from sqlalchemy import Integer, String, Numeric
4+
from sqlalchemy import Integer, String, Numeric, JSON
55
from sqlalchemy.orm import declarative_base, Session
66

77
Base = declarative_base()
@@ -19,6 +19,7 @@ class _TestCase02(Base):
1919
col_str = Column(String)
2020
col_num = Column(Numeric)
2121
col_nested = Column()
22+
col_json = Column(JSON)
2223

2324
class _User(Base):
2425
__tablename__ = USER_TABLE
@@ -109,7 +110,7 @@ def test_nested_data_insert(self, engine):
109110
"""
110111
INSERT INTO "%s" VALUE {
111112
'key_partition': :pk, 'key_sort': :sk,
112-
'col_str': :col1, 'col_nested': :col2
113+
'col_str': :col1, 'col_nested': :col2, 'col_json': :col3
113114
}
114115
"""
115116
% TESTCASE02_TABLE
@@ -124,13 +125,14 @@ def test_nested_data_insert(self, engine):
124125
"sk": 0,
125126
"col1": "test case nested 0",
126127
"col2": nested_data,
128+
"col3": nested_data,
127129
}
128130
conn.execute(text(sql_one_row_2_0_), params_2_0_)
129131

130132
rows = conn.execute(
131133
text(
132134
"""
133-
SELECT col_nested FROM %s WHERE key_partition = :pk
135+
SELECT col_nested, col_json FROM %s WHERE key_partition = :pk
134136
AND key_sort = :sk
135137
"""
136138
% TESTCASE02_TABLE
@@ -139,6 +141,7 @@ def test_nested_data_insert(self, engine):
139141
).fetchall()
140142
assert len(rows) == 1
141143
assert rows[0][0] == nested_data
144+
assert rows[0][1] == nested_data
142145

143146
def test_declarative_table_insert(self, engine):
144147
engine, conn = engine
@@ -150,6 +153,7 @@ def test_declarative_table_insert(self, engine):
150153
test_case02.key_sort = i
151154
test_case02.col_str = "test case declarative table " + str(i)
152155
test_case02.col_num = i
156+
test_case02.col_json = {"key": "value"}
153157
session.add(test_case02)
154158
session.commit()
155159

@@ -176,6 +180,7 @@ def test_declarative_table_update(self, engine):
176180
).one()
177181
test_case.col_str = "test case declarative table 99"
178182
test_case.col_num = 99
183+
test_case.col_json = {"key": "value updated"}
179184
session.commit()
180185

181186
rows = conn.execute(
@@ -192,6 +197,24 @@ def test_declarative_table_update(self, engine):
192197
assert len(rows) == 1
193198
assert rows[0][2] == "test case declarative table 99"
194199
assert rows[0][3] == 99
200+
assert rows[0][5] == '{"key": "value updated"}'
201+
202+
def test_declarative_table_select(self, engine):
203+
engine, _ = engine
204+
205+
with Session(engine) as session:
206+
test_case = session.scalars(
207+
select(_TestCase02).where(
208+
_TestCase02.key_partition == "test_one_row_3",
209+
_TestCase02.key_sort == 4,
210+
)
211+
).one()
212+
assert test_case.key_partition == "test_one_row_3"
213+
assert test_case.key_sort == 4
214+
assert test_case.col_str == "test case declarative table 4"
215+
assert test_case.col_num == 4
216+
assert test_case.col_nested is None
217+
assert test_case.col_json == {"key": "value"}
195218

196219
def test_declarative_table_delete(self, engine):
197220
engine, _ = engine
@@ -260,11 +283,13 @@ def test_reflect_table(self, engine):
260283
Column("key_sort", Integer),
261284
Column("col_str", String),
262285
Column("col_num", Numeric),
286+
Column("col_json", JSON)
263287
)
264-
assert len(table.c) == 4
288+
assert len(table.c) == 5
265289

266290
rows = conn.execute(table.select()).fetchall()
267291
assert len(rows) == 10
292+
assert rows[7][4] == {"key": "value updated"}
268293

269294
rows = conn.execute(
270295
table.select().where(

0 commit comments

Comments
 (0)