Skip to content

Commit 7fd8d29

Browse files
authored
Merge branch 'master' into association_proxy-support
2 parents 64ee907 + d0668cc commit 7fd8d29

File tree

10 files changed

+100
-31
lines changed

10 files changed

+100
-31
lines changed

Diff for: .github/workflows/tests.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ jobs:
1414
strategy:
1515
max-parallel: 10
1616
matrix:
17-
sql-alchemy: ["1.2", "1.3", "1.4"]
18-
python-version: ["3.7", "3.8", "3.9", "3.10"]
17+
sql-alchemy: [ "1.2", "1.3", "1.4","2.0" ]
18+
python-version: [ "3.7", "3.8", "3.9", "3.10" ]
1919

2020
steps:
2121
- uses: actions/checkout@v3

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ __pycache__/
1212
.Python
1313
env/
1414
.venv/
15+
venv/
1516
build/
1617
develop-eggs/
1718
dist/

Diff for: graphene_sqlalchemy/batching.py

+18-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@
55
import sqlalchemy
66
from sqlalchemy.orm import Session, strategies
77
from sqlalchemy.orm.query import QueryContext
8+
from sqlalchemy.util import immutabledict
89

9-
from .utils import SQL_VERSION_HIGHER_EQUAL_THAN_1_4, is_graphene_version_less_than
10+
from .utils import (
11+
SQL_VERSION_HIGHER_EQUAL_THAN_1_4,
12+
SQL_VERSION_HIGHER_EQUAL_THAN_2,
13+
is_graphene_version_less_than,
14+
)
1015

1116

1217
def get_data_loader_impl() -> Any: # pragma: no cover
@@ -76,7 +81,18 @@ async def batch_load_fn(self, parents):
7681
query_context = parent_mapper_query._compile_context()
7782
else:
7883
query_context = QueryContext(session.query(parent_mapper.entity))
79-
if SQL_VERSION_HIGHER_EQUAL_THAN_1_4:
84+
if SQL_VERSION_HIGHER_EQUAL_THAN_2: # pragma: no cover
85+
self.selectin_loader._load_for_path(
86+
query_context,
87+
parent_mapper._path_registry,
88+
states,
89+
None,
90+
child_mapper,
91+
None,
92+
None, # recursion depth can be none
93+
immutabledict(), # default value for selectinload->lazyload
94+
)
95+
elif SQL_VERSION_HIGHER_EQUAL_THAN_1_4:
8096
self.selectin_loader._load_for_path(
8197
query_context,
8298
parent_mapper._path_registry,

Diff for: graphene_sqlalchemy/tests/models.py

+18-5
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,24 @@
1616
String,
1717
Table,
1818
func,
19-
select,
2019
)
2120
from sqlalchemy.ext.associationproxy import association_proxy
2221
from sqlalchemy.ext.declarative import declarative_base
2322
from sqlalchemy.ext.hybrid import hybrid_property
2423
from sqlalchemy.orm import backref, column_property, composite, mapper, relationship
25-
from sqlalchemy.sql.sqltypes import _LookupExpressionAdapter
2624
from sqlalchemy.sql.type_api import TypeEngine
2725

26+
from graphene_sqlalchemy.tests.utils import wrap_select_func
27+
from graphene_sqlalchemy.utils import SQL_VERSION_HIGHER_EQUAL_THAN_1_4, SQL_VERSION_HIGHER_EQUAL_THAN_2
28+
29+
# fmt: off
30+
import sqlalchemy
31+
if SQL_VERSION_HIGHER_EQUAL_THAN_2:
32+
from sqlalchemy.sql.sqltypes import HasExpressionLookup # noqa # isort:skip
33+
else:
34+
from sqlalchemy.sql.sqltypes import _LookupExpressionAdapter as HasExpressionLookup # noqa # isort:skip
35+
# fmt: on
36+
2837
PetKind = Enum("cat", "dog", name="pet_kind")
2938

3039

@@ -132,7 +141,7 @@ def hybrid_prop_list(self) -> List[int]:
132141
return [1, 2, 3]
133142

134143
column_prop = column_property(
135-
select([func.cast(func.count(id), Integer)]), doc="Column property"
144+
wrap_select_func(func.cast(func.count(id), Integer)), doc="Column property"
136145
)
137146

