Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,3 @@ exclude_also =
directory = reports/coverage_html/
skip_covered = False
show_contexts = True

[xml]
output = reports/coverage.xml
20 changes: 10 additions & 10 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ jobs:
runs-on: ubuntu-24.04

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

# We use Python 3.10 here because it's the minimum Python version supported by this library.
- name: Setup Python 3.10
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: 3.10
python-version: '3.10'

- name: Install dependencies
run: pip install --upgrade pip build
Expand All @@ -28,7 +28,7 @@ jobs:
run: python -m build

- name: Upload build artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: dist_packages
path: dist/
Expand All @@ -40,15 +40,15 @@ jobs:
runs-on: ubuntu-24.04

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Setup Python 3.10
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: 3.10
python-version: '3.10'

- name: Download build artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: dist_packages
path: dist/
Expand All @@ -66,7 +66,7 @@ jobs:

steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: dist_packages
path: dist/
Expand All @@ -78,7 +78,7 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}

- name: Publish package to PyPI
uses: pypa/gh-action-pypi-publish@v1.13.0
uses: pypa/gh-action-pypi-publish@v1.14.0
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
15 changes: 6 additions & 9 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@

name: Unit tests

# TODO: Remove dev-mypy after merging it into main.
on:
push:
branches:
- main
- dev-mypy
pull_request:
branches:
- main
- dev-mypy

jobs:
test:
Expand All @@ -32,10 +29,10 @@ jobs:
- '2.0'

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Setup Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}

Expand All @@ -47,7 +44,7 @@ jobs:
run: tox run -e clean,py-sqlalchemy${{ matrix.sqlalchemy-version }},report,flake8,mypy -- --junit-xml=reports/pytest_${{ matrix.python-version }}_sqlalchemy${{ matrix.sqlalchemy-version }}.xml

- name: Upload test result artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
if: success() || failure()
with:
name: pytest-results-${{ matrix.python-version }}-sqlalchemy${{ matrix.sqlalchemy-version }}
Expand All @@ -64,17 +61,17 @@ jobs:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Download test result artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
path: reports/
pattern: pytest-results-*
merge-multiple: true

- name: Publish unit test reports
uses: dorny/test-reporter@v2.1.1
uses: dorny/test-reporter@v3.0.0
if: success() || failure()
with:
name: Pytest Report
Expand Down
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ build:
tox:
tox run

# Run tox suite with the latest installed Python version and SQLAlchemy 2.0
.PHONY: tox-latest
tox-latest:
tox run -e clean,py-sqlalchemy2.0,report,flake8,mypy

# Run tox in venv (needs to be installed with `make venv` first)
.PHONY: venv-tox
venv-tox:
Expand Down
1 change: 0 additions & 1 deletion constraints.sqlalchemy1.4.txt

This file was deleted.

1 change: 0 additions & 1 deletion constraints.sqlalchemy2.0.txt

This file was deleted.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"

[tool.setuptools_scm]
write_to = "src/validataclass_search_queries/_version.py"
version_scheme = "post-release"
version_scheme = "no-guess-dev"

[tool.mypy]
files = ["src/", "tests/"]
Expand Down
6 changes: 4 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ classifiers =
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.12
Programming Language :: Python :: 3.13
Programming Language :: Python :: 3.14
Programming Language :: Python :: Implementation :: CPython
Topic :: Software Development :: Libraries :: Python Modules
Topic :: Utilities
Expand All @@ -42,7 +44,7 @@ where = src
# changes how error messages look in a minor version update.
testing =
pytest ~= 9.0.3
pytest-cov ~= 7.0.0
pytest-cov ~= 7.1.0
coverage ~= 7.13.5
flake8 ~= 7.3.0
mypy ~= 1.19.1
mypy ~= 1.20.1
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class AbstractPaginationMixin(ABC):
limit: int | None

@abstractmethod
def apply_pagination_to_query(self, query: Query[T], model_cls: Any) -> Query[T]:
def apply_pagination_to_query(self, query: 'Query[T]', model_cls: Any) -> 'Query[T]':
"""
Applies the pagination parameters to an SQLAlchemy query and returns the new query.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def get_cursor_column(self, model_cls: Any) -> ColumnElement[Any]:
return cast(ColumnElement[Any], getattr(model_cls, self.get_cursor_column_name()))

@override
def apply_pagination_to_query(self, query: Query[T], model_cls: Any) -> Query[T]:
def apply_pagination_to_query(self, query: 'Query[T]', model_cls: Any) -> 'Query[T]':
"""
Applies the pagination parameters to an SQLAlchemy query and returns the new query.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def __init_subclass__(cls, **kwargs: Any):
super().__init_subclass__(**kwargs)

@override
def apply_pagination_to_query(self, query: Query[T], model_cls: Any) -> Query[T]:
def apply_pagination_to_query(self, query: 'Query[T]', model_cls: Any) -> 'Query[T]':
"""
Applies the pagination parameters to an SQLAlchemy query and returns the new query.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,11 @@ def model_cls(self) -> type[T_Model]:
"""
raise NotImplementedError

