From 19a32951d4509b9e4b3ee90c9eb3fb0d975b4e92 Mon Sep 17 00:00:00 2001 From: Kurt McKee Date: Thu, 5 Sep 2024 18:29:51 -0500 Subject: [PATCH] Drop support for sqlalchemy 1.3 --- .github/workflows/test.yaml | 4 -- CHANGES.rst | 1 + conftest.py | 20 ++---- setup.py | 2 +- sqlalchemy_utils/aggregates.py | 5 +- sqlalchemy_utils/compat.py | 69 ------------------- sqlalchemy_utils/generic.py | 6 +- sqlalchemy_utils/listeners.py | 2 - sqlalchemy_utils/relationships/__init__.py | 3 +- .../types/encrypted/encrypted_type.py | 6 +- sqlalchemy_utils/view.py | 2 - tests/functions/test_cast_if.py | 9 +-- tests/functions/test_get_type.py | 3 +- tests/functions/test_json_sql.py | 5 +- tests/functions/test_jsonb_sql.py | 5 +- .../test_make_order_by_deterministic.py | 3 +- tests/functions/test_render.py | 3 +- .../test_select_correlated_expression.py | 18 +++-- tests/test_compat.py | 20 ------ tests/test_expressions.py | 6 +- tests/test_views.py | 29 ++++---- tests/types/test_arrow.py | 3 +- tests/types/test_choice.py | 3 +- tests/types/test_color.py | 3 +- tests/types/test_composite.py | 7 +- tests/types/test_country.py | 3 +- tests/types/test_currency.py | 3 +- tests/types/test_date_range.py | 3 +- tests/types/test_datetime_range.py | 3 +- tests/types/test_email.py | 3 +- tests/types/test_encrypted.py | 18 +---- tests/types/test_enriched_date_pendulum.py | 3 +- tests/types/test_enriched_datetime_arrow.py | 3 +- tests/types/test_int_range.py | 5 +- tests/types/test_ip_address.py | 3 +- tests/types/test_json.py | 3 +- tests/types/test_locale.py | 3 +- tests/types/test_ltree.py | 3 +- tests/types/test_numeric_range.py | 3 +- tests/types/test_password.py | 27 ++------ tests/types/test_phonenumber.py | 3 +- tests/types/test_scalar_list.py | 3 +- tests/types/test_timezone.py | 3 +- tests/types/test_url.py | 3 +- tests/types/test_uuid.py | 3 +- tests/types/test_weekdays.py | 3 +- tox.ini | 3 +- 47 files changed, 77 insertions(+), 267 deletions(-) delete mode 100644 tests/test_compat.py diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index ef0d20c3..c5e83e02 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -13,10 +13,6 @@ jobs: - '3.12' tox_env: ['sqlalchemy14', 'sqlalchemy2'] include: - # Test against Python 3.9 and sqlalchemy 1.3 - - os: 'ubuntu-20.04' - python-version: '3.9' - tox_env: 'sqlalchemy13' # Test against Python 3.9 and sqlalchemy 1.4 - os: 'ubuntu-20.04' python-version: '3.9' diff --git a/CHANGES.rst b/CHANGES.rst index ab2eb19d..181fcf1c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,6 +7,7 @@ Unreleased changes ^^^^^^^^^^^^^^^^^^ - Drop support for Python 3.7 and 3.8. +- Drop support for sqlalchemy 1.3. - Add support for Python 3.12. - Add a Read the Docs configuration file. - Make documentation builds reproducible. diff --git a/conftest.py b/conftest.py index 76642ee1..57c2e6e3 100644 --- a/conftest.py +++ b/conftest.py @@ -7,7 +7,7 @@ import sqlalchemy.exc from sqlalchemy import create_engine from sqlalchemy.ext.hybrid import hybrid_property -from sqlalchemy.orm import sessionmaker +from sqlalchemy.orm import declarative_base, sessionmaker, synonym_for from sqlalchemy.orm.session import close_all_sessions from sqlalchemy_utils import ( @@ -16,11 +16,6 @@ i18n, InstrumentedList ) -from sqlalchemy_utils.compat import ( - _declarative_base, - _select_args, - _synonym_for -) from sqlalchemy_utils.functions.orm import _get_class_registry from sqlalchemy_utils.types.pg_composite import remove_composite_listeners @@ -154,7 +149,7 @@ def connection(engine): @pytest.fixture def Base(): - return _declarative_base() + return declarative_base() @pytest.fixture @@ -191,7 +186,7 @@ def articles_count(self): def articles_count(cls): Article = _get_class_registry(Base)['Article'] return ( - sa.select(*_select_args(sa.func.count(Article.id))) + sa.select(sa.func.count(Article.id)) .where(Article.category_id == cls.id) .correlate(Article.__table__) .label('article_count') @@ -201,7 +196,7 @@ def articles_count(cls): def name_alias(self): return self.name - @_synonym_for('name') + @synonym_for('name') @property def name_synonym(self): return self.name @@ -238,12 +233,7 @@ def session(request, engine, connection, Base, init_models): with connection.begin(): Base.metadata.create_all(connection) Session = sessionmaker(bind=connection) - try: - # Enable sqlalchemy 2.0 behavior. - session = Session(future=True) - except TypeError: - # sqlalchemy 1.3 - session = Session() + session = Session(future=True) i18n.get_locale = get_locale def teardown(): diff --git a/setup.py b/setup.py index a1d44ad2..5ab09c49 100644 --- a/setup.py +++ b/setup.py @@ -75,7 +75,7 @@ def get_version(): include_package_data=True, platforms='any', install_requires=[ - 'SQLAlchemy>=1.3', + 'SQLAlchemy>=1.4', ], extras_require=extras_require, python_requires='>=3.9', diff --git a/sqlalchemy_utils/aggregates.py b/sqlalchemy_utils/aggregates.py index 9005e108..7d4a6b8e 100644 --- a/sqlalchemy_utils/aggregates.py +++ b/sqlalchemy_utils/aggregates.py @@ -371,7 +371,6 @@ class Rating(Base): from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.sql.functions import _FunctionGenerator -from .compat import _select_args, get_scalar_subquery from .functions.orm import get_column_key from .relationships import ( chained_join, @@ -455,7 +454,7 @@ def aggregate_query(self): self.relationships[0].mapper.class_ ) - return get_scalar_subquery(query) + return query.scalar_subquery() def update_query(self, objects): table = self.class_.__table__ @@ -490,7 +489,7 @@ def update_query(self, objects): return query.where( local.in_( sa.select( - *_select_args(remote) + remote ).select_from( chained_join(*reversed(self.relationships)) ).where( diff --git a/sqlalchemy_utils/compat.py b/sqlalchemy_utils/compat.py index 16cded7e..27eed17a 100644 --- a/sqlalchemy_utils/compat.py +++ b/sqlalchemy_utils/compat.py @@ -10,72 +10,3 @@ def get_sqlalchemy_version(version=metadata("sqlalchemy")["Version"]): return tuple(int(v) for v in match.groups() if v is not None) except AttributeError: return () - - -_sqlalchemy_version = get_sqlalchemy_version() - - -# In sqlalchemy 2.0, some functions moved to sqlalchemy.orm. -# In sqlalchemy 1.3, they are only available in .ext.declarative. -# In sqlalchemy 1.4, they are available in both places. -# -# WARNING -# ------- -# -# These imports are for internal, private compatibility. -# They are not supported and may change or move at any time. -# Do not import these in your own code. -# - -if _sqlalchemy_version >= (1, 4): - from sqlalchemy.orm import declarative_base as _declarative_base - from sqlalchemy.orm import synonym_for as _synonym_for -else: - from sqlalchemy.ext.declarative import \ - declarative_base as _declarative_base - from sqlalchemy.ext.declarative import synonym_for as _synonym_for - - -# scalar subqueries -if _sqlalchemy_version >= (1, 4): - def get_scalar_subquery(query): - return query.scalar_subquery() -else: - def get_scalar_subquery(query): - return query.as_scalar() - - -# In sqlalchemy 2.0, select() columns are positional. -# In sqlalchemy 1.3, select() columns must be wrapped in a list. -# -# _select_args() is designed so its return value can be unpacked: -# -# select(*_select_args(1, 2)) -# -# When sqlalchemy 1.3 support is dropped, remove the call to _select_args() -# and keep the arguments the same: -# -# select(1, 2) -# -# WARNING -# ------- -# -# _select_args() is a private, internal function. -# It is not supported and may change or move at any time. -# Do not import this in your own code. -# -if _sqlalchemy_version >= (1, 4): - def _select_args(*args): - return args -else: - def _select_args(*args): - return [args] - - -__all__ = ( - "_declarative_base", - "get_scalar_subquery", - "get_sqlalchemy_version", - "_select_args", - "_synonym_for", -) diff --git a/sqlalchemy_utils/generic.py b/sqlalchemy_utils/generic.py index a9ec5f3c..bf0a6353 100644 --- a/sqlalchemy_utils/generic.py +++ b/sqlalchemy_utils/generic.py @@ -53,11 +53,7 @@ def get(self, state, dict_, passive=attributes.PASSIVE_OFF): id = self.get_state_id(state) - try: - target = session.get(target_class, id) - except AttributeError: - # sqlalchemy 1.3 - target = session.query(target_class).get(id) + target = session.get(target_class, id) # Return found (or not found) target. return target diff --git a/sqlalchemy_utils/listeners.py b/sqlalchemy_utils/listeners.py index 02e21be0..7b96374d 100644 --- a/sqlalchemy_utils/listeners.py +++ b/sqlalchemy_utils/listeners.py @@ -143,8 +143,6 @@ def auto_delete_orphans(attr): from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy import * from sqlalchemy.orm import * - # Necessary in sqlalchemy 1.3: - # from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import event diff --git a/sqlalchemy_utils/relationships/__init__.py b/sqlalchemy_utils/relationships/__init__.py index 31e42fd3..fb8a0f92 100644 --- a/sqlalchemy_utils/relationships/__init__.py +++ b/sqlalchemy_utils/relationships/__init__.py @@ -2,7 +2,6 @@ import sqlalchemy.orm from sqlalchemy.sql.util import ClauseAdapter -from ..compat import _select_args from .chained_join import chained_join # noqa @@ -96,7 +95,7 @@ def select_correlated_expression( ): relationships = list(reversed(path_to_relationships(path, root_model))) - query = sa.select(*_select_args(expr)) + query = sa.select(expr) join_expr, aliases = chained_inverse_join(relationships, leaf_model) diff --git a/sqlalchemy_utils/types/encrypted/encrypted_type.py b/sqlalchemy_utils/types/encrypted/encrypted_type.py index 173bcabc..28a45849 100644 --- a/sqlalchemy_utils/types/encrypted/encrypted_type.py +++ b/sqlalchemy_utils/types/encrypted/encrypted_type.py @@ -257,11 +257,7 @@ class StringEncryptedType(TypeDecorator, ScalarCoercible): import sqlalchemy as sa from sqlalchemy import create_engine - try: - from sqlalchemy.orm import declarative_base - except ImportError: - # sqlalchemy 1.3 - from sqlalchemy.ext.declarative import declarative_base + from sqlalchemy.orm import declarative_base from sqlalchemy.orm import sessionmaker from sqlalchemy_utils import StringEncryptedType diff --git a/sqlalchemy_utils/view.py b/sqlalchemy_utils/view.py index fae06bfc..64f35841 100644 --- a/sqlalchemy_utils/view.py +++ b/sqlalchemy_utils/view.py @@ -160,8 +160,6 @@ def create_view( ) premium_members = select(users).where(users.c.premium_user == True) - # sqlalchemy 1.3: - # premium_members = select([users]).where(users.c.premium_user == True) create_view('premium_users', premium_members, metadata) metadata.create_all(engine) # View is created at this point diff --git a/tests/functions/test_cast_if.py b/tests/functions/test_cast_if.py index 5a5953af..d95ec845 100644 --- a/tests/functions/test_cast_if.py +++ b/tests/functions/test_cast_if.py @@ -3,16 +3,11 @@ import sqlalchemy.orm from sqlalchemy_utils import cast_if -from sqlalchemy_utils.compat import ( - _declarative_base, - _select_args, - get_scalar_subquery -) @pytest.fixture(scope='class') def base(): - return _declarative_base() + return sqlalchemy.orm.declarative_base() @pytest.fixture(scope='class') @@ -44,7 +39,7 @@ def test_synonym(self, article_cls): assert cast_if(expr, sa.String) is expr def test_scalar_selectable(self, article_cls): - expr = get_scalar_subquery(sa.select(*_select_args(article_cls.id))) + expr = sa.select(article_cls.id).scalar_subquery() assert cast_if(expr, sa.Integer) is expr def test_scalar(self): diff --git a/tests/functions/test_get_type.py b/tests/functions/test_get_type.py index 11c5ce2b..7340d469 100644 --- a/tests/functions/test_get_type.py +++ b/tests/functions/test_get_type.py @@ -3,7 +3,6 @@ import sqlalchemy.orm from sqlalchemy_utils import get_type -from sqlalchemy_utils.compat import _select_args, get_scalar_subquery @pytest.fixture @@ -47,5 +46,5 @@ def test_relationship_property(self, Article, User): assert get_type(Article.author) == User def test_scalar_select(self, Article): - query = get_scalar_subquery(sa.select(*_select_args(Article.id))) + query = sa.select(Article.id).scalar_subquery() assert isinstance(get_type(query), sa.Integer) diff --git a/tests/functions/test_json_sql.py b/tests/functions/test_json_sql.py index ab98846b..cf3e4e07 100644 --- a/tests/functions/test_json_sql.py +++ b/tests/functions/test_json_sql.py @@ -2,7 +2,6 @@ import sqlalchemy as sa from sqlalchemy_utils import json_sql -from sqlalchemy_utils.compat import _select_args @pytest.mark.usefixtures('postgresql_dsn') @@ -22,12 +21,12 @@ class TestJSONSQL: ([1, 2], [1, 2]), ([], []), ( - [sa.select(*_select_args(sa.text('1'))).label('alias')], + [sa.select(sa.text('1')).label('alias')], [1] ) ) ) def test_compiled_scalars(self, connection, value, result): assert result == ( - connection.execute(sa.select(*_select_args(json_sql(value)))).fetchone()[0] + connection.execute(sa.select(json_sql(value))).fetchone()[0] ) diff --git a/tests/functions/test_jsonb_sql.py b/tests/functions/test_jsonb_sql.py index d561b6aa..842578d2 100644 --- a/tests/functions/test_jsonb_sql.py +++ b/tests/functions/test_jsonb_sql.py @@ -2,7 +2,6 @@ import sqlalchemy as sa from sqlalchemy_utils import jsonb_sql -from sqlalchemy_utils.compat import _select_args @pytest.mark.usefixtures('postgresql_dsn') @@ -22,12 +21,12 @@ class TestJSONBSQL: ([1, 2], [1, 2]), ([], []), ( - [sa.select(*_select_args(sa.text('1'))).label('alias')], + [sa.select(sa.text('1')).label('alias')], [1] ) ) ) def test_compiled_scalars(self, connection, value, result): assert result == ( - connection.execute(sa.select(*_select_args(jsonb_sql(value)))).fetchone()[0] + connection.execute(sa.select(jsonb_sql(value))).fetchone()[0] ) diff --git a/tests/functions/test_make_order_by_deterministic.py b/tests/functions/test_make_order_by_deterministic.py index 13552565..4a950413 100644 --- a/tests/functions/test_make_order_by_deterministic.py +++ b/tests/functions/test_make_order_by_deterministic.py @@ -2,7 +2,6 @@ import sqlalchemy as sa import sqlalchemy.orm -from sqlalchemy_utils.compat import _select_args from sqlalchemy_utils.functions.sort_query import make_order_by_deterministic from .. import assert_contains @@ -31,7 +30,7 @@ class User(Base): ) User.article_count = sa.orm.column_property( - sa.select(*_select_args(sa.func.count())) + sa.select(sa.func.count()) .select_from(Article) .where(Article.author_id == User.id) .label('article_count') diff --git a/tests/functions/test_render.py b/tests/functions/test_render.py index 1849a005..23bd69da 100644 --- a/tests/functions/test_render.py +++ b/tests/functions/test_render.py @@ -1,7 +1,6 @@ import pytest import sqlalchemy as sa -from sqlalchemy_utils.compat import _select_args from sqlalchemy_utils.functions import ( mock_engine, render_expression, @@ -40,7 +39,7 @@ def test_render_statement(self, session, User): assert 'WHERE user.id = 3' in text def test_render_statement_without_mapper(self, session): - statement = sa.select(*_select_args(sa.text('1'))) + statement = sa.select(sa.text('1')) text = render_statement(statement, bind=session.bind) assert 'SELECT 1' in text diff --git a/tests/relationships/test_select_correlated_expression.py b/tests/relationships/test_select_correlated_expression.py index e363c466..2a97ef5e 100644 --- a/tests/relationships/test_select_correlated_expression.py +++ b/tests/relationships/test_select_correlated_expression.py @@ -3,7 +3,6 @@ import sqlalchemy.orm from sqlalchemy.ext.hybrid import hybrid_property -from sqlalchemy_utils.compat import _select_args, get_scalar_subquery from sqlalchemy_utils.relationships import select_correlated_expression @@ -68,14 +67,14 @@ class User(Base): ) friendship_union = ( - sa.select(*_select_args( + sa.select( friendship_tbl.c.friend_a_id, friendship_tbl.c.friend_b_id, - )).union( - sa.select(*_select_args( + ).union( + sa.select( friendship_tbl.c.friend_b_id, friendship_tbl.c.friend_a_id, - )) + ) ).alias() ) @@ -157,11 +156,10 @@ class Comment(Base): author = sa.orm.relationship(User, backref='comments') Article.comment_count = sa.orm.column_property( - get_scalar_subquery( - sa.select(*_select_args(sa.func.count(Comment.id))) - .where(Comment.article_id == Article.id) - .correlate_except(Article) - ) + sa.select(sa.func.count(Comment.id)) + .where(Comment.article_id == Article.id) + .correlate_except(Article) + .scalar_subquery() ) return Comment diff --git a/tests/test_compat.py b/tests/test_compat.py deleted file mode 100644 index 86b92d85..00000000 --- a/tests/test_compat.py +++ /dev/null @@ -1,20 +0,0 @@ -import pytest - -import sqlalchemy_utils.compat - - -@pytest.mark.parametrize( - "version, expected", - ( - ("2.0.0b3", (2, 0, 0)), - ("2.0.0b", (2, 0, 0)), - ("2.0.0", (2, 0, 0)), - ("2.0.", (2, 0)), - ("2.0", (2, 0)), - ("2.", (2,)), - ("2", (2,)), - ("", ()), - ), -) -def test_get_sqlalchemy_version(version, expected): - assert sqlalchemy_utils.compat.get_sqlalchemy_version(version) == expected diff --git a/tests/test_expressions.py b/tests/test_expressions.py index 41f71648..c86376d6 100644 --- a/tests/test_expressions.py +++ b/tests/test_expressions.py @@ -1,9 +1,9 @@ import pytest import sqlalchemy as sa from sqlalchemy.dialects import postgresql +from sqlalchemy.orm import declarative_base from sqlalchemy_utils import Asterisk, row_to_json -from sqlalchemy_utils.compat import _declarative_base @pytest.fixture @@ -29,7 +29,7 @@ class Article(Base): class TestAsterisk: def test_with_table_object(self): - Base = _declarative_base() + Base = declarative_base() class Article(Base): __tablename__ = 'article' @@ -38,7 +38,7 @@ class Article(Base): assert str(Asterisk(Article.__table__)) == 'article.*' def test_with_quoted_identifier(self): - Base = _declarative_base() + Base = declarative_base() class User(Base): __tablename__ = 'user' diff --git a/tests/test_views.py b/tests/test_views.py index 2ec3ef96..54899a65 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -7,7 +7,6 @@ create_view, refresh_materialized_view ) -from sqlalchemy_utils.compat import _select_args from sqlalchemy_utils.view import CreateView @@ -37,12 +36,10 @@ class ArticleMV(Base): __table__ = create_materialized_view( name='article-mv', selectable=sa.select( - *_select_args( - Article.id, - Article.name, - User.id.label('author_id'), - User.name.label('author_name'), - ) + Article.id, + Article.name, + User.id.label('author_id'), + User.name.label('author_name'), ).select_from( Article.__table__.join(User, Article.author_id == User.id) ), @@ -59,12 +56,10 @@ class ArticleView(Base): __table__ = create_view( name='article-view', selectable=sa.select( - *_select_args( - Article.id, - Article.name, - User.id.label('author_id'), - User.name.label('author_name'), - ) + Article.id, + Article.name, + User.id.label('author_id'), + User.name.label('author_name'), ).select_from( Article.__table__.join(User, Article.author_id == User.id) ), @@ -127,7 +122,7 @@ def life_cycle( ): create_view( name='trivial_view', - selectable=sa.select(*_select_args(column)), + selectable=sa.select(column), metadata=metadata, cascade_on_drop=cascade_on_drop, replace=replace, @@ -192,13 +187,13 @@ def test_life_cycle_replace_existing( ): create_view( name='trivial_view', - selectable=sa.select(*_select_args(User.id)), + selectable=sa.select(User.id), metadata=Base.metadata, ) Base.metadata.create_all(engine) view = CreateView( name='trivial_view', - selectable=sa.select(*_select_args(User.id)), + selectable=sa.select(User.id), replace=True, ) with connection.begin(): @@ -215,7 +210,7 @@ def test_replace_materialized( with pytest.raises(ValueError): CreateView( name='trivial_view', - selectable=sa.select(*_select_args(User.id)), + selectable=sa.select(User.id), materialized=True, replace=True, ) diff --git a/tests/types/test_arrow.py b/tests/types/test_arrow.py index 7da16c70..32911c67 100644 --- a/tests/types/test_arrow.py +++ b/tests/types/test_arrow.py @@ -4,7 +4,6 @@ import sqlalchemy as sa from dateutil import tz -from sqlalchemy_utils.compat import _select_args from sqlalchemy_utils.types import arrow @@ -80,6 +79,6 @@ def test_timezone(self, session, Article): assert item.published_at.to(timezone) == dt def test_compilation(self, Article, session): - query = sa.select(*_select_args(Article.created_at)) + query = sa.select(Article.created_at) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_choice.py b/tests/types/test_choice.py index 7a4c3cd7..5044d9ff 100644 --- a/tests/types/test_choice.py +++ b/tests/types/test_choice.py @@ -3,7 +3,6 @@ from flexmock import flexmock from sqlalchemy_utils import Choice, ChoiceType, ImproperlyConfigured -from sqlalchemy_utils.compat import _select_args from sqlalchemy_utils.types.choice import Enum @@ -82,7 +81,7 @@ def test_throws_exception_if_no_choices_given(self): ChoiceType([]) def test_compilation(self, User, session): - query = sa.select(*_select_args(User.type)) + query = sa.select(User.type) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_color.py b/tests/types/test_color.py index 06443286..09428719 100644 --- a/tests/types/test_color.py +++ b/tests/types/test_color.py @@ -3,7 +3,6 @@ from flexmock import flexmock from sqlalchemy_utils import ColorType, types # noqa -from sqlalchemy_utils.compat import _select_args @pytest.fixture @@ -63,6 +62,6 @@ def test_literal_param(self, session, Document): assert compiled == "document.bg_color = 'white'" def test_compilation(self, Document, session): - query = sa.select(*_select_args(Document.bg_color)) + query = sa.select(Document.bg_color) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_composite.py b/tests/types/test_composite.py index 3ea9af42..b9eea450 100644 --- a/tests/types/test_composite.py +++ b/tests/types/test_composite.py @@ -436,12 +436,7 @@ def session(self, request, engine, connection, Base, Account): sa.orm.configure_mappers() Session = sessionmaker(bind=connection) - try: - # Enable sqlalchemy 2.0 behavior - session = Session(future=True) - except TypeError: - # sqlalchemy 1.3 - session = Session() + session = Session(future=True) session.execute( sa.text('CREATE TYPE "MoneyType" AS (currency VARCHAR, amount INTEGER)') ) diff --git a/tests/types/test_country.py b/tests/types/test_country.py index c6a51aea..3914be8d 100644 --- a/tests/types/test_country.py +++ b/tests/types/test_country.py @@ -2,7 +2,6 @@ import sqlalchemy as sa from sqlalchemy_utils import Country, CountryType, i18n # noqa -from sqlalchemy_utils.compat import _select_args @pytest.fixture @@ -46,6 +45,6 @@ def test_literal_param(self, session, User): assert compiled == '"user".country = \'FI\'' def test_compilation(self, User, session): - query = sa.select(*_select_args(User.country)) + query = sa.select(User.country) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_currency.py b/tests/types/test_currency.py index 8b20d61b..c8c525c3 100644 --- a/tests/types/test_currency.py +++ b/tests/types/test_currency.py @@ -2,7 +2,6 @@ import sqlalchemy as sa from sqlalchemy_utils import Currency, CurrencyType, i18n -from sqlalchemy_utils.compat import _select_args @pytest.fixture @@ -55,6 +54,6 @@ def test_literal_param(self, session, User): assert compiled == '"user".currency = \'USD\'' def test_compilation(self, User, session): - query = sa.select(*_select_args(User.currency)) + query = sa.select(User.currency) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_date_range.py b/tests/types/test_date_range.py index 61e2bf56..809eefa9 100644 --- a/tests/types/test_date_range.py +++ b/tests/types/test_date_range.py @@ -4,7 +4,6 @@ import sqlalchemy as sa from sqlalchemy_utils import DateRangeType -from sqlalchemy_utils.compat import _select_args intervals = None inf = 0 @@ -84,7 +83,7 @@ def test_integer_coercion(self, Booking): assert booking.during.upper == datetime(2015, 1, 1).date() def test_compilation(self, session, Booking): - query = sa.select(*_select_args(Booking.during)) + query = sa.select(Booking.during) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_datetime_range.py b/tests/types/test_datetime_range.py index d622f356..d46d0f3d 100644 --- a/tests/types/test_datetime_range.py +++ b/tests/types/test_datetime_range.py @@ -4,7 +4,6 @@ import sqlalchemy as sa from sqlalchemy_utils import DateTimeRangeType -from sqlalchemy_utils.compat import _select_args intervals = None inf = 0 @@ -84,7 +83,7 @@ def test_integer_coercion(self, Booking): assert booking.during.upper == datetime(2015, 1, 1) def test_compilation(self, session, Booking): - query = sa.select(*_select_args(Booking.during)) + query = sa.select(Booking.during) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_email.py b/tests/types/test_email.py index 727fd6fe..78599315 100644 --- a/tests/types/test_email.py +++ b/tests/types/test_email.py @@ -2,7 +2,6 @@ import sqlalchemy as sa from sqlalchemy_utils import EmailType -from sqlalchemy_utils.compat import _select_args @pytest.fixture @@ -37,6 +36,6 @@ def test_custom_length(self, session, User): assert User.short_email.type.impl.length == 70 def test_compilation(self, User, session): - query = sa.select(*_select_args(User.email)) + query = sa.select(User.email) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_encrypted.py b/tests/types/test_encrypted.py index 8bfd576a..d5503034 100644 --- a/tests/types/test_encrypted.py +++ b/tests/types/test_encrypted.py @@ -205,11 +205,7 @@ def user( session.add(user) session.commit() - try: - return session.get(User, user.id) - except AttributeError: - # sqlalchemy 1.3 - return session.query(User).get(user.id) + return session.get(User, user.id) @pytest.fixture @@ -320,11 +316,7 @@ def test_lookup_key(self, session, Team): id=team_1_id ).one()[0] - try: - team = session.get(Team, team_1_id) - except AttributeError: - # sqlalchemy 1.3 - team = session.query(Team).get(team_1_id) + team = session.get(Team, team_1_id) assert team.name == 'One' @@ -334,11 +326,7 @@ def test_lookup_key(self, session, Team): id=team_2_id ).one()[0] - try: - team = session.get(Team, team_2_id) - except AttributeError: - # sqlalchemy 1.3 - team = session.query(Team).get(team_2_id) + team = session.get(Team, team_2_id) assert team.name == 'Two' diff --git a/tests/types/test_enriched_date_pendulum.py b/tests/types/test_enriched_date_pendulum.py index dce2332f..f85c9e7a 100644 --- a/tests/types/test_enriched_date_pendulum.py +++ b/tests/types/test_enriched_date_pendulum.py @@ -3,7 +3,6 @@ import pytest import sqlalchemy as sa -from sqlalchemy_utils.compat import _select_args from sqlalchemy_utils.types.enriched_datetime import ( enriched_date_type, pendulum_date @@ -67,6 +66,6 @@ def test_literal_param(self, session, User): assert compiled == "users.birthday > '2015-01-01'" def test_compilation(self, User, session): - query = sa.select(*_select_args(User.birthday)) + query = sa.select(User.birthday) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_enriched_datetime_arrow.py b/tests/types/test_enriched_datetime_arrow.py index 81685f92..0692806c 100644 --- a/tests/types/test_enriched_datetime_arrow.py +++ b/tests/types/test_enriched_datetime_arrow.py @@ -4,7 +4,6 @@ import sqlalchemy as sa from dateutil import tz -from sqlalchemy_utils.compat import _select_args from sqlalchemy_utils.types.enriched_datetime import ( arrow_datetime, enriched_datetime_type @@ -91,6 +90,6 @@ def test_timezone(self, session, Article): assert item.published_at.to(timezone) == dt def test_compilation(self, Article, session): - query = sa.select(*_select_args(Article.published_at)) + query = sa.select(Article.published_at) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_int_range.py b/tests/types/test_int_range.py index 12f8f4e6..17b68d23 100644 --- a/tests/types/test_int_range.py +++ b/tests/types/test_int_range.py @@ -2,7 +2,6 @@ import sqlalchemy as sa from sqlalchemy_utils import IntRangeType -from sqlalchemy_utils.compat import _select_args, get_scalar_subquery intervals = None inf = -1 @@ -101,7 +100,7 @@ def test_integer_coercion(self, Building): assert building.persons_at_night.upper == 15 def test_compilation(self, session, Building): - query = sa.select(*_select_args(Building.persons_at_night)) + query = sa.select(Building.persons_at_night) # the type should be cacheable and not throw exception session.execute(query) @@ -290,7 +289,7 @@ def test_eq_with_query_arg(self, session, Building, create_building): session.query(Building) .filter( Building.persons_at_night == - get_scalar_subquery(session.query(Building.persons_at_night)) + session.query(Building.persons_at_night).scalar_subquery() ).order_by(Building.persons_at_night).limit(1) ) assert query.count() diff --git a/tests/types/test_ip_address.py b/tests/types/test_ip_address.py index 4f2f8376..5f33823e 100644 --- a/tests/types/test_ip_address.py +++ b/tests/types/test_ip_address.py @@ -1,7 +1,6 @@ import pytest import sqlalchemy as sa -from sqlalchemy_utils.compat import _select_args from sqlalchemy_utils.types import ip_address @@ -36,6 +35,6 @@ def test_parameter_processing(self, session, Visitor): assert str(visitor.ip_address) == '111.111.111.111' def test_compilation(self, Visitor, session): - query = sa.select(*_select_args(Visitor.ip_address)) + query = sa.select(Visitor.ip_address) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_json.py b/tests/types/test_json.py index f401958c..d6ae6415 100644 --- a/tests/types/test_json.py +++ b/tests/types/test_json.py @@ -2,7 +2,6 @@ import sqlalchemy as sa import sqlalchemy.orm as sa_orm -from sqlalchemy_utils.compat import _select_args from sqlalchemy_utils.types import json @@ -76,7 +75,7 @@ def test_non_ascii_chars(self, session, Document): assert document.json == {'something': 'äääööö'} def test_compilation(self, Document, session): - query = sa.select(*_select_args(Document.json)) + query = sa.select(Document.json) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_locale.py b/tests/types/test_locale.py index 42f7690e..5b40ac2e 100644 --- a/tests/types/test_locale.py +++ b/tests/types/test_locale.py @@ -1,7 +1,6 @@ import pytest import sqlalchemy as sa -from sqlalchemy_utils.compat import _select_args from sqlalchemy_utils.types import locale @@ -62,7 +61,7 @@ def test_literal_param(self, session, User): assert compiled == '"user".locale = \'en_US\'' def test_compilation(self, User, session): - query = sa.select(*_select_args(User.locale)) + query = sa.select(User.locale) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_ltree.py b/tests/types/test_ltree.py index 798d7cb6..9f00351f 100644 --- a/tests/types/test_ltree.py +++ b/tests/types/test_ltree.py @@ -2,7 +2,6 @@ import sqlalchemy as sa from sqlalchemy_utils import Ltree, LtreeType -from sqlalchemy_utils.compat import _select_args @pytest.fixture @@ -43,6 +42,6 @@ def test_literal_param(self, session, Section): assert compiled == 'section.path = \'path\'' def test_compilation(self, Section, session): - query = sa.select(*_select_args(Section.path)) + query = sa.select(Section.path) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_numeric_range.py b/tests/types/test_numeric_range.py index 1c167c7c..d8cce962 100644 --- a/tests/types/test_numeric_range.py +++ b/tests/types/test_numeric_range.py @@ -4,7 +4,6 @@ import sqlalchemy as sa from sqlalchemy_utils import NumericRangeType -from sqlalchemy_utils.compat import _select_args intervals = None inf = 0 @@ -91,7 +90,7 @@ def test_integer_coercion(self, Car): assert car.price_range.upper == 15 def test_compilation(self, session, Car): - query = sa.select(*_select_args(Car.price_range)) + query = sa.select(Car.price_range) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_password.py b/tests/types/test_password.py index ae8a83cd..f330f853 100644 --- a/tests/types/test_password.py +++ b/tests/types/test_password.py @@ -9,7 +9,6 @@ from sqlalchemy import inspect from sqlalchemy_utils import Password, PasswordType, types # noqa -from sqlalchemy_utils.compat import _select_args @pytest.fixture @@ -96,11 +95,7 @@ def test_check(self, session, User): session.add(obj) session.commit() - try: - obj = session.get(User, obj.id) - except AttributeError: - # sqlalchemy 1.3 - obj = session.query(User).get(obj.id) + obj = session.get(User, obj.id) assert obj.password == b'b' assert obj.password != 'a' @@ -162,11 +157,7 @@ def test_set_none(self, session, User): session.add(obj) session.commit() - try: - obj = session.get(User, obj.id) - except AttributeError: - # sqlalchemy 1.3 - obj = session.query(User).get(obj.id) + obj = session.get(User, obj.id) assert obj.password is None @@ -182,11 +173,7 @@ def test_update_none(self, session, User): session.add(obj) session.commit() - try: - obj = session.get(User, obj.id) - except AttributeError: - # sqlalchemy 1.3 - obj = session.query(User).get(obj.id) + obj = session.get(User, obj.id) obj.password = 'b' session.commit() @@ -226,11 +213,7 @@ def test_check_and_update_persist(self, session, User): session.commit() - try: - obj = session.get(User, obj.id) - except AttributeError: - # sqlalchemy 1.3 - obj = session.query(User).get(obj.id) + obj = session.get(User, obj.id) assert obj.password.hash.decode('utf8').startswith('$pbkdf2-sha512$') assert obj.password == 'b' @@ -274,6 +257,6 @@ def test_context_is_lazy(self): assert not onload.called def test_compilation(self, User, session): - query = sa.select(*_select_args(User.password)) + query = sa.select(User.password) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_phonenumber.py b/tests/types/test_phonenumber.py index 7a7f52d1..eb0c2d93 100644 --- a/tests/types/test_phonenumber.py +++ b/tests/types/test_phonenumber.py @@ -8,7 +8,6 @@ PhoneNumberType, types ) -from sqlalchemy_utils.compat import _select_args VALID_PHONE_NUMBERS = ( "040 1234567", @@ -159,7 +158,7 @@ def test_scalar_attributes_get_coerced_to_objects(self, User): assert isinstance(user.phone_number, PhoneNumber) def test_compilation(self, User, session): - query = sa.select(*_select_args(User.phone_number)) + query = sa.select(User.phone_number) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_scalar_list.py b/tests/types/test_scalar_list.py index 2feb5fe5..460452da 100644 --- a/tests/types/test_scalar_list.py +++ b/tests/types/test_scalar_list.py @@ -3,7 +3,6 @@ import sqlalchemy.exc from sqlalchemy_utils import ScalarListType -from sqlalchemy_utils.compat import _select_args class TestScalarIntegerList: @@ -95,6 +94,6 @@ def test_save_and_retrieve_empty_list(self, session, User): assert user.some_list == [] def test_compilation(self, User, session): - query = sa.select(*_select_args(User.some_list)) + query = sa.select(User.some_list) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_timezone.py b/tests/types/test_timezone.py index f1cdc464..30c0d798 100644 --- a/tests/types/test_timezone.py +++ b/tests/types/test_timezone.py @@ -8,7 +8,6 @@ except ImportError: from backports import zoneinfo -from sqlalchemy_utils.compat import _select_args from sqlalchemy_utils.types import timezone, TimezoneType @@ -63,7 +62,7 @@ def test_parameter_processing(self, session, Visitor): assert visitor_zoneinfo is not None def test_compilation(self, Visitor, session): - query = sa.select(*_select_args(Visitor.timezone_pytz)) + query = sa.select(Visitor.timezone_pytz) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_url.py b/tests/types/test_url.py index e8ee57cd..09c72652 100644 --- a/tests/types/test_url.py +++ b/tests/types/test_url.py @@ -1,7 +1,6 @@ import pytest import sqlalchemy as sa -from sqlalchemy_utils.compat import _select_args from sqlalchemy_utils.types import url @@ -41,6 +40,6 @@ def test_scalar_attributes_get_coerced_to_objects(self, User): assert isinstance(user.website, url.furl) def test_compilation(self, User, session): - query = sa.select(*_select_args(User.website)) + query = sa.select(User.website) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_uuid.py b/tests/types/test_uuid.py index 71a33c90..f3ca122f 100644 --- a/tests/types/test_uuid.py +++ b/tests/types/test_uuid.py @@ -5,7 +5,6 @@ from sqlalchemy.dialects import postgresql from sqlalchemy_utils import UUIDType -from sqlalchemy_utils.compat import _select_args @pytest.fixture @@ -59,7 +58,7 @@ def test_coerce(self, User): assert obj.id.bytes == identifier def test_compilation(self, User, session): - query = sa.select(*_select_args(User.id)) + query = sa.select(User.id) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tests/types/test_weekdays.py b/tests/types/test_weekdays.py index 9088d965..986a9d99 100644 --- a/tests/types/test_weekdays.py +++ b/tests/types/test_weekdays.py @@ -2,7 +2,6 @@ import sqlalchemy as sa from sqlalchemy_utils import i18n -from sqlalchemy_utils.compat import _select_args from sqlalchemy_utils.primitives import WeekDays from sqlalchemy_utils.types import WeekDaysType @@ -48,7 +47,7 @@ def test_scalar_attributes_get_coerced_to_objects(self, Schedule): assert isinstance(schedule.working_days, WeekDays) def test_compilation(self, Schedule, session): - query = sa.select(*_select_args(Schedule.working_days)) + query = sa.select(Schedule.working_days) # the type should be cacheable and not throw exception session.execute(query) diff --git a/tox.ini b/tox.ini index c293cef0..65cc7548 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py{3.9, 3.10, 3.11, 3.12}-sqlalchemy{13, 14, 2} + py{3.9, 3.10, 3.11, 3.12}-sqlalchemy{14, 2} flake8 isort docs @@ -13,7 +13,6 @@ commands = deps = .[test_all] pytest-cov - sqlalchemy13: SQLAlchemy[postgresql_pg8000]>=1.3,<1.4 sqlalchemy14: SQLAlchemy>=1.4,<1.5 sqlalchemy2: SQLAlchemy>=2 ; It's sometimes necessary to test against specific sqlalchemy versions