138147
composite_prop = composite(
@@ -179,7 +188,11 @@ def __subclasses__(cls):
179188

180189
editor_table = Table("editors", Base.metadata, autoload=True)
181190

182-
mapper(ReflectedEditor, editor_table)
191+
# TODO Remove when switching min sqlalchemy version to SQLAlchemy 1.4
192+
if SQL_VERSION_HIGHER_EQUAL_THAN_1_4:
193+
Base.registry.map_imperatively(ReflectedEditor, editor_table)
194+
else:
195+
mapper(ReflectedEditor, editor_table)
183196

184197

185198
############################################
@@ -353,7 +366,7 @@ class Employee(Person):
353366
############################################
354367

355368

356-
class CustomIntegerColumn(_LookupExpressionAdapter, TypeEngine):
369+
class CustomIntegerColumn(HasExpressionLookup, TypeEngine):
357370
"""
358371
Custom Column Type that our converters don't recognize
359372
Adapted from sqlalchemy.Integer

Diff for: graphene_sqlalchemy/tests/models_batching.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@
1111
String,
1212
Table,
1313
func,
14-
select,
1514
)
1615
from sqlalchemy.ext.declarative import declarative_base
1716
from sqlalchemy.orm import column_property, relationship
1817

18+
from graphene_sqlalchemy.tests.utils import wrap_select_func
19+
1920
PetKind = Enum("cat", "dog", name="pet_kind")
2021

2122

@@ -61,7 +62,7 @@ class Reporter(Base):
6162
favorite_article = relationship("Article", uselist=False)
6263

6364
column_prop = column_property(
64-
select([func.cast(func.count(id), Integer)]), doc="Column property"
65+
wrap_select_func(func.cast(func.count(id), Integer)), doc="Column property"
6566
)
6667

6768

Diff for: graphene_sqlalchemy/tests/test_converter.py

+32-15
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,28 @@
22
import sys
33
from typing import Dict, Tuple, Union
44

5+
import graphene
56
import pytest
67
import sqlalchemy
78
import sqlalchemy_utils as sqa_utils
8-
from sqlalchemy import Column, func, select, types
9+
from graphene.relay import Node
10+
from graphene.types.structures import Structure
11+
from sqlalchemy import Column, func, types
912
from sqlalchemy.dialects import postgresql
1013
from sqlalchemy.ext.declarative import declarative_base
1114
from sqlalchemy.ext.hybrid import hybrid_property
1215
from sqlalchemy.inspection import inspect
1316
from sqlalchemy.orm import column_property, composite
1417

15-
import graphene
16-
from graphene.relay import Node
17-
from graphene.types.structures import Structure
18-
18+
from .models import (
19+
Article,
20+
CompositeFullName,
21+
Pet,
22+
Reporter,
23+
ShoppingCart,
24+
ShoppingCartItem,
25+
)
26+
from .utils import wrap_select_func
1927
from ..converter import (
2028
convert_sqlalchemy_association_proxy,
2129
convert_sqlalchemy_column,
@@ -28,6 +36,7 @@
2836
from ..fields import UnsortedSQLAlchemyConnectionField, default_connection_field_factory
2937
from ..registry import Registry, get_global_registry
3038
from ..types import ORMField, SQLAlchemyObjectType
39+
from ..utils import is_sqlalchemy_version_less_than
3140
from .models import (
3241
Article,
3342
CompositeFullName,
@@ -206,9 +215,9 @@ def prop_method() -> int | str:
206215
return "not allowed in gql schema"
207216

208217
with pytest.raises(
209-
ValueError,
210-
match=r"Cannot convert hybrid_property Union to "
211-
r"graphene.Union: the Union contains scalars. \.*",
218+
ValueError,
219+
match=r"Cannot convert hybrid_property Union to "
220+
r"graphene.Union: the Union contains scalars. \.*",
212221
):
213222
get_hybrid_property_type(prop_method)
214223

@@ -462,7 +471,7 @@ class TestEnum(enum.IntEnum):
462471

463472
def test_should_columproperty_convert():
464473
field = get_field_from_column(
465-
column_property(select([func.sum(func.cast(id, types.Integer))]).where(id == 1))
474+
column_property(wrap_select_func(func.sum(func.cast(id, types.Integer))).where(id == 1))
466475
)
467476

468477
assert field.type == graphene.Int
@@ -479,10 +488,18 @@ def test_should_jsontype_convert_jsonstring():
479488
assert get_field(types.JSON).type == graphene.JSONString
480489

481490

491+
@pytest.mark.skipif(
492+
(not is_sqlalchemy_version_less_than("2.0.0b1")),
493+
reason="SQLAlchemy >=2.0 does not support this: Variant is no longer used in SQLAlchemy",
494+
)
482495
def test_should_variant_int_convert_int():
483496
assert get_field(types.Variant(types.Integer(), {})).type == graphene.Int
484497

485498

499+
@pytest.mark.skipif(
500+
(not is_sqlalchemy_version_less_than("2.0.0b1")),
501+
reason="SQLAlchemy >=2.0 does not support this: Variant is no longer used in SQLAlchemy",
502+
)
486503
def test_should_variant_string_convert_string():
487504
assert get_field(types.Variant(types.String(), {})).type == graphene.String
488505

@@ -871,8 +888,8 @@ class Meta:
871888
)
872889

873890
for (
874-
hybrid_prop_name,
875-
hybrid_prop_expected_return_type,
891+
hybrid_prop_name,
892+
hybrid_prop_expected_return_type,
876893
) in shopping_cart_item_expected_types.items():
877894
hybrid_prop_field = ShoppingCartItemType._meta.fields[hybrid_prop_name]
878895

@@ -883,7 +900,7 @@ class Meta:
883900
str(hybrid_prop_expected_return_type),
884901
)
885902
assert (
886-
hybrid_prop_field.description is None
903+
hybrid_prop_field.description is None
887904
) # "doc" is ignored by hybrid property
888905

889906
###################################################
@@ -930,8 +947,8 @@ class Meta:
930947
)
931948

932949
for (
933-
hybrid_prop_name,
934-
hybrid_prop_expected_return_type,
950+
hybrid_prop_name,
951+
hybrid_prop_expected_return_type,
935952
) in shopping_cart_expected_types.items():
936953
hybrid_prop_field = ShoppingCartType._meta.fields[hybrid_prop_name]
937954

@@ -942,5 +959,5 @@ class Meta:
942959
str(hybrid_prop_expected_return_type),
943960
)
944961
assert (
945-
hybrid_prop_field.description is None
962+
hybrid_prop_field.description is None
946963
) # "doc" is ignored by hybrid property

Diff for: graphene_sqlalchemy/tests/utils.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import inspect
22
import re
33

4+
from sqlalchemy import select
5+
6+
from graphene_sqlalchemy.utils import SQL_VERSION_HIGHER_EQUAL_THAN_1_4
7+
48

59
def to_std_dicts(value):
610
"""Convert nested ordered dicts to normal dicts for better comparison."""
@@ -18,8 +22,15 @@ def remove_cache_miss_stat(message):
1822
return re.sub(r"\[generated in \d+.?\d*s\]\s", "", message)
1923

2024

21-
async def eventually_await_session(session, func, *args):
25+
def wrap_select_func(query):
26+
# TODO remove this when we drop support for sqa < 2.0
27+
if SQL_VERSION_HIGHER_EQUAL_THAN_1_4:
28+
return select(query)
29+
else:
30+
return select([query])
31+
2232

33+
async def eventually_await_session(session, func, *args):
2334
if inspect.iscoroutinefunction(getattr(session, func)):
2435
await getattr(session, func)(*args)
2536
else:

Diff for: graphene_sqlalchemy/utils.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,18 @@ def is_graphene_version_less_than(version_string): # pragma: no cover
2727

2828
SQL_VERSION_HIGHER_EQUAL_THAN_1_4 = False
2929

30-
if not is_sqlalchemy_version_less_than("1.4"):
30+
if not is_sqlalchemy_version_less_than("1.4"): # pragma: no cover
3131
from sqlalchemy.ext.asyncio import AsyncSession
3232

3333
SQL_VERSION_HIGHER_EQUAL_THAN_1_4 = True
3434

3535

36+
SQL_VERSION_HIGHER_EQUAL_THAN_2 = False
37+
38+
if not is_sqlalchemy_version_less_than("2.0.0b1"): # pragma: no cover
39+
SQL_VERSION_HIGHER_EQUAL_THAN_2 = True
40+
41+
3642
def get_session(context):
3743
return context.get("session")
3844

Diff for: setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# To keep things simple, we only support newer versions of Graphene
1616
"graphene>=3.0.0b7",
1717
"promise>=2.3",
18-
"SQLAlchemy>=1.1,<2",
18+
"SQLAlchemy>=1.1",
1919
"aiodataloader>=0.2.0,<1.0",
2020
]
2121

Diff for: tox.ini

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[tox]
2-
envlist = pre-commit,py{37,38,39,310}-sql{12,13,14}
2+
envlist = pre-commit,py{37,38,39,310}-sql{12,13,14,20}
33
skipsdist = true
44
minversion = 3.7.0
55

@@ -15,6 +15,7 @@ SQLALCHEMY =
1515
1.2: sql12
1616
1.3: sql13
1717
1.4: sql14
18+
2.0: sql20
1819

1920
[testenv]
2021
passenv = GITHUB_*
@@ -23,8 +24,11 @@ deps =
2324
sql12: sqlalchemy>=1.2,<1.3
2425
sql13: sqlalchemy>=1.3,<1.4
2526
sql14: sqlalchemy>=1.4,<1.5
27+
sql20: sqlalchemy>=2.0.0b3
28+
setenv =
29+
SQLALCHEMY_WARN_20 = 1
2630
commands =
27-
pytest graphene_sqlalchemy --cov=graphene_sqlalchemy --cov-report=term --cov-report=xml {posargs}
31+
python -W always -m pytest graphene_sqlalchemy --cov=graphene_sqlalchemy --cov-report=term --cov-report=xml {posargs}
2832

2933
[testenv:pre-commit]
3034
basepython=python3.10

0 commit comments

Comments
 (0)