def _search_and_paginate(self, query: Query[Any], search_query: BaseSearchQuery | None) -> PaginatedResult[T_Model]:
def _search_and_paginate(
self,
query: 'Query[Any]',
search_query: BaseSearchQuery | None,
) -> PaginatedResult[T_Model]:
"""
Apply filters, sorting and pagination to a database query, based on search parameters (usually parsed from
HTTP query parameters), then execute the query and return a paginated list of results.
Expand All @@ -158,7 +162,11 @@ def _search_and_paginate(self, query: Query[Any], search_query: BaseSearchQuery
query = self._order_by_search_query(query, search_query)
return self._paginate_result(query, search_query)

def _filter_by_search_query(self, query: Query[T_Query], search_query: BaseSearchQuery | None) -> Query[T_Query]:
def _filter_by_search_query(
self,
query: 'Query[T_Query]',
search_query: BaseSearchQuery | None,
) -> 'Query[T_Query]':
"""
Apply filters to a database query, based on search parameters (usually parsed from HTTP query parameters).
This does not include sorting or pagination!
Expand All @@ -174,7 +182,11 @@ def _filter_by_search_query(self, query: Query[T_Query], search_query: BaseSearc

return query

def _apply_bound_search_filter(self, query: Query[T_Query], bound_filter: BoundSearchFilter) -> Query[T_Query]:
def _apply_bound_search_filter(
self,
query: 'Query[T_Query]',
bound_filter: BoundSearchFilter,
) -> 'Query[T_Query]':
"""
Apply a single search filter from a `BoundSearchFilter` to a database query with `query.filter(...)`.
Called by `_filter_by_search_query()` for every set search filter.
Expand All @@ -185,7 +197,11 @@ def _apply_bound_search_filter(self, query: Query[T_Query], bound_filter: BoundS
col = getattr(self.model_cls, bound_filter.column_name)
return query.filter(bound_filter.get_sqlalchemy_filter(col))

def _order_by_search_query(self, query: Query[T_Query], search_query: BaseSearchQuery | None) -> Query[T_Query]:
def _order_by_search_query(
self,
query: 'Query[T_Query]',
search_query: BaseSearchQuery | None,
) -> 'Query[T_Query]':
"""
Apply sorting (`query.order_by(...)`) to a database query based on sorting parameters from a search query.

Expand All @@ -197,7 +213,11 @@ def _order_by_search_query(self, query: Query[T_Query], search_query: BaseSearch

return query

def _paginate_result(self, query: Query[Any], search_query: BaseSearchQuery | None) -> PaginatedResult[T_Model]:
def _paginate_result(
self,
query: 'Query[Any]',
search_query: BaseSearchQuery | None,
) -> PaginatedResult[T_Model]:
"""
Apply pagination to a database query based on search parameters, execute the query and return a paginated list
of results.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def apply_sorting_direction(self, column: ColumnElement[T]) -> ColumnElement[T]:
raise NotImplementedError

@abstractmethod
def apply_sorting_to_query(self, query: Query[T], model_cls: Any) -> Query[T]:
def apply_sorting_to_query(self, query: 'Query[T]', model_cls: Any) -> 'Query[T]':
"""
Applies the sorting parameters to an SQLAlchemy query (`query.order_by()`) and returns the new query.

Expand Down
2 changes: 1 addition & 1 deletion src/validataclass_search_queries/sorting/sorting_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def apply_sorting_direction(self, column: ColumnElement[T]) -> ColumnElement[T]:
return column.desc() if self.sorting_direction is SortingDirection.DESC else column.asc()

@override
def apply_sorting_to_query(self, query: Query[T], model_cls: Any) -> Query[T]:
def apply_sorting_to_query(self, query: 'Query[T]', model_cls: Any) -> 'Query[T]':
"""
Applies the sorting parameters to an SQLAlchemy query (`query.order_by()`) and returns the new query.

Expand Down
28 changes: 17 additions & 11 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tox]
minversion = 4.5.1
envlist = clean,py{310,311,312,313,314}-sqlalchemy{1.4,2.0},report,flake8,mypy
requires = tox >= 4.32
envlist = clean, py3{10-14}-sqlalchemy{1.4,2.0}, report, flake8, mypy
skip_missing_interpreters = true

[flake8]
Expand All @@ -9,13 +9,26 @@ exclude = _version.py
ignore =

[testenv]
# Build and install a wheel (speeds up tox runs)
package = wheel
wheel_build_env = .pkg

# Install all testing requirements by default(pytest, mypy etc.)
extras = testing

# Default command: Run pytest
commands = python -m pytest --cov --cov-append {posargs}

# Conditionally install SQLAlchemy version depending on the sqlalchemy factor
constrain_package_deps = true
deps =
sqlalchemy1.4: sqlalchemy >= 1.4, < 2.0
sqlalchemy2.0: sqlalchemy >= 2.0, < 2.1

[testenv:flake8]
commands = flake8 src/ tests/

[testenv:mypy,py{310,311,312,313,314}-mypy]
[testenv:mypy, py3{10-14}-mypy]
commands = mypy {posargs}

[testenv:mypy-debug]
Expand All @@ -25,16 +38,9 @@ commands = mypy --show-traceback --no-incremental {posargs}
[testenv:clean]
commands = coverage erase

[testenv:report,py{310,311,312,313,314}-report]
[testenv:report, py3{10-14}-report]
commands =
coverage html
coverage xml
# TODO: As soon as we've reached 100% test coverage, enforce this test coverage.
# coverage report --fail-under=100
coverage report

[testenv:sqlalchemy1.4]
set_env = PIP_CONSTRAINT=constraints.sqlalchemy1.4.txt

[testenv:sqlalchemy2.0]
set_env = PIP_CONSTRAINT=constraints.sqlalchemy2.0.txt