From 67b5de3e8b274d8ff962f26ac5c6915c01aac1b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Radoslav=20Bod=C3=B3?= Date: Fri, 2 Jul 2021 11:02:57 +0200 Subject: [PATCH 01/26] githubactions: init --- .github/workflows/tests.yml | 73 +++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..e7a6c99 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,73 @@ +name: Tests CI +on: + - push + - pull_request + +jobs: + tests: + name: ${{ matrix.tox }} + runs-on: ubuntu-latest + + services: + mariadb: + image: mariadb:latest + ports: + - 3306:3306 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 + + postgres: + image: postgres + ports: + - 5432:5432 + env: + POSTGRES_USER: postgres + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_DB: test_sqlalchemy_filters + POSTGRES_INITDB_ARGS: "--encoding=UTF8 --lc-collate=en_US.utf8 --lc-ctype=en_US.utf8" + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + + + strategy: + fail-fast: false + matrix: + include: + - {python: '2.7', tox: "py27-sqlalchemy1.0"} + - {python: '2.7', tox: "py27-sqlalchemy1.1"} + - {python: '2.7', tox: "py27-sqlalchemy1.2"} + - {python: '2.7', tox: "py27-sqlalchemy1.3"} + - {python: '2.7', tox: "py27-sqlalchemylatest"} + + - {python: '3.5', tox: "py35-sqlalchemy1.0"} + - {python: '3.5', tox: "py35-sqlalchemy1.1"} + - {python: '3.5', tox: "py35-sqlalchemy1.2"} + - {python: '3.5', tox: "py35-sqlalchemy1.3"} + - {python: '3.5', tox: "py35-sqlalchemylatest"} + + - {python: '3.6', tox: "py36-sqlalchemy1.0"} + - {python: '3.6', tox: "py36-sqlalchemy1.1"} + - {python: '3.6', tox: "py36-sqlalchemy1.2"} + - {python: '3.6', tox: "py36-sqlalchemy1.3"} + - {python: '3.6', tox: "py36-sqlalchemylatest"} + + - {python: '3.7', tox: "py37-sqlalchemy1.0"} + - {python: '3.7', tox: "py37-sqlalchemy1.1"} + - {python: '3.7', tox: "py37-sqlalchemy1.2"} + - {python: '3.7', tox: "py37-sqlalchemy1.3"} + - {python: '3.7', tox: "py37-sqlalchemylatest"} + + - {python: '3.8', tox: "py38-sqlalchemy1.0"} + - {python: '3.8', tox: "py38-sqlalchemy1.1"} + - {python: '3.8', tox: "py38-sqlalchemy1.2"} + - {python: '3.8', tox: "py38-sqlalchemy1.3"} + - {python: '3.8', tox: "py38-sqlalchemylatest"} + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + + - run: pip install tox + - run: tox -e ${{ matrix.tox }} From 499e669baa8b03085a0be7587f813ae9a0df60f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Radoslav=20Bod=C3=B3?= Date: Fri, 2 Jul 2021 11:12:39 +0200 Subject: [PATCH 02/26] githubactions: handle py27 vs sqlalchemy-utils vs sqlalchemy version dependencies --- .github/workflows/tests.yml | 1 - setup.py | 3 ++- tox.ini | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e7a6c99..eebf8b9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -37,7 +37,6 @@ jobs: - {python: '2.7', tox: "py27-sqlalchemy1.1"} - {python: '2.7', tox: "py27-sqlalchemy1.2"} - {python: '2.7', tox: "py27-sqlalchemy1.3"} - - {python: '2.7', tox: "py27-sqlalchemylatest"} - {python: '3.5', tox: "py35-sqlalchemy1.0"} - {python: '3.5', tox: "py35-sqlalchemy1.1"} diff --git a/setup.py b/setup.py index 5e9c10e..1631980 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,8 @@ 'dev': [ 'pytest>=4.6.9', 'coverage~=5.0.4', - 'sqlalchemy-utils~=0.36.3', + # for sqlalchemy1.4 >= 0.37 is required + 'sqlalchemy-utils>=0.36.3', 'flake8', 'restructuredtext-lint', 'Pygments', diff --git a/tox.ini b/tox.ini index 46a9424..79a7e42 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = {py27,py35,py36,py37,py38}-sqlalchemy{1.0,1.1,1.2,1.3,latest} +envlist = {py27}-sqlalchemy{1.0,1.1,1.2,1.3},{py35,py36,py37,py38}-sqlalchemy{1.0,1.1,1.2,1.3,latest} skipsdist = True [testenv] @@ -14,6 +14,9 @@ deps = # https://docs.pytest.org/en/latest/py27-py34-deprecation.html py27: pytest<5.0.0 {py35,py36,py37,py38}: pytest~=5.4.1 + # https://github.com/kvesteri/sqlalchemy-utils/blob/master/CHANGES.rst#0364-2020-04-30 + py27: sqlalchemy-utils==0.36.3 + {py35,py36,py37,py38}: sqlalchemy-utils~=0.37.8 sqlalchemy1.0: sqlalchemy>=1.0,<1.1 sqlalchemy1.1: sqlalchemy>=1.1,<1.2 sqlalchemy1.2: sqlalchemy>=1.2,<1.3 From b13f662383eb55c538a419bdda00f627565a7682 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Radoslav=20Bod=C3=B3?= Date: Fri, 2 Jul 2021 17:51:03 +0200 Subject: [PATCH 03/26] add sqlalchemy 1.4 support --- sqlalchemy_filters/models.py | 67 ++++++++++++++++++++++++++--------- test/interface/test_models.py | 8 +++-- 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/sqlalchemy_filters/models.py b/sqlalchemy_filters/models.py index 1c79516..5bfa47e 100644 --- a/sqlalchemy_filters/models.py +++ b/sqlalchemy_filters/models.py @@ -1,12 +1,19 @@ +from sqlalchemy import __version__ as sqlalchemy_version from sqlalchemy.exc import InvalidRequestError +from sqlalchemy.orm import mapperlib from sqlalchemy.inspection import inspect -from sqlalchemy.orm.mapper import Mapper from sqlalchemy.util import symbol import types from .exceptions import BadQuery, FieldNotFound, BadSpec +def sqlalchemy_version_lt(version): + """compares sqla version < version""" + + return tuple(sqlalchemy_version.split('.')) < tuple(version.split('.')) + + class Field(object): def __init__(self, model, field_name): @@ -61,20 +68,33 @@ def get_query_models(query): A dictionary with all the models included in the query. """ models = [col_desc['entity'] for col_desc in query.column_descriptions] - models.extend(mapper.class_ for mapper in query._join_entities) + try: + join_entities = ( + query._join_entities + if sqlalchemy_version_lt('1.4') + else query._compile_state()._join_entities + ) + models.extend(mapper.class_ for mapper in join_entities) + except InvalidRequestError: # pragma: nocover + pass # handle compilation errors in sqla 1.4 # account also query.select_from entities - if ( - hasattr(query, '_select_from_entity') and - (query._select_from_entity is not None) - ): - model_class = ( - query._select_from_entity.class_ - if isinstance(query._select_from_entity, Mapper) # sqlalchemy>=1.1 - else query._select_from_entity # sqlalchemy==1.0 - ) - if model_class not in models: - models.append(model_class) + model_class = None + if sqlalchemy_version_lt('1.4'): # pragma: nocover ; sqlalchemy<1.4 + if query._select_from_entity: + model_class = ( + query._select_from_entity + if sqlalchemy_version_lt('1.1') + else query._select_from_entity.class_ + ) + else: # pragma: nocover ; sqlalchemy>=1.4 + if query._from_obj: + for registry in mapperlib._all_registries(): + for mapper in registry.mappers: + if query._from_obj[0] in mapper.tables: + model_class = mapper.class_ + if model_class and (model_class not in models): + models.append(model_class) return {model.__name__: model for model in models} @@ -152,13 +172,26 @@ def auto_join(query, *model_names): """ # every model has access to the registry, so we can use any from the query query_models = get_query_models(query).values() - model_registry = list(query_models)[-1]._decl_class_registry + last_model = list(query_models)[-1] + model_registry = ( + last_model._decl_class_registry + if sqlalchemy_version_lt('1.4') + else last_model.registry._class_registry + ) for name in model_names: model = get_model_class_by_name(model_registry, name) - if model not in get_query_models(query).values(): - try: - query = query.join(model) + if model and (model not in get_query_models(query).values()): + try: # pragma: nocover + if sqlalchemy_version_lt('1.4'): + query = query.join(model) + else: + # https://docs.sqlalchemy.org/en/14/changelog/migration_14.html + # Many Core and ORM statement objects now perform much of + # their construction and validation in the compile phase + tmp = query.join(model) + tmp._compile_state() + query = tmp except InvalidRequestError: pass # can't be autojoined return query diff --git a/test/interface/test_models.py b/test/interface/test_models.py index 0531095..86853ac 100644 --- a/test/interface/test_models.py +++ b/test/interface/test_models.py @@ -5,7 +5,7 @@ from sqlalchemy_filters.exceptions import BadSpec, BadQuery from sqlalchemy_filters.models import ( auto_join, get_default_model, get_query_models, get_model_class_by_name, - get_model_from_spec + get_model_from_spec, sqlalchemy_version_lt ) from test.models import Base, Bar, Foo, Qux @@ -132,7 +132,11 @@ class TestGetModelClassByName: @pytest.fixture def registry(self): - return Base._decl_class_registry + return ( + Base._decl_class_registry + if sqlalchemy_version_lt('1.4') + else Base.registry._class_registry + ) def test_exists(self, registry): assert get_model_class_by_name(registry, 'Foo') == Foo From 8c469e5949bcc66bc6c665fbc3fb76c5fd86977a Mon Sep 17 00:00:00 2001 From: Radoslav Bodo Date: Mon, 12 Jul 2021 00:11:11 +0200 Subject: [PATCH 04/26] sqlalchemy 1.4 infer tables from query without columns (sqlalchemy-datatables usecase) --- sqlalchemy_filters/models.py | 46 +++++++++++++++++++++++------------ test/interface/test_models.py | 7 ++++++ 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/sqlalchemy_filters/models.py b/sqlalchemy_filters/models.py index 5bfa47e..e9e1c3b 100644 --- a/sqlalchemy_filters/models.py +++ b/sqlalchemy_filters/models.py @@ -58,6 +58,16 @@ def _is_hybrid_method(orm_descriptor): return orm_descriptor.extension_type == symbol('HYBRID_METHOD') +def get_model_from_table(table): # pragma: nocover + """Resolve model class from table object""" + + for registry in mapperlib._all_registries(): + for mapper in registry.mappers: + if table in mapper.tables: + return mapper.class_ + return None + + def get_query_models(query): """Get models from query. @@ -68,31 +78,37 @@ def get_query_models(query): A dictionary with all the models included in the query. """ models = [col_desc['entity'] for col_desc in query.column_descriptions] - try: - join_entities = ( - query._join_entities - if sqlalchemy_version_lt('1.4') - else query._compile_state()._join_entities - ) - models.extend(mapper.class_ for mapper in join_entities) - except InvalidRequestError: # pragma: nocover - pass # handle compilation errors in sqla 1.4 + + # account joined entities + if sqlalchemy_version_lt('1.4'): # pragma: nocover + models.extend(mapper.class_ for mapper in query._join_entities) + else: # pragma: nocover + try: + models.extend( + mapper.class_ + for mapper + in query._compile_state()._join_entities + ) + except InvalidRequestError: + # query might not contain columns yet, hence cannot be compiled + # try to infer the models from various internals + for table_tuple in query._setup_joins + query._legacy_setup_joins: + model_class = get_model_from_table(table_tuple[0]) + if model_class: + models.append(model_class) # account also query.select_from entities model_class = None - if sqlalchemy_version_lt('1.4'): # pragma: nocover ; sqlalchemy<1.4 + if sqlalchemy_version_lt('1.4'): # pragma: nocover if query._select_from_entity: model_class = ( query._select_from_entity if sqlalchemy_version_lt('1.1') else query._select_from_entity.class_ ) - else: # pragma: nocover ; sqlalchemy>=1.4 + else: # pragma: nocover if query._from_obj: - for registry in mapperlib._all_registries(): - for mapper in registry.mappers: - if query._from_obj[0] in mapper.tables: - model_class = mapper.class_ + model_class = get_model_from_table(query._from_obj[0]) if model_class and (model_class not in models): models.append(model_class) diff --git a/test/interface/test_models.py b/test/interface/test_models.py index 86853ac..8910efb 100644 --- a/test/interface/test_models.py +++ b/test/interface/test_models.py @@ -33,6 +33,13 @@ def test_query_with_select_from_model(self, session): assert {'Bar': Bar} == entities + def test_query_with_select_from_and_join_model(self, session): + query = session.query().select_from(Bar).join(Foo) + + entities = get_query_models(query) + + assert {'Bar': Bar, 'Foo': Foo} == entities + def test_query_with_multiple_models(self, session): query = session.query(Bar, Qux) From 58a9f767d39fb5b262bc5ea62acac4c2ae9bbfa4 Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 6 Apr 2023 14:26:28 +0100 Subject: [PATCH 05/26] add sqlalchemy 1.4 to tox conf --- tox.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 79a7e42..5f82b7e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = {py27}-sqlalchemy{1.0,1.1,1.2,1.3},{py35,py36,py37,py38}-sqlalchemy{1.0,1.1,1.2,1.3,latest} +envlist = {py27}-sqlalchemy{1.0,1.1,1.2,1.3,1.4},{py35,py36,py37,py38}-sqlalchemy{1.0,1.1,1.2,1.3,1.4,latest} skipsdist = True [testenv] @@ -21,5 +21,6 @@ deps = sqlalchemy1.1: sqlalchemy>=1.1,<1.2 sqlalchemy1.2: sqlalchemy>=1.2,<1.3 sqlalchemy1.3: sqlalchemy>=1.3,<1.4 + sqlalchemy1.4: sqlalchemy>=1.4,<1.5 commands = make coverage ARGS='-x -vv' From 342e89c82b5ae502de1dbd554644ed674a6b7746 Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 6 Apr 2023 14:36:55 +0100 Subject: [PATCH 06/26] github actions, actions/setup-python v2->v4 --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index eebf8b9..ea3e912 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -64,7 +64,7 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} From 02b72d582511ac366ccafb87aa2e609bd41e1cd8 Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 6 Apr 2023 14:40:10 +0100 Subject: [PATCH 07/26] github actions needs patch level python versions and add sqlalchemy 1.4 --- .github/workflows/tests.yml | 45 ++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ea3e912..e1f1b24 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -37,30 +37,35 @@ jobs: - {python: '2.7', tox: "py27-sqlalchemy1.1"} - {python: '2.7', tox: "py27-sqlalchemy1.2"} - {python: '2.7', tox: "py27-sqlalchemy1.3"} + - {python: '2.7', tox: "py27-sqlalchemy1.4"} - - {python: '3.5', tox: "py35-sqlalchemy1.0"} - - {python: '3.5', tox: "py35-sqlalchemy1.1"} - - {python: '3.5', tox: "py35-sqlalchemy1.2"} - - {python: '3.5', tox: "py35-sqlalchemy1.3"} - - {python: '3.5', tox: "py35-sqlalchemylatest"} + - {python: '3.5.10', tox: "py35-sqlalchemy1.0"} + - {python: '3.5.10', tox: "py35-sqlalchemy1.1"} + - {python: '3.5.10', tox: "py35-sqlalchemy1.2"} + - {python: '3.5.10', tox: "py35-sqlalchemy1.3"} + - {python: '3.5.10', tox: "py35-sqlalchemy1.4"} + - {python: '3.5.10', tox: "py35-sqlalchemylatest"} - - {python: '3.6', tox: "py36-sqlalchemy1.0"} - - {python: '3.6', tox: "py36-sqlalchemy1.1"} - - {python: '3.6', tox: "py36-sqlalchemy1.2"} - - {python: '3.6', tox: "py36-sqlalchemy1.3"} - - {python: '3.6', tox: "py36-sqlalchemylatest"} + - {python: '3.6.15', tox: "py36-sqlalchemy1.0"} + - {python: '3.6.15', tox: "py36-sqlalchemy1.1"} + - {python: '3.6.15', tox: "py36-sqlalchemy1.2"} + - {python: '3.6.15', tox: "py36-sqlalchemy1.3"} + - {python: '3.6.15', tox: "py36-sqlalchemy1.4"} + - {python: '3.6.15', tox: "py36-sqlalchemylatest"} - - {python: '3.7', tox: "py37-sqlalchemy1.0"} - - {python: '3.7', tox: "py37-sqlalchemy1.1"} - - {python: '3.7', tox: "py37-sqlalchemy1.2"} - - {python: '3.7', tox: "py37-sqlalchemy1.3"} - - {python: '3.7', tox: "py37-sqlalchemylatest"} + - {python: '3.7.16', tox: "py37-sqlalchemy1.0"} + - {python: '3.7.16', tox: "py37-sqlalchemy1.1"} + - {python: '3.7.16', tox: "py37-sqlalchemy1.2"} + - {python: '3.7.16', tox: "py37-sqlalchemy1.3"} + - {python: '3.7.16', tox: "py37-sqlalchemy1.4"} + - {python: '3.7.16', tox: "py37-sqlalchemylatest"} - - {python: '3.8', tox: "py38-sqlalchemy1.0"} - - {python: '3.8', tox: "py38-sqlalchemy1.1"} - - {python: '3.8', tox: "py38-sqlalchemy1.2"} - - {python: '3.8', tox: "py38-sqlalchemy1.3"} - - {python: '3.8', tox: "py38-sqlalchemylatest"} + - {python: '3.8.16', tox: "py38-sqlalchemy1.0"} + - {python: '3.8.16', tox: "py38-sqlalchemy1.1"} + - {python: '3.8.16', tox: "py38-sqlalchemy1.2"} + - {python: '3.8.16', tox: "py38-sqlalchemy1.3"} + - {python: '3.8.16', tox: "py38-sqlalchemy1.4"} + - {python: '3.8.16', tox: "py38-sqlalchemylatest"} steps: - uses: actions/checkout@v2 From c13776d5bc9b7f7db30387afd600183dffa0c600 Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 6 Apr 2023 14:43:01 +0100 Subject: [PATCH 08/26] GHA: downgrade ubuntu 22.04->20.04 for more python versions --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e1f1b24..3da4883 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -6,7 +6,7 @@ on: jobs: tests: name: ${{ matrix.tox }} - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 services: mariadb: From e9219fe45792591cd9194e033bdbf1a09b90c0d4 Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 6 Apr 2023 14:45:31 +0100 Subject: [PATCH 09/26] revert python patch versions --- .github/workflows/tests.yml | 48 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3da4883..76f78f2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -39,33 +39,33 @@ jobs: - {python: '2.7', tox: "py27-sqlalchemy1.3"} - {python: '2.7', tox: "py27-sqlalchemy1.4"} - - {python: '3.5.10', tox: "py35-sqlalchemy1.0"} - - {python: '3.5.10', tox: "py35-sqlalchemy1.1"} - - {python: '3.5.10', tox: "py35-sqlalchemy1.2"} - - {python: '3.5.10', tox: "py35-sqlalchemy1.3"} - - {python: '3.5.10', tox: "py35-sqlalchemy1.4"} - - {python: '3.5.10', tox: "py35-sqlalchemylatest"} + - {python: '3.5', tox: "py35-sqlalchemy1.0"} + - {python: '3.5', tox: "py35-sqlalchemy1.1"} + - {python: '3.5', tox: "py35-sqlalchemy1.2"} + - {python: '3.5', tox: "py35-sqlalchemy1.3"} + - {python: '3.5', tox: "py35-sqlalchemy1.4"} + - {python: '3.5', tox: "py35-sqlalchemylatest"} - - {python: '3.6.15', tox: "py36-sqlalchemy1.0"} - - {python: '3.6.15', tox: "py36-sqlalchemy1.1"} - - {python: '3.6.15', tox: "py36-sqlalchemy1.2"} - - {python: '3.6.15', tox: "py36-sqlalchemy1.3"} - - {python: '3.6.15', tox: "py36-sqlalchemy1.4"} - - {python: '3.6.15', tox: "py36-sqlalchemylatest"} + - {python: '3.6', tox: "py36-sqlalchemy1.0"} + - {python: '3.6', tox: "py36-sqlalchemy1.1"} + - {python: '3.6', tox: "py36-sqlalchemy1.2"} + - {python: '3.6', tox: "py36-sqlalchemy1.3"} + - {python: '3.6', tox: "py36-sqlalchemy1.4"} + - {python: '3.6', tox: "py36-sqlalchemylatest"} - - {python: '3.7.16', tox: "py37-sqlalchemy1.0"} - - {python: '3.7.16', tox: "py37-sqlalchemy1.1"} - - {python: '3.7.16', tox: "py37-sqlalchemy1.2"} - - {python: '3.7.16', tox: "py37-sqlalchemy1.3"} - - {python: '3.7.16', tox: "py37-sqlalchemy1.4"} - - {python: '3.7.16', tox: "py37-sqlalchemylatest"} + - {python: '3.7', tox: "py37-sqlalchemy1.0"} + - {python: '3.7', tox: "py37-sqlalchemy1.1"} + - {python: '3.7', tox: "py37-sqlalchemy1.2"} + - {python: '3.7', tox: "py37-sqlalchemy1.3"} + - {python: '3.7', tox: "py37-sqlalchemy1.4"} + - {python: '3.7', tox: "py37-sqlalchemylatest"} - - {python: '3.8.16', tox: "py38-sqlalchemy1.0"} - - {python: '3.8.16', tox: "py38-sqlalchemy1.1"} - - {python: '3.8.16', tox: "py38-sqlalchemy1.2"} - - {python: '3.8.16', tox: "py38-sqlalchemy1.3"} - - {python: '3.8.16', tox: "py38-sqlalchemy1.4"} - - {python: '3.8.16', tox: "py38-sqlalchemylatest"} + - {python: '3.8', tox: "py38-sqlalchemy1.0"} + - {python: '3.8', tox: "py38-sqlalchemy1.1"} + - {python: '3.8', tox: "py38-sqlalchemy1.2"} + - {python: '3.8', tox: "py38-sqlalchemy1.3"} + - {python: '3.8', tox: "py38-sqlalchemy1.4"} + - {python: '3.8', tox: "py38-sqlalchemylatest"} steps: - uses: actions/checkout@v2 From 79844ebbe88c9bcdd9e2889bc48c4f94f6374202 Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 6 Apr 2023 14:50:43 +0100 Subject: [PATCH 10/26] GHA: pin tox --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 76f78f2..b327ea8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -73,5 +73,5 @@ jobs: with: python-version: ${{ matrix.python }} - - run: pip install tox + - run: pip install tox==3.15.1 - run: tox -e ${{ matrix.tox }} From 7a69686ab0e2aacf416799e6cded857f518f31f3 Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 6 Apr 2023 14:55:07 +0100 Subject: [PATCH 11/26] remove py2.7/sqlalchemy 1.4; add py3.9 --- .github/workflows/tests.yml | 8 +++++++- tox.ini | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b327ea8..1d1462e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -37,7 +37,6 @@ jobs: - {python: '2.7', tox: "py27-sqlalchemy1.1"} - {python: '2.7', tox: "py27-sqlalchemy1.2"} - {python: '2.7', tox: "py27-sqlalchemy1.3"} - - {python: '2.7', tox: "py27-sqlalchemy1.4"} - {python: '3.5', tox: "py35-sqlalchemy1.0"} - {python: '3.5', tox: "py35-sqlalchemy1.1"} @@ -67,6 +66,13 @@ jobs: - {python: '3.8', tox: "py38-sqlalchemy1.4"} - {python: '3.8', tox: "py38-sqlalchemylatest"} + - {python: '3.9', tox: "py39-sqlalchemy1.0"} + - {python: '3.9', tox: "py39-sqlalchemy1.1"} + - {python: '3.9', tox: "py39-sqlalchemy1.2"} + - {python: '3.9', tox: "py39-sqlalchemy1.3"} + - {python: '3.9', tox: "py39-sqlalchemy1.4"} + - {python: '3.9', tox: "py39-sqlalchemylatest"} + steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v4 diff --git a/tox.ini b/tox.ini index 5f82b7e..aa6c825 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = {py27}-sqlalchemy{1.0,1.1,1.2,1.3,1.4},{py35,py36,py37,py38}-sqlalchemy{1.0,1.1,1.2,1.3,1.4,latest} +envlist = {py27}-sqlalchemy{1.0,1.1,1.2,1.3,1.4},{py35,py36,py37,py38,py39}-sqlalchemy{1.0,1.1,1.2,1.3,1.4,latest} skipsdist = True [testenv] @@ -13,10 +13,10 @@ deps = py27: funcsigs # https://docs.pytest.org/en/latest/py27-py34-deprecation.html py27: pytest<5.0.0 - {py35,py36,py37,py38}: pytest~=5.4.1 + {py35,py36,py37,py38,py39}: pytest~=5.4.1 # https://github.com/kvesteri/sqlalchemy-utils/blob/master/CHANGES.rst#0364-2020-04-30 py27: sqlalchemy-utils==0.36.3 - {py35,py36,py37,py38}: sqlalchemy-utils~=0.37.8 + {py35,py36,py37,py38,py39}: sqlalchemy-utils~=0.37.8 sqlalchemy1.0: sqlalchemy>=1.0,<1.1 sqlalchemy1.1: sqlalchemy>=1.1,<1.2 sqlalchemy1.2: sqlalchemy>=1.2,<1.3 From 31d226d27415f7356ccbc3e7e70fea0f07ce997c Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 6 Apr 2023 17:43:36 +0100 Subject: [PATCH 12/26] tox 4 doesn't support Python<3.7 --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1d1462e..3bcde57 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -79,5 +79,5 @@ jobs: with: python-version: ${{ matrix.python }} - - run: pip install tox==3.15.1 + - run: pip install tox~=3.28 - run: tox -e ${{ matrix.tox }} From eba355115b7a70ce174b7f39333f4979f49e2491 Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 6 Apr 2023 18:12:00 +0100 Subject: [PATCH 13/26] update changelog --- CHANGELOG.rst | 8 ++++++++ README.rst | 2 +- setup.py | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 821e30a..2cad1f1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,14 @@ Release Notes Here you can see the full list of changes between sqlalchemy-filters versions, where semantic versioning is used: *major.minor.patch*. +0.13.0 +------ + +Released TBD + +* Add support for SQLAlchemy 1.4 (#69) thanks to @bodik +* Add support for Python 3.9 + 0.12.0 ------ diff --git a/README.rst b/README.rst index 8c3b8f2..bffb33b 100644 --- a/README.rst +++ b/README.rst @@ -499,7 +499,7 @@ SQLAlchemy support ------------------ The following SQLAlchemy_ versions are supported: ``1.0``, ``1.1``, -``1.2``, ``1.3``. +``1.2``, ``1.3``, ``1.4``. Changelog diff --git a/setup.py b/setup.py index 1631980..03b66a0 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,7 @@ "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", "Topic :: Database", "Topic :: Database :: Front-Ends", "Topic :: Software Development :: Libraries :: Python Modules", From c0ce3025bbf5668201c23b25467609d6b7942305 Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 6 Apr 2023 18:21:07 +0100 Subject: [PATCH 14/26] bump version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 03b66a0..72f113c 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name='sqlalchemy-filters', - version='0.12.0', + version='0.13.0', description='A library to filter SQLAlchemy queries.', long_description=readme, long_description_content_type='text/x-rst', From 9a50614f74f2a4d655e01d1d6055b2ee4f8e5400 Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 13 Apr 2023 08:59:02 +0100 Subject: [PATCH 15/26] use coverage-conditional-plugin --- setup.py | 1 + sqlalchemy_filters/models.py | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/setup.py b/setup.py index 72f113c..a2efdcf 100644 --- a/setup.py +++ b/setup.py @@ -30,6 +30,7 @@ 'flake8', 'restructuredtext-lint', 'Pygments', + 'coverage-conditional-plugin', ], 'mysql': ['mysql-connector-python-rf==2.2.2'], 'postgresql': ['psycopg2==2.8.4'], diff --git a/sqlalchemy_filters/models.py b/sqlalchemy_filters/models.py index e9e1c3b..b60b35b 100644 --- a/sqlalchemy_filters/models.py +++ b/sqlalchemy_filters/models.py @@ -58,7 +58,7 @@ def _is_hybrid_method(orm_descriptor): return orm_descriptor.extension_type == symbol('HYBRID_METHOD') -def get_model_from_table(table): # pragma: nocover +def get_model_from_table(table): # pragma: sqlalchemy_gte_1_4 """Resolve model class from table object""" for registry in mapperlib._all_registries(): @@ -80,9 +80,9 @@ def get_query_models(query): models = [col_desc['entity'] for col_desc in query.column_descriptions] # account joined entities - if sqlalchemy_version_lt('1.4'): # pragma: nocover + if sqlalchemy_version_lt('1.4'): # pragma: sqlalchemy_lt_1_4 models.extend(mapper.class_ for mapper in query._join_entities) - else: # pragma: nocover + else: # pragma: sqlalchemy_gte_1_4 try: models.extend( mapper.class_ @@ -99,14 +99,14 @@ def get_query_models(query): # account also query.select_from entities model_class = None - if sqlalchemy_version_lt('1.4'): # pragma: nocover + if sqlalchemy_version_lt('1.4'): # pragma: sqlalchemy_lt_1_4 if query._select_from_entity: model_class = ( query._select_from_entity if sqlalchemy_version_lt('1.1') else query._select_from_entity.class_ ) - else: # pragma: nocover + else: # pragma: sqlalchemy_gte_1_4 if query._from_obj: model_class = get_model_from_table(query._from_obj[0]) if model_class and (model_class not in models): @@ -198,10 +198,10 @@ def auto_join(query, *model_names): for name in model_names: model = get_model_class_by_name(model_registry, name) if model and (model not in get_query_models(query).values()): - try: # pragma: nocover - if sqlalchemy_version_lt('1.4'): + try: + if sqlalchemy_version_lt('1.4'): # pragma: sqlalchemy_lt_1_4 query = query.join(model) - else: + else: # pragma: sqlalchemy_gte_1_4 # https://docs.sqlalchemy.org/en/14/changelog/migration_14.html # Many Core and ORM statement objects now perform much of # their construction and validation in the compile phase From 326368b73a8d4fa9f80c63c1a5f3b46b8dea2496 Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 13 Apr 2023 09:11:14 +0100 Subject: [PATCH 16/26] only support in life Python versions see https://endoflife.date/python --- .github/workflows/tests.yml | 26 +++-------- .travis.yml | 87 ----------------------------------- README.rst | 8 ---- setup.py | 4 +- sqlalchemy_filters/filters.py | 14 +----- tox.ini | 10 +--- 6 files changed, 12 insertions(+), 137 deletions(-) delete mode 100644 .travis.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3bcde57..fe8cb61 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,25 +33,6 @@ jobs: fail-fast: false matrix: include: - - {python: '2.7', tox: "py27-sqlalchemy1.0"} - - {python: '2.7', tox: "py27-sqlalchemy1.1"} - - {python: '2.7', tox: "py27-sqlalchemy1.2"} - - {python: '2.7', tox: "py27-sqlalchemy1.3"} - - - {python: '3.5', tox: "py35-sqlalchemy1.0"} - - {python: '3.5', tox: "py35-sqlalchemy1.1"} - - {python: '3.5', tox: "py35-sqlalchemy1.2"} - - {python: '3.5', tox: "py35-sqlalchemy1.3"} - - {python: '3.5', tox: "py35-sqlalchemy1.4"} - - {python: '3.5', tox: "py35-sqlalchemylatest"} - - - {python: '3.6', tox: "py36-sqlalchemy1.0"} - - {python: '3.6', tox: "py36-sqlalchemy1.1"} - - {python: '3.6', tox: "py36-sqlalchemy1.2"} - - {python: '3.6', tox: "py36-sqlalchemy1.3"} - - {python: '3.6', tox: "py36-sqlalchemy1.4"} - - {python: '3.6', tox: "py36-sqlalchemylatest"} - - {python: '3.7', tox: "py37-sqlalchemy1.0"} - {python: '3.7', tox: "py37-sqlalchemy1.1"} - {python: '3.7', tox: "py37-sqlalchemy1.2"} @@ -73,6 +54,13 @@ jobs: - {python: '3.9', tox: "py39-sqlalchemy1.4"} - {python: '3.9', tox: "py39-sqlalchemylatest"} + - {python: '3.10', tox: "py39-sqlalchemy1.0"} + - {python: '3.10', tox: "py39-sqlalchemy1.1"} + - {python: '3.10', tox: "py39-sqlalchemy1.2"} + - {python: '3.10', tox: "py39-sqlalchemy1.3"} + - {python: '3.10', tox: "py39-sqlalchemy1.4"} + - {python: '3.10', tox: "py39-sqlalchemylatest"} + steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v4 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6c9de39..0000000 --- a/.travis.yml +++ /dev/null @@ -1,87 +0,0 @@ -language: python -python: 3.7 - -dist: xenial - -services: - - docker - -before_install: - - make mysql-container - - make postgres-container - -install: - - pip install tox - -matrix: - include: - - stage: test - python: 2.7 - env: TOX_ENV="py27-sqlalchemy1.0" - - python: 2.7 - env: TOX_ENV="py27-sqlalchemy1.1" - - python: 2.7 - env: TOX_ENV="py27-sqlalchemy1.2" - - python: 2.7 - env: TOX_ENV="py27-sqlalchemy1.3" - - python: 2.7 - env: TOX_ENV="py27-sqlalchemylatest" - - - python: 3.5 - env: TOX_ENV="py35-sqlalchemy1.0" - - python: 3.5 - env: TOX_ENV="py35-sqlalchemy1.1" - - python: 3.5 - env: TOX_ENV="py35-sqlalchemy1.2" - - python: 3.5 - env: TOX_ENV="py35-sqlalchemy1.3" - - python: 3.5 - env: TOX_ENV="py35-sqlalchemylatest" - - - python: 3.6 - env: TOX_ENV="py36-sqlalchemy1.0" - - python: 3.6 - env: TOX_ENV="py36-sqlalchemy1.1" - - python: 3.6 - env: TOX_ENV="py36-sqlalchemy1.2" - - python: 3.6 - env: TOX_ENV="py36-sqlalchemy1.3" - - python: 3.6 - env: TOX_ENV="py36-sqlalchemylatest" - - - python: 3.7 - env: TOX_ENV="py37-sqlalchemy1.0" - - python: 3.7 - env: TOX_ENV="py37-sqlalchemy1.1" - - python: 3.7 - env: TOX_ENV="py37-sqlalchemy1.2" - - python: 3.7 - env: TOX_ENV="py37-sqlalchemy1.3" - - python: 3.7 - env: TOX_ENV="py37-sqlalchemylatest" - - - python: 3.8 - env: TOX_ENV="py38-sqlalchemy1.0" - - python: 3.8 - env: TOX_ENV="py38-sqlalchemy1.1" - - python: 3.8 - env: TOX_ENV="py38-sqlalchemy1.2" - - python: 3.8 - env: TOX_ENV="py38-sqlalchemy1.3" - - python: 3.8 - env: TOX_ENV="py38-sqlalchemylatest" - - - stage: deploy - script: skip - deploy: - provider: pypi - user: mattbennett - password: - secure: "x27Zk+mvbYOtuf3XojYBh3O32KTv79SebMzkXE1lmtSms/pvRT+OG5eUAvWq1tro0shQdvv1X86aK9UHNzkRpIJEvbyH8zP3nWr/TlI62iZJC6y9m/c6JWwIUBk3p+CDHSwA1i6QXrxnffRfE/KSlAuTOY2FDYFDUBdVrEQrqIJa68Ij9200kxNcYWC9JmJKLaGl1l/V9M2K7YDsll3PxBByrCQeM95oeuoL3U81fPV0CJnsPNXAMtQHC9+dv+WE9UCo9QVsxWrnJGvCVUAfNQ7Ub+pECwSqzIL1O1ltqi0JH5CafwF6EMcQWxjh6R7IuN224QoAGTBzJ+ngKMxV7aQr58rkP82CyGaYEI7w9pvM9cZ6nBVIdgfCe8EfXCg+tTYy6v7SptT66wzP+GdX7sOuNFDYadmXGHZdwtUtEDFHwiCETRJNt/t5ONejlktY0fXlRUCta5UbYj755iryhk1lE7Ldj3sU336WjG74iAFdAd0fO8gVM0OwKLYZeaU2mN4JBI4qcp6n2S0sNtoyqXzm9OL/76Is5vAzUQavto9ao9bw0HN+ZWVLyE4+NLKS3ti+Pi/YofieoUFZ6kQJQRGja3tt7RCl0MYHqdIQQWx9MJWdiDpkyqqtR+0Q2sSYo8g4TsJe8zCuQLOflwd9pLKXVwdfqMUVGFCbdzZ/rig=" - on: - tags: true - repo: juliotrigo/sqlalchemy-filters - distributions: "sdist bdist_wheel" - -script: - - tox -e $TOX_ENV diff --git a/README.rst b/README.rst index bffb33b..cf8c880 100644 --- a/README.rst +++ b/README.rst @@ -487,14 +487,6 @@ The following RDBMS are supported (tested): - PostgreSQL -Python 2 --------- - -There is no active support for Python 2. However, it is compatible as of -February 2019, if you install ``funcsigs``, included in the ``python2`` -extra requirements. - - SQLAlchemy support ------------------ diff --git a/setup.py b/setup.py index a2efdcf..06d99f3 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,6 @@ ], 'mysql': ['mysql-connector-python-rf==2.2.2'], 'postgresql': ['psycopg2==2.8.4'], - 'python2': ['funcsigs>=1.0.2'], }, zip_safe=True, license='Apache License, Version 2.0', @@ -44,11 +43,10 @@ "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", "Topic :: Database", "Topic :: Database :: Front-Ends", "Topic :: Software Development :: Libraries :: Python Modules", diff --git a/sqlalchemy_filters/filters.py b/sqlalchemy_filters/filters.py index 356c4fd..ba9d919 100644 --- a/sqlalchemy_filters/filters.py +++ b/sqlalchemy_filters/filters.py @@ -1,17 +1,7 @@ # -*- coding: utf-8 -*- from collections import namedtuple -try: - from collections.abc import Iterable -except ImportError: # pragma: no cover - # For python2 capability. - from collections import Iterable -try: - from inspect import signature -except ImportError: # pragma: no cover - # For python2 capability. NOTE: This is in not handled in install_requires - # but rather in extras_require. You can install with - # 'pip install sqlalchemy-filters[python2]' - from funcsigs import signature +from collections.abc import Iterable +from inspect import signature from itertools import chain from six import string_types diff --git a/tox.ini b/tox.ini index aa6c825..ca2fca8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = {py27}-sqlalchemy{1.0,1.1,1.2,1.3,1.4},{py35,py36,py37,py38,py39}-sqlalchemy{1.0,1.1,1.2,1.3,1.4,latest} +envlist = {py37,py38,py39,py310}-sqlalchemy{1.0,1.1,1.2,1.3,1.4,latest} skipsdist = True [testenv] @@ -10,13 +10,7 @@ extras = mysql postgresql deps = - py27: funcsigs - # https://docs.pytest.org/en/latest/py27-py34-deprecation.html - py27: pytest<5.0.0 - {py35,py36,py37,py38,py39}: pytest~=5.4.1 - # https://github.com/kvesteri/sqlalchemy-utils/blob/master/CHANGES.rst#0364-2020-04-30 - py27: sqlalchemy-utils==0.36.3 - {py35,py36,py37,py38,py39}: sqlalchemy-utils~=0.37.8 + {py37,py38,py39,py310}: sqlalchemy-utils~=0.37.8 sqlalchemy1.0: sqlalchemy>=1.0,<1.1 sqlalchemy1.1: sqlalchemy>=1.1,<1.2 sqlalchemy1.2: sqlalchemy>=1.2,<1.3 From 879d05a9a034f1f1b322ce7316750b6a02c5b1cb Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 13 Apr 2023 10:22:28 +0100 Subject: [PATCH 17/26] fix conditional coverage --- .coveragerc | 8 ++++++++ setup.cfg | 2 ++ sqlalchemy_filters/models.py | 14 +++++++------- 3 files changed, 17 insertions(+), 7 deletions(-) create mode 100644 .coveragerc create mode 100644 setup.cfg diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..2a1f049 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,8 @@ +[coverage:run] +plugins = + coverage_conditional_plugin + +[coverage:coverage_conditional_plugin] +rules = + "package_version('sqlalchemy') < (1, 4)": no_cover_sqlalchemy_lt_1_4 + "package_version('sqlalchemy') >= (1, 4)": no_cover_sqlalchemy_gte_1_4 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..2bcd70e --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 88 diff --git a/sqlalchemy_filters/models.py b/sqlalchemy_filters/models.py index b60b35b..b4f3084 100644 --- a/sqlalchemy_filters/models.py +++ b/sqlalchemy_filters/models.py @@ -58,7 +58,7 @@ def _is_hybrid_method(orm_descriptor): return orm_descriptor.extension_type == symbol('HYBRID_METHOD') -def get_model_from_table(table): # pragma: sqlalchemy_gte_1_4 +def get_model_from_table(table): # pragma: no_cover_sqlalchemy_lt_1_4 """Resolve model class from table object""" for registry in mapperlib._all_registries(): @@ -80,9 +80,9 @@ def get_query_models(query): models = [col_desc['entity'] for col_desc in query.column_descriptions] # account joined entities - if sqlalchemy_version_lt('1.4'): # pragma: sqlalchemy_lt_1_4 + if sqlalchemy_version_lt('1.4'): # pragma: no_cover_sqlalchemy_gte_1_4 models.extend(mapper.class_ for mapper in query._join_entities) - else: # pragma: sqlalchemy_gte_1_4 + else: # pragma: no_cover_sqlalchemy_lt_1_4 try: models.extend( mapper.class_ @@ -99,14 +99,14 @@ def get_query_models(query): # account also query.select_from entities model_class = None - if sqlalchemy_version_lt('1.4'): # pragma: sqlalchemy_lt_1_4 + if sqlalchemy_version_lt('1.4'): # pragma: no_cover_sqlalchemy_gte_1_4 if query._select_from_entity: model_class = ( query._select_from_entity if sqlalchemy_version_lt('1.1') else query._select_from_entity.class_ ) - else: # pragma: sqlalchemy_gte_1_4 + else: # pragma: no_cover_sqlalchemy_lt_1_4 if query._from_obj: model_class = get_model_from_table(query._from_obj[0]) if model_class and (model_class not in models): @@ -199,9 +199,9 @@ def auto_join(query, *model_names): model = get_model_class_by_name(model_registry, name) if model and (model not in get_query_models(query).values()): try: - if sqlalchemy_version_lt('1.4'): # pragma: sqlalchemy_lt_1_4 + if sqlalchemy_version_lt('1.4'): # pragma: no_cover_sqlalchemy_gte_1_4 query = query.join(model) - else: # pragma: sqlalchemy_gte_1_4 + else: # pragma: no_cover_sqlalchemy_lt_1_4 # https://docs.sqlalchemy.org/en/14/changelog/migration_14.html # Many Core and ORM statement objects now perform much of # their construction and validation in the compile phase From 2eb0761bd8917178bf68ca38dde0365ae6e53f87 Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 13 Apr 2023 10:42:06 +0100 Subject: [PATCH 18/26] improve coverage --- test/interface/test_models.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/interface/test_models.py b/test/interface/test_models.py index 8910efb..9a2d3f5 100644 --- a/test/interface/test_models.py +++ b/test/interface/test_models.py @@ -5,12 +5,21 @@ from sqlalchemy_filters.exceptions import BadSpec, BadQuery from sqlalchemy_filters.models import ( auto_join, get_default_model, get_query_models, get_model_class_by_name, - get_model_from_spec, sqlalchemy_version_lt + get_model_from_spec, sqlalchemy_version_lt, get_model_from_table ) from test.models import Base, Bar, Foo, Qux class TestGetQueryModels(object): + def test_returns_none_for_unknown_table(self): + + class FakeUnmappedTable: + pass + + table = FakeUnmappedTable() + + result = get_model_from_table(table) + assert result is None def test_query_with_no_models(self, session): query = session.query() From 5e0f731575cd855b0e9133db9abe55921e9a833a Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 13 Apr 2023 10:44:18 +0100 Subject: [PATCH 19/26] fix github actions --- .github/workflows/tests.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fe8cb61..26808d7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -54,12 +54,12 @@ jobs: - {python: '3.9', tox: "py39-sqlalchemy1.4"} - {python: '3.9', tox: "py39-sqlalchemylatest"} - - {python: '3.10', tox: "py39-sqlalchemy1.0"} - - {python: '3.10', tox: "py39-sqlalchemy1.1"} - - {python: '3.10', tox: "py39-sqlalchemy1.2"} - - {python: '3.10', tox: "py39-sqlalchemy1.3"} - - {python: '3.10', tox: "py39-sqlalchemy1.4"} - - {python: '3.10', tox: "py39-sqlalchemylatest"} + - {python: '3.10', tox: "py310-sqlalchemy1.0"} + - {python: '3.10', tox: "py310-sqlalchemy1.1"} + - {python: '3.10', tox: "py310-sqlalchemy1.2"} + - {python: '3.10', tox: "py310-sqlalchemy1.3"} + - {python: '3.10', tox: "py310-sqlalchemy1.4"} + - {python: '3.10', tox: "py310-sqlalchemylatest"} steps: - uses: actions/checkout@v2 From eeb35d8b5ed5a20c1565822f9ca6ff7b74ad0ce4 Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 13 Apr 2023 10:51:16 +0100 Subject: [PATCH 20/26] skip testing get_model_from_table when not used --- test/interface/test_models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/interface/test_models.py b/test/interface/test_models.py index 9a2d3f5..2ef7f88 100644 --- a/test/interface/test_models.py +++ b/test/interface/test_models.py @@ -11,6 +11,9 @@ class TestGetQueryModels(object): + @pytest.mark.skipif( + sqlalchemy_version_lt('1.4'), reason='tests sqlalchemy 1.4 code' + ) def test_returns_none_for_unknown_table(self): class FakeUnmappedTable: From d0dc3297cca1ddc3533617e23db67ef1fbd05efb Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 13 Apr 2023 10:51:39 +0100 Subject: [PATCH 21/26] always use newer sqlalchemy-utils --- setup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 06d99f3..29212fb 100644 --- a/setup.py +++ b/setup.py @@ -25,8 +25,7 @@ 'dev': [ 'pytest>=4.6.9', 'coverage~=5.0.4', - # for sqlalchemy1.4 >= 0.37 is required - 'sqlalchemy-utils>=0.36.3', + 'sqlalchemy-utils>=0.37', 'flake8', 'restructuredtext-lint', 'Pygments', From 51f011f9aa6c8898aa399391164e4e017d35898d Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 13 Apr 2023 10:59:43 +0100 Subject: [PATCH 22/26] sqlalchemy <= 1.1 error for py3.10 --- .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 26808d7..1d1ab19 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -54,8 +54,8 @@ jobs: - {python: '3.9', tox: "py39-sqlalchemy1.4"} - {python: '3.9', tox: "py39-sqlalchemylatest"} - - {python: '3.10', tox: "py310-sqlalchemy1.0"} - - {python: '3.10', tox: "py310-sqlalchemy1.1"} + # sqlalchemy <= 1.1 error with: + # AttributeError: module 'collections' has no attribute 'MutableMapping' - {python: '3.10', tox: "py310-sqlalchemy1.2"} - {python: '3.10', tox: "py310-sqlalchemy1.3"} - {python: '3.10', tox: "py310-sqlalchemy1.4"} From 364f229fc17fd49f3f173e279eb5aa93f05d675c Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 13 Apr 2023 11:10:56 +0100 Subject: [PATCH 23/26] set python_requires='>=3.7' --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 29212fb..5dc0a57 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,7 @@ author_email='wearehiring@student.com', url='https://github.com/juliotrigo/sqlalchemy-filters', packages=find_packages(exclude=['test', 'test.*']), + python_requires='>=3.7', install_requires=['sqlalchemy>=1.0.16', 'six>=1.10.0'], extras_require={ 'dev': [ From 7746db8c94c56da7c8911e91722d68a3e1173346 Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 13 Apr 2023 11:14:56 +0100 Subject: [PATCH 24/26] don't test broken versions --- .github/workflows/tests.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1d1ab19..f831037 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,33 +33,32 @@ jobs: fail-fast: false matrix: include: + # sqlalchemylatest (i.e. > 2.0.0) is not yet supported + # for any version of python + - {python: '3.7', tox: "py37-sqlalchemy1.0"} - {python: '3.7', tox: "py37-sqlalchemy1.1"} - {python: '3.7', tox: "py37-sqlalchemy1.2"} - {python: '3.7', tox: "py37-sqlalchemy1.3"} - {python: '3.7', tox: "py37-sqlalchemy1.4"} - - {python: '3.7', tox: "py37-sqlalchemylatest"} - {python: '3.8', tox: "py38-sqlalchemy1.0"} - {python: '3.8', tox: "py38-sqlalchemy1.1"} - {python: '3.8', tox: "py38-sqlalchemy1.2"} - {python: '3.8', tox: "py38-sqlalchemy1.3"} - {python: '3.8', tox: "py38-sqlalchemy1.4"} - - {python: '3.8', tox: "py38-sqlalchemylatest"} - {python: '3.9', tox: "py39-sqlalchemy1.0"} - {python: '3.9', tox: "py39-sqlalchemy1.1"} - {python: '3.9', tox: "py39-sqlalchemy1.2"} - {python: '3.9', tox: "py39-sqlalchemy1.3"} - {python: '3.9', tox: "py39-sqlalchemy1.4"} - - {python: '3.9', tox: "py39-sqlalchemylatest"} - # sqlalchemy <= 1.1 error with: + # python3.10 with sqlalchemy <= 1.1 errors with: # AttributeError: module 'collections' has no attribute 'MutableMapping' - {python: '3.10', tox: "py310-sqlalchemy1.2"} - {python: '3.10', tox: "py310-sqlalchemy1.3"} - {python: '3.10', tox: "py310-sqlalchemy1.4"} - - {python: '3.10', tox: "py310-sqlalchemylatest"} steps: - uses: actions/checkout@v2 From a8fdd354e5da118dc755d6fbabc910155c01b470 Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 13 Apr 2023 11:20:07 +0100 Subject: [PATCH 25/26] update status badge --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index cf8c880..2dd9cda 100644 --- a/README.rst +++ b/README.rst @@ -16,8 +16,8 @@ SQLAlchemy filters .. image:: https://img.shields.io/pypi/format/sqlalchemy-filters.svg :target: https://pypi.org/project/sqlalchemy-filters/ -.. image:: https://travis-ci.org/juliotrigo/sqlalchemy-filters.svg?branch=master - :target: https://travis-ci.org/juliotrigo/sqlalchemy-filters +.. image:: https://github.com/juliotrigo/sqlalchemy-filters/actions/workflows/tests.yml/badge.svg + :target: https://github.com/juliotrigo/sqlalchemy-filters/actions Filtering From c53c8da87f12b1c407919e9714df003d009bfa42 Mon Sep 17 00:00:00 2001 From: Tom V Date: Thu, 13 Apr 2023 12:08:48 +0100 Subject: [PATCH 26/26] update changelog --- CHANGELOG.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2cad1f1..4cf510f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,11 +7,11 @@ versions, where semantic versioning is used: *major.minor.patch*. 0.13.0 ------ -Released TBD +Released 2023-04-13 * Add support for SQLAlchemy 1.4 (#69) thanks to @bodik -* Add support for Python 3.9 - +* Add support for Python 3.9 & Python 3.10 +* Drop support for Python 2.7, 3.5 & 3.6 0.12.0 ------