From 2e321e92f14613ce97c1293168da49d8c77a463e Mon Sep 17 00:00:00 2001 From: Erik Wrede Date: Sun, 4 Dec 2022 21:13:29 +0100 Subject: [PATCH 01/11] chore: prepare for sqlalchemy2.0 adjustments --- .github/workflows/tests.yml | 13 ++++--------- graphene_sqlalchemy/tests/conftest.py | 7 +++++++ tox.ini | 6 +++++- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7632fd38..d8239457 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,20 +1,15 @@ name: Tests -on: - push: - branches: - - 'master' - pull_request: - branches: - - '*' +on: [ push, pull_request ] + jobs: test: runs-on: ubuntu-latest strategy: max-parallel: 10 matrix: - sql-alchemy: ["1.2", "1.3", "1.4"] - python-version: ["3.7", "3.8", "3.9", "3.10"] + sql-alchemy: [ "1.2", "1.3", "1.4", "2.0" ] + python-version: [ "3.7", "3.8", "3.9", "3.10" ] steps: - uses: actions/checkout@v3 diff --git a/graphene_sqlalchemy/tests/conftest.py b/graphene_sqlalchemy/tests/conftest.py index 89b357a4..d3fcedc9 100644 --- a/graphene_sqlalchemy/tests/conftest.py +++ b/graphene_sqlalchemy/tests/conftest.py @@ -3,6 +3,13 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker +# fmt: off +# Fixme remove when https://github.com/kvesteri/sqlalchemy-utils/pull/644 is released #noqa +import sqlalchemy # noqa # isort:skip +if sqlalchemy.__version__ == "2.0.0b3": # noqa # isort:skip + sqlalchemy.__version__ = "2.0.0" # noqa # isort:skip +# fmt: on + import graphene from graphene_sqlalchemy.utils import SQL_VERSION_HIGHER_EQUAL_THAN_1_4 diff --git a/tox.ini b/tox.ini index 2802dee0..f7b5f973 100644 --- a/tox.ini +++ b/tox.ini @@ -15,6 +15,7 @@ SQLALCHEMY = 1.2: sql12 1.3: sql13 1.4: sql14 + 2.0: sql20 [testenv] passenv = GITHUB_* @@ -23,8 +24,11 @@ deps = sql12: sqlalchemy>=1.2,<1.3 sql13: sqlalchemy>=1.3,<1.4 sql14: sqlalchemy>=1.4,<1.5 + sql20: sqlalchemy>=2.0.0b3,<2.1 +setenv = + SQLALCHEMY_WARN_20 = 1 commands = - pytest graphene_sqlalchemy --cov=graphene_sqlalchemy --cov-report=term --cov-report=xml {posargs} + python -W always -m pytest graphene_sqlalchemy --cov=graphene_sqlalchemy --cov-report=term --cov-report=xml {posargs} [testenv:pre-commit] basepython=python3.10 From e4b1a7f08bfd23cba56e2f456a15e0e651ba8488 Mon Sep 17 00:00:00 2001 From: Erik Wrede Date: Sun, 4 Dec 2022 21:18:59 +0100 Subject: [PATCH 02/11] update envlist for tox,reduce number of python versions --- .github/workflows/tests.yml | 2 +- tox.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d8239457..402de29f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,7 +9,7 @@ jobs: max-parallel: 10 matrix: sql-alchemy: [ "1.2", "1.3", "1.4", "2.0" ] - python-version: [ "3.7", "3.8", "3.9", "3.10" ] + python-version: ["3.9", "3.10" ] steps: - uses: actions/checkout@v3 diff --git a/tox.ini b/tox.ini index f7b5f973..1841cb1a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = pre-commit,py{37,38,39,310}-sql{12,13,14} +envlist = pre-commit,py{37,38,39,310}-sql{12,13,14,20} skipsdist = true minversion = 3.7.0 From 812b28bfdbb10a787f3528216ea4262fb704e543 Mon Sep 17 00:00:00 2001 From: Erik Wrede Date: Sun, 4 Dec 2022 21:22:33 +0100 Subject: [PATCH 03/11] fix: set sqlalchemy max version to 2.1 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9122baf2..9650e6d2 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ # To keep things simple, we only support newer versions of Graphene "graphene>=3.0.0b7", "promise>=2.3", - "SQLAlchemy>=1.1,<2", + "SQLAlchemy>=1.1,<2.1", "aiodataloader>=0.2.0,<1.0", ] From 728811776ce413beece177f8006796a5755026ca Mon Sep 17 00:00:00 2001 From: Erik Wrede Date: Mon, 2 Jan 2023 15:14:37 +0100 Subject: [PATCH 04/11] fix: all unit tests running Signed-off-by: Erik Wrede --- graphene_sqlalchemy/batching.py | 12 ++++++++++ graphene_sqlalchemy/tests/models.py | 20 ++++++++++++---- graphene_sqlalchemy/tests/models_batching.py | 13 +++++++--- graphene_sqlalchemy/tests/test_converter.py | 25 ++++++++++++++++++++ graphene_sqlalchemy/utils.py | 6 +++++ 5 files changed, 69 insertions(+), 7 deletions(-) diff --git a/graphene_sqlalchemy/batching.py b/graphene_sqlalchemy/batching.py index 23b6712e..d7feb4fb 100644 --- a/graphene_sqlalchemy/batching.py +++ b/graphene_sqlalchemy/batching.py @@ -5,6 +5,7 @@ import sqlalchemy from sqlalchemy.orm import Session, strategies from sqlalchemy.orm.query import QueryContext +from sqlalchemy.util import immutabledict from .utils import SQL_VERSION_HIGHER_EQUAL_THAN_1_4, is_graphene_version_less_than @@ -77,6 +78,17 @@ async def batch_load_fn(self, parents): else: query_context = QueryContext(session.query(parent_mapper.entity)) if SQL_VERSION_HIGHER_EQUAL_THAN_1_4: + self.selectin_loader._load_for_path( + query_context, + parent_mapper._path_registry, + states, + None, + child_mapper, + None, + None, # recursion depth can be none + immutabledict(), # default value for selectinload->lazyload + ) + elif SQL_VERSION_HIGHER_EQUAL_THAN_1_4: self.selectin_loader._load_for_path( query_context, parent_mapper._path_registry, diff --git a/graphene_sqlalchemy/tests/models.py b/graphene_sqlalchemy/tests/models.py index ee286585..a2ccd82f 100644 --- a/graphene_sqlalchemy/tests/models.py +++ b/graphene_sqlalchemy/tests/models.py @@ -22,6 +22,8 @@ from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm import backref, column_property, composite, mapper, relationship +from graphene_sqlalchemy.utils import SQL_VERSION_HIGHER_EQUAL_THAN_1_4 + PetKind = Enum("cat", "dog", name="pet_kind") @@ -116,9 +118,15 @@ def hybrid_prop_bool(self) -> bool: def hybrid_prop_list(self) -> List[int]: return [1, 2, 3] - column_prop = column_property( - select([func.cast(func.count(id), Integer)]), doc="Column property" - ) + # TODO Remove when switching min sqlalchemy version to SQLAlchemy 1.4 + if SQL_VERSION_HIGHER_EQUAL_THAN_1_4: + column_prop = column_property( + select(func.cast(func.count(id), Integer)), doc="Column property" + ) + else: + column_prop = column_property( + select([func.cast(func.count(id), Integer)]), doc="Column property" + ) composite_prop = composite( CompositeFullName, first_name, last_name, doc="Composite" @@ -161,7 +169,11 @@ def __subclasses__(cls): editor_table = Table("editors", Base.metadata, autoload=True) -mapper(ReflectedEditor, editor_table) +# TODO Remove when switching min sqlalchemy version to SQLAlchemy 1.4 +if SQL_VERSION_HIGHER_EQUAL_THAN_1_4: + Base.registry.map_imperatively(ReflectedEditor, editor_table) +else: + mapper(ReflectedEditor, editor_table) ############################################ diff --git a/graphene_sqlalchemy/tests/models_batching.py b/graphene_sqlalchemy/tests/models_batching.py index 6f1c42ff..dde6d45c 100644 --- a/graphene_sqlalchemy/tests/models_batching.py +++ b/graphene_sqlalchemy/tests/models_batching.py @@ -16,6 +16,8 @@ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import column_property, relationship +from graphene_sqlalchemy.utils import SQL_VERSION_HIGHER_EQUAL_THAN_1_4 + PetKind = Enum("cat", "dog", name="pet_kind") @@ -60,9 +62,14 @@ class Reporter(Base): articles = relationship("Article", backref="reporter") favorite_article = relationship("Article", uselist=False) - column_prop = column_property( - select([func.cast(func.count(id), Integer)]), doc="Column property" - ) + if SQL_VERSION_HIGHER_EQUAL_THAN_1_4: + column_prop = column_property( + select(func.cast(func.count(id), Integer)), doc="Column property" + ) + else: + column_prop = column_property( + select([func.cast(func.count(id), Integer)]), doc="Column property" + ) class Article(Base): diff --git a/graphene_sqlalchemy/tests/test_converter.py b/graphene_sqlalchemy/tests/test_converter.py index b9a1c152..bfd3ee66 100644 --- a/graphene_sqlalchemy/tests/test_converter.py +++ b/graphene_sqlalchemy/tests/test_converter.py @@ -24,6 +24,7 @@ from ..fields import UnsortedSQLAlchemyConnectionField, default_connection_field_factory from ..registry import Registry, get_global_registry from ..types import ORMField, SQLAlchemyObjectType +from ..utils import SQL_VERSION_HIGHER_EQUAL_THAN_1_4, is_sqlalchemy_version_less_than from .models import ( Article, CompositeFullName, @@ -336,6 +337,22 @@ class TestEnum(enum.IntEnum): assert graphene_type._meta.enum.__members__["two"].value == 2 +@pytest.mark.skipif( + not SQL_VERSION_HIGHER_EQUAL_THAN_1_4, + reason="SQLAlchemy <1.4 does not support this", +) +def test_should_columproperty_convert_sqa_20(): + field = get_field_from_column( + column_property(select(func.sum(func.cast(id, types.Integer))).where(id == 1)) + ) + + assert field.type == graphene.Int + + +@pytest.mark.skipif( + not is_sqlalchemy_version_less_than("2.0.0b1"), + reason="SQLAlchemy >=2.0 does not support this syntax, see convert_sqa_20", +) def test_should_columproperty_convert(): field = get_field_from_column( column_property(select([func.sum(func.cast(id, types.Integer))]).where(id == 1)) @@ -355,10 +372,18 @@ def test_should_jsontype_convert_jsonstring(): assert get_field(types.JSON).type == graphene.JSONString +@pytest.mark.skipif( + (not is_sqlalchemy_version_less_than("2.0.0b1")), + reason="SQLAlchemy >=2.0 does not support this: Variant is no longer used in SQLAlchemy", +) def test_should_variant_int_convert_int(): assert get_field(types.Variant(types.Integer(), {})).type == graphene.Int +@pytest.mark.skipif( + (not is_sqlalchemy_version_less_than("2.0.0b1")), + reason="SQLAlchemy >=2.0 does not support this: Variant is no longer used in SQLAlchemy", +) def test_should_variant_string_convert_string(): assert get_field(types.Variant(types.String(), {})).type == graphene.String diff --git a/graphene_sqlalchemy/utils.py b/graphene_sqlalchemy/utils.py index 62c71d8d..86ebcd79 100644 --- a/graphene_sqlalchemy/utils.py +++ b/graphene_sqlalchemy/utils.py @@ -32,6 +32,12 @@ def is_graphene_version_less_than(version_string): # pragma: no cover SQL_VERSION_HIGHER_EQUAL_THAN_1_4 = True +SQL_VERSION_HIGHER_EQUAL_THAN_2 = False + +if not is_sqlalchemy_version_less_than("2.0.0b1"): + SQL_VERSION_HIGHER_EQUAL_THAN_2 = True + + def get_session(context): return context.get("session") From e562cc2aecd84121203ef999c294322c700579ec Mon Sep 17 00:00:00 2001 From: Erik Wrede Date: Mon, 2 Jan 2023 15:19:01 +0100 Subject: [PATCH 05/11] fix: corrected sql version check for batching Signed-off-by: Erik Wrede --- graphene_sqlalchemy/batching.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/graphene_sqlalchemy/batching.py b/graphene_sqlalchemy/batching.py index d7feb4fb..87974ce8 100644 --- a/graphene_sqlalchemy/batching.py +++ b/graphene_sqlalchemy/batching.py @@ -7,7 +7,11 @@ from sqlalchemy.orm.query import QueryContext from sqlalchemy.util import immutabledict -from .utils import SQL_VERSION_HIGHER_EQUAL_THAN_1_4, is_graphene_version_less_than +from .utils import ( + SQL_VERSION_HIGHER_EQUAL_THAN_1_4, + SQL_VERSION_HIGHER_EQUAL_THAN_2, + is_graphene_version_less_than, +) def get_data_loader_impl() -> Any: # pragma: no cover @@ -77,7 +81,7 @@ async def batch_load_fn(self, parents): query_context = parent_mapper_query._compile_context() else: query_context = QueryContext(session.query(parent_mapper.entity)) - if SQL_VERSION_HIGHER_EQUAL_THAN_1_4: + if SQL_VERSION_HIGHER_EQUAL_THAN_2: self.selectin_loader._load_for_path( query_context, parent_mapper._path_registry, From e525a8db462bf229e0d5f1577520412f6fbd556e Mon Sep 17 00:00:00 2001 From: Erik Wrede Date: Mon, 2 Jan 2023 15:25:59 +0100 Subject: [PATCH 06/11] fix: added pragma no cover to version checks Signed-off-by: Erik Wrede --- graphene_sqlalchemy/batching.py | 2 +- graphene_sqlalchemy/utils.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/graphene_sqlalchemy/batching.py b/graphene_sqlalchemy/batching.py index 87974ce8..a5804516 100644 --- a/graphene_sqlalchemy/batching.py +++ b/graphene_sqlalchemy/batching.py @@ -81,7 +81,7 @@ async def batch_load_fn(self, parents): query_context = parent_mapper_query._compile_context() else: query_context = QueryContext(session.query(parent_mapper.entity)) - if SQL_VERSION_HIGHER_EQUAL_THAN_2: + if SQL_VERSION_HIGHER_EQUAL_THAN_2: # pragma: no cover self.selectin_loader._load_for_path( query_context, parent_mapper._path_registry, diff --git a/graphene_sqlalchemy/utils.py b/graphene_sqlalchemy/utils.py index 86ebcd79..381164a7 100644 --- a/graphene_sqlalchemy/utils.py +++ b/graphene_sqlalchemy/utils.py @@ -26,7 +26,7 @@ def is_graphene_version_less_than(version_string): # pragma: no cover SQL_VERSION_HIGHER_EQUAL_THAN_1_4 = False -if not is_sqlalchemy_version_less_than("1.4"): +if not is_sqlalchemy_version_less_than("1.4"): # pragma: no cover from sqlalchemy.ext.asyncio import AsyncSession SQL_VERSION_HIGHER_EQUAL_THAN_1_4 = True @@ -34,7 +34,7 @@ def is_graphene_version_less_than(version_string): # pragma: no cover SQL_VERSION_HIGHER_EQUAL_THAN_2 = False -if not is_sqlalchemy_version_less_than("2.0.0b1"): +if not is_sqlalchemy_version_less_than("2.0.0b1"): # pragma: no cover SQL_VERSION_HIGHER_EQUAL_THAN_2 = True From b84aa9fa31238e7c3bc8f74db157175ee576e5d2 Mon Sep 17 00:00:00 2001 From: Erik Wrede Date: Mon, 2 Jan 2023 15:27:17 +0100 Subject: [PATCH 07/11] chore: test with all python versions Signed-off-by: Erik Wrede --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 402de29f..ad45c81b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,8 +8,8 @@ jobs: strategy: max-parallel: 10 matrix: - sql-alchemy: [ "1.2", "1.3", "1.4", "2.0" ] - python-version: ["3.9", "3.10" ] + sql-alchemy: [ "1.2", "1.3", "1.4","2.0" ] + python-version: [ "3.7", "3.8", "3.9", "3.10" ] steps: - uses: actions/checkout@v3 From 39834eda72bfbbd42a2dc06260c08c18c5e9a339 Mon Sep 17 00:00:00 2001 From: Erik Wrede Date: Sun, 14 May 2023 15:21:23 +0200 Subject: [PATCH 08/11] chore: fix review comments --- .gitignore | 1 + graphene_sqlalchemy/tests/models.py | 14 ++--- graphene_sqlalchemy/tests/models_batching.py | 14 ++--- graphene_sqlalchemy/tests/test_converter.py | 63 +++++++------------- graphene_sqlalchemy/tests/utils.py | 13 +++- 5 files changed, 44 insertions(+), 61 deletions(-) diff --git a/.gitignore b/.gitignore index c4a735fe..47a82df0 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ __pycache__/ .Python env/ .venv/ +venv/ build/ develop-eggs/ dist/ diff --git a/graphene_sqlalchemy/tests/models.py b/graphene_sqlalchemy/tests/models.py index a2ccd82f..8349a394 100644 --- a/graphene_sqlalchemy/tests/models.py +++ b/graphene_sqlalchemy/tests/models.py @@ -16,12 +16,12 @@ String, Table, func, - select, ) from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm import backref, column_property, composite, mapper, relationship +from graphene_sqlalchemy.tests.utils import wrap_select_func from graphene_sqlalchemy.utils import SQL_VERSION_HIGHER_EQUAL_THAN_1_4 PetKind = Enum("cat", "dog", name="pet_kind") @@ -118,15 +118,9 @@ def hybrid_prop_bool(self) -> bool: def hybrid_prop_list(self) -> List[int]: return [1, 2, 3] - # TODO Remove when switching min sqlalchemy version to SQLAlchemy 1.4 - if SQL_VERSION_HIGHER_EQUAL_THAN_1_4: - column_prop = column_property( - select(func.cast(func.count(id), Integer)), doc="Column property" - ) - else: - column_prop = column_property( - select([func.cast(func.count(id), Integer)]), doc="Column property" - ) + column_prop = column_property( + wrap_select_func(func.cast(func.count(id), Integer)), doc="Column property" + ) composite_prop = composite( CompositeFullName, first_name, last_name, doc="Composite" diff --git a/graphene_sqlalchemy/tests/models_batching.py b/graphene_sqlalchemy/tests/models_batching.py index dde6d45c..5dde366f 100644 --- a/graphene_sqlalchemy/tests/models_batching.py +++ b/graphene_sqlalchemy/tests/models_batching.py @@ -11,12 +11,11 @@ String, Table, func, - select, ) from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import column_property, relationship -from graphene_sqlalchemy.utils import SQL_VERSION_HIGHER_EQUAL_THAN_1_4 +from graphene_sqlalchemy.tests.utils import wrap_select_func PetKind = Enum("cat", "dog", name="pet_kind") @@ -62,14 +61,9 @@ class Reporter(Base): articles = relationship("Article", backref="reporter") favorite_article = relationship("Article", uselist=False) - if SQL_VERSION_HIGHER_EQUAL_THAN_1_4: - column_prop = column_property( - select(func.cast(func.count(id), Integer)), doc="Column property" - ) - else: - column_prop = column_property( - select([func.cast(func.count(id), Integer)]), doc="Column property" - ) + column_prop = column_property( + wrap_select_func(func.cast(func.count(id), Integer)), doc="Column property" + ) class Article(Base): diff --git a/graphene_sqlalchemy/tests/test_converter.py b/graphene_sqlalchemy/tests/test_converter.py index bfd3ee66..1a5e0093 100644 --- a/graphene_sqlalchemy/tests/test_converter.py +++ b/graphene_sqlalchemy/tests/test_converter.py @@ -2,19 +2,26 @@ import sys from typing import Dict, Union +import graphene import pytest import sqlalchemy_utils as sqa_utils -from sqlalchemy import Column, func, select, types +from graphene.relay import Node +from graphene.types.structures import Structure +from sqlalchemy import Column, func, types from sqlalchemy.dialects import postgresql from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.inspection import inspect from sqlalchemy.orm import column_property, composite -import graphene -from graphene.relay import Node -from graphene.types.structures import Structure - +from .models import ( + Article, + CompositeFullName, + Pet, + Reporter, + ShoppingCart, + ShoppingCartItem, +) from ..converter import ( convert_sqlalchemy_column, convert_sqlalchemy_composite, @@ -24,15 +31,7 @@ from ..fields import UnsortedSQLAlchemyConnectionField, default_connection_field_factory from ..registry import Registry, get_global_registry from ..types import ORMField, SQLAlchemyObjectType -from ..utils import SQL_VERSION_HIGHER_EQUAL_THAN_1_4, is_sqlalchemy_version_less_than -from .models import ( - Article, - CompositeFullName, - Pet, - Reporter, - ShoppingCart, - ShoppingCartItem, -) +from ..utils import is_sqlalchemy_version_less_than def mock_resolver(): @@ -88,9 +87,9 @@ def prop_method() -> int | str: return "not allowed in gql schema" with pytest.raises( - ValueError, - match=r"Cannot convert hybrid_property Union to " - r"graphene.Union: the Union contains scalars. \.*", + ValueError, + match=r"Cannot convert hybrid_property Union to " + r"graphene.Union: the Union contains scalars. \.*", ): get_hybrid_property_type(prop_method) @@ -337,25 +336,9 @@ class TestEnum(enum.IntEnum): assert graphene_type._meta.enum.__members__["two"].value == 2 -@pytest.mark.skipif( - not SQL_VERSION_HIGHER_EQUAL_THAN_1_4, - reason="SQLAlchemy <1.4 does not support this", -) -def test_should_columproperty_convert_sqa_20(): - field = get_field_from_column( - column_property(select(func.sum(func.cast(id, types.Integer))).where(id == 1)) - ) - - assert field.type == graphene.Int - - -@pytest.mark.skipif( - not is_sqlalchemy_version_less_than("2.0.0b1"), - reason="SQLAlchemy >=2.0 does not support this syntax, see convert_sqa_20", -) def test_should_columproperty_convert(): field = get_field_from_column( - column_property(select([func.sum(func.cast(id, types.Integer))]).where(id == 1)) + column_property(wrap_select_func(func.sum(func.cast(id, types.Integer))).where(id == 1)) ) assert field.type == graphene.Int @@ -654,8 +637,8 @@ class Meta: ) for ( - hybrid_prop_name, - hybrid_prop_expected_return_type, + hybrid_prop_name, + hybrid_prop_expected_return_type, ) in shopping_cart_item_expected_types.items(): hybrid_prop_field = ShoppingCartItemType._meta.fields[hybrid_prop_name] @@ -666,7 +649,7 @@ class Meta: str(hybrid_prop_expected_return_type), ) assert ( - hybrid_prop_field.description is None + hybrid_prop_field.description is None ) # "doc" is ignored by hybrid property ################################################### @@ -714,8 +697,8 @@ class Meta: ) for ( - hybrid_prop_name, - hybrid_prop_expected_return_type, + hybrid_prop_name, + hybrid_prop_expected_return_type, ) in shopping_cart_expected_types.items(): hybrid_prop_field = ShoppingCartType._meta.fields[hybrid_prop_name] @@ -726,5 +709,5 @@ class Meta: str(hybrid_prop_expected_return_type), ) assert ( - hybrid_prop_field.description is None + hybrid_prop_field.description is None ) # "doc" is ignored by hybrid property diff --git a/graphene_sqlalchemy/tests/utils.py b/graphene_sqlalchemy/tests/utils.py index 4a118243..6e843316 100644 --- a/graphene_sqlalchemy/tests/utils.py +++ b/graphene_sqlalchemy/tests/utils.py @@ -1,6 +1,10 @@ import inspect import re +from sqlalchemy import select + +from graphene_sqlalchemy.utils import SQL_VERSION_HIGHER_EQUAL_THAN_1_4 + def to_std_dicts(value): """Convert nested ordered dicts to normal dicts for better comparison.""" @@ -18,8 +22,15 @@ def remove_cache_miss_stat(message): return re.sub(r"\[generated in \d+.?\d*s\]\s", "", message) -async def eventually_await_session(session, func, *args): +def wrap_select_func(query): + # TODO remove this when we drop support for sqa < 2.0 + if SQL_VERSION_HIGHER_EQUAL_THAN_1_4: + return select(query) + else: + return select([query]) + +async def eventually_await_session(session, func, *args): if inspect.iscoroutinefunction(getattr(session, func)): await getattr(session, func)(*args) else: From a9915d6af26ab8e6f0e74444db8c0cec7a8c6f05 Mon Sep 17 00:00:00 2001 From: Erik Wrede Date: Sun, 14 May 2023 19:36:44 +0200 Subject: [PATCH 09/11] chore: update dependencies and fix test --- graphene_sqlalchemy/tests/test_converter.py | 1 + setup.py | 2 +- tox.ini | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/graphene_sqlalchemy/tests/test_converter.py b/graphene_sqlalchemy/tests/test_converter.py index 1a5e0093..4666d9a2 100644 --- a/graphene_sqlalchemy/tests/test_converter.py +++ b/graphene_sqlalchemy/tests/test_converter.py @@ -22,6 +22,7 @@ ShoppingCart, ShoppingCartItem, ) +from .utils import wrap_select_func from ..converter import ( convert_sqlalchemy_column, convert_sqlalchemy_composite, diff --git a/setup.py b/setup.py index 9650e6d2..0e828caa 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ # To keep things simple, we only support newer versions of Graphene "graphene>=3.0.0b7", "promise>=2.3", - "SQLAlchemy>=1.1,<2.1", + "SQLAlchemy>=1.1", "aiodataloader>=0.2.0,<1.0", ] diff --git a/tox.ini b/tox.ini index 1841cb1a..9ce901e4 100644 --- a/tox.ini +++ b/tox.ini @@ -24,7 +24,7 @@ deps = sql12: sqlalchemy>=1.2,<1.3 sql13: sqlalchemy>=1.3,<1.4 sql14: sqlalchemy>=1.4,<1.5 - sql20: sqlalchemy>=2.0.0b3,<2.1 + sql20: sqlalchemy>=2.0.0b3 setenv = SQLALCHEMY_WARN_20 = 1 commands = From 4712e10e5f16f04d1c780e6e14dfccc7a26798a7 Mon Sep 17 00:00:00 2001 From: Erik Wrede Date: Sun, 14 May 2023 19:55:21 +0200 Subject: [PATCH 10/11] chore: update sqa-utils fix --- graphene_sqlalchemy/tests/conftest.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/graphene_sqlalchemy/tests/conftest.py b/graphene_sqlalchemy/tests/conftest.py index d3fcedc9..89b357a4 100644 --- a/graphene_sqlalchemy/tests/conftest.py +++ b/graphene_sqlalchemy/tests/conftest.py @@ -3,13 +3,6 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker -# fmt: off -# Fixme remove when https://github.com/kvesteri/sqlalchemy-utils/pull/644 is released #noqa -import sqlalchemy # noqa # isort:skip -if sqlalchemy.__version__ == "2.0.0b3": # noqa # isort:skip - sqlalchemy.__version__ = "2.0.0" # noqa # isort:skip -# fmt: on - import graphene from graphene_sqlalchemy.utils import SQL_VERSION_HIGHER_EQUAL_THAN_1_4 From a0abf8a00f09e7cdf3538c8e0bef7a3d281bc036 Mon Sep 17 00:00:00 2001 From: Erik Wrede Date: Sun, 14 May 2023 21:41:45 +0200 Subject: [PATCH 11/11] fix: adjust test after sqlalchemy 2.0 update --- graphene_sqlalchemy/tests/models.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/graphene_sqlalchemy/tests/models.py b/graphene_sqlalchemy/tests/models.py index 68c96bf6..b638b5d4 100644 --- a/graphene_sqlalchemy/tests/models.py +++ b/graphene_sqlalchemy/tests/models.py @@ -20,11 +20,18 @@ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm import backref, column_property, composite, mapper, relationship -from sqlalchemy.sql.sqltypes import _LookupExpressionAdapter from sqlalchemy.sql.type_api import TypeEngine from graphene_sqlalchemy.tests.utils import wrap_select_func -from graphene_sqlalchemy.utils import SQL_VERSION_HIGHER_EQUAL_THAN_1_4 +from graphene_sqlalchemy.utils import SQL_VERSION_HIGHER_EQUAL_THAN_1_4, SQL_VERSION_HIGHER_EQUAL_THAN_2 + +# fmt: off +import sqlalchemy +if SQL_VERSION_HIGHER_EQUAL_THAN_2: + from sqlalchemy.sql.sqltypes import HasExpressionLookup # noqa # isort:skip +else: + from sqlalchemy.sql.sqltypes import _LookupExpressionAdapter as HasExpressionLookup # noqa # isort:skip +# fmt: on PetKind = Enum("cat", "dog", name="pet_kind") @@ -343,7 +350,7 @@ class Employee(Person): ############################################ -class CustomIntegerColumn(_LookupExpressionAdapter, TypeEngine): +class CustomIntegerColumn(HasExpressionLookup, TypeEngine): """ Custom Column Type that our converters don't recognize Adapted from sqlalchemy.Integer