Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop python 3.8 #1321

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ "3.8" ]
python-version: [ "3.9" ]

steps:
- uses: actions/checkout@v4
Expand All @@ -41,4 +41,4 @@ jobs:

- name: Lint
run: |
poetry run pre-commit run --all-files --show-diff-on-failure
poetry run pre-commit run --all-files --show-diff-on-failure
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ 3.8 ]
python-version: [ "3.9" ]

steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [ "3.8", "3.9", "3.10", "3.11" ] # , "3.12" ]
python-version: [ "3.9", "3.10", "3.11", ] # "3.12" ]
pydantic_v2: [ "true", "false" ]
fastapi_version: [ "<0.112.4", ">=0.112.4" ]

Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ repos:
language: python
name: ruff-format
pass_filenames: false
language_version: python3.8
language_version: python3.9
entry: poetry run ruff format fastapi_pagination tests

- repo: local
Expand All @@ -14,7 +14,7 @@ repos:
language: python
name: ruff
pass_filenames: false
language_version: python3.8
language_version: python3.9
entry: poetry run ruff check --fix --exit-non-zero-on-fix --show-fixes fastapi_pagination tests

- repo: local
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ and use them to generate paginated responses that include the requested subset o
The library supports a variety of pagination strategies, including cursor-based pagination and page-based pagination.

`fastapi-pagination` is built on top of the popular `fastapi` library, and it works with a wide range
of SQL and NoSQL databases frameworks. It also supports async/await syntax and is compatible with Python 3.8 and higher.
of SQL and NoSQL databases frameworks. It also supports async/await syntax and is compatible with Python 3.9 and higher.

Features:

* Simplifies pagination in FastAPI applications.
* Supports a variety of pagination strategies, including cursor-based pagination and page-based pagination
* Works with a wide range of SQL and NoSQL databases frameworks, including `SQLAlchemy`, `Tortoise ORM`, and `PyMongo`.
* Supports async/await syntax.
* Compatible with Python 3.8 and higher.
* Compatible with Python 3.9 and higher.

----

Expand Down
2 changes: 1 addition & 1 deletion docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The documentation is built using [mkdocs](https://www.mkdocs.org/). All document
`fastapi-pagination` uses [poetry](https://python-poetry.org/) for dependency management.
Please, install poetry before continuing.

Minimum supported python version is `3.8`.
Minimum supported python version is `3.9`.


### Step 2: clone the repo
Expand Down
16 changes: 7 additions & 9 deletions fastapi_pagination/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,10 @@
AsyncIterator,
Callable,
ContextManager,
Dict,
Iterator,
Literal,
Optional,
Sequence,
Type,
TypeVar,
cast,
overload,
Expand All @@ -53,7 +51,7 @@
TAbstractParams = TypeVar("TAbstractParams", covariant=True, bound=AbstractParams)

_params_val: ContextVar[AbstractParams] = ContextVar("_params_val")
_page_val: ContextVar[Type[AbstractPage[Any]]] = ContextVar("_page_val", default=Page)
_page_val: ContextVar[type[AbstractPage[Any]]] = ContextVar("_page_val", default=Page)

_rsp_val: ContextVar[Response] = ContextVar("_rsp_val")
_req_val: ContextVar[Request] = ContextVar("_req_val")
Expand Down Expand Up @@ -213,7 +211,7 @@ def set_params(params: AbstractParams) -> ContextManager[None]:
return _ctx_var_with_reset(_params_val, params)


def set_page(page: Type[AbstractPage[Any]]) -> ContextManager[None]:
def set_page(page: type[AbstractPage[Any]]) -> ContextManager[None]:
return _ctx_var_with_reset(_page_val, page)


Expand Down Expand Up @@ -272,7 +270,7 @@ def apply_items_transformer(


def _create_params_dependency(
params: Type[TAbstractParams],
params: type[TAbstractParams],
) -> Callable[[TAbstractParams], AsyncIterator[TAbstractParams]]:
async def _pagination_params(*args: Any, **kwargs: Any) -> AsyncIterator[TAbstractParams]:
val = params(*args, **kwargs)
Expand Down Expand Up @@ -306,8 +304,8 @@ async def _noop_dep() -> None:


def pagination_ctx(
page: Optional[Type[AbstractPage[Any]]] = None,
params: Optional[Type[AbstractParams]] = None,
page: Optional[type[AbstractPage[Any]]] = None,
params: Optional[type[AbstractParams]] = None,
transformer: Optional[ItemsTransformer] = None,
__page_ctx_dep__: bool = False,
) -> Callable[..., AsyncIterator[AbstractParams]]:
Expand Down Expand Up @@ -363,7 +361,7 @@ def _update_route(route: APIRoute) -> None:
if not lenient_issubclass(page_cls, AbstractPage):
return

cls = cast(Type[AbstractPage[Any]], page_cls)
cls = cast(type[AbstractPage[Any]], page_cls)
dep = Depends(pagination_ctx(cls, __page_ctx_dep__=True))

route.dependencies.append(dep)
Expand All @@ -378,7 +376,7 @@ def _update_route(route: APIRoute) -> None:
route.app = request_response(route.get_route_handler())


def _patch_openapi(dst: Dict[str, Any], src: Dict[str, Any]) -> None:
def _patch_openapi(dst: dict[str, Any], src: dict[str, Any]) -> None:
with suppress(KeyError):
dst["paths"].update(src["paths"])

Expand Down
22 changes: 10 additions & 12 deletions fastapi_pagination/bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,11 @@
TYPE_CHECKING,
Any,
ClassVar,
Dict,
Generic,
List,
Optional,
Sequence,
Set,
Tuple,
Type,
TypeVar,
cast,
Expand Down Expand Up @@ -145,15 +143,15 @@ def _check_for_old_sign(func: Any) -> bool:


class AbstractPage(GenericModel, Generic[T], ABC):
__params_type__: ClassVar[Type[AbstractParams]]
__params_type__: ClassVar[type[AbstractParams]]

# used by pydantic v2
__model_aliases__: ClassVar[Dict[str, str]] = {}
__model_aliases__: ClassVar[dict[str, str]] = {}
__model_exclude__: ClassVar[Set[str]] = set()

if TYPE_CHECKING: # only for pydantic v1
__concrete__: ClassVar[bool]
__parameters__: ClassVar[Tuple[Any, ...]]
__parameters__: ClassVar[tuple[Any, ...]]

if IS_PYDANTIC_V2:

Expand Down Expand Up @@ -192,7 +190,7 @@ def __init_subclass__(cls, **kwargs: Any) -> None:
@classmethod
@abstractmethod
def create(
cls: Type[C],
cls: type[C],
*args: Any,
**kwargs: Any,
) -> C:
Expand All @@ -201,13 +199,13 @@ def create(
@classmethod
def _old_customization(
cls,
custom_params: Optional[Type[AbstractParams]] = None,
custom_params: Optional[type[AbstractParams]] = None,
/,
*,
cls_name: Optional[str] = None,
module: Optional[str] = None,
**kwargs: Any,
) -> Type[Self]:
) -> type[Self]:
from .customization import CustomizedPage, PageCustomizer, UseModule, UseName, UseParams, UseParamsFields

args: List[PageCustomizer] = []
Expand All @@ -221,7 +219,7 @@ def _old_customization(
if kwargs:
args.append(UseParamsFields(**kwargs))

return cast(Type[Self], CustomizedPage[(cls, *args)])
return cast(type[Self], CustomizedPage[(cls, *args)])

@classmethod
@deprecated(
Expand All @@ -235,7 +233,7 @@ def with_custom_options(
cls_name: Optional[str] = None,
module: Optional[str] = None,
**kwargs: Any,
) -> Type[Self]:
) -> type[Self]:
return cls._old_customization(
cls_name=cls_name,
module=module or get_caller(),
Expand All @@ -250,11 +248,11 @@ def with_custom_options(
)
def with_params(
cls,
custom_params: Type[AbstractParams],
custom_params: type[AbstractParams],
*,
cls_name: Optional[str] = None,
module: Optional[str] = None,
) -> Type[Self]:
) -> type[Self]:
return cls._old_customization(
custom_params,
cls_name=cls_name,
Expand Down
21 changes: 9 additions & 12 deletions fastapi_pagination/customization.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,9 @@
from typing import (
TYPE_CHECKING,
Any,
Dict,
Generic,
Optional,
Protocol,
Tuple,
Type,
TypeVar,
Union,
no_type_check,
Expand All @@ -46,15 +43,15 @@
from .bases import AbstractPage, AbstractParams, BaseRawParams
from .utils import IS_PYDANTIC_V2, get_caller

ClsNamespace: TypeAlias = Dict[str, Any]
PageCls: TypeAlias = "Type[AbstractPage[Any]]"
ClsNamespace: TypeAlias = dict[str, Any]
PageCls: TypeAlias = "type[AbstractPage[Any]]"

TPage = TypeVar("TPage", bound=PageCls)


@no_type_check
def get_page_bases(cls: Type[TPage]) -> tuple[Type[Any], ...]:
bases: Tuple[Type[Any], ...]
def get_page_bases(cls: type[TPage]) -> tuple[type[Any], ...]:
bases: tuple[type[Any], ...]

if IS_PYDANTIC_V2:
params = cls.__pydantic_generic_metadata__["parameters"]
Expand All @@ -69,7 +66,7 @@ def get_page_bases(cls: Type[TPage]) -> tuple[Type[Any], ...]:
return bases


def new_page_cls(cls: Type[TPage], new_ns: ClsNamespace) -> Type[TPage]:
def new_page_cls(cls: type[TPage], new_ns: ClsNamespace) -> type[TPage]:
return new_class(
new_ns.get("__name__", cls.__name__),
get_page_bases(cls),
Expand Down Expand Up @@ -175,7 +172,7 @@ def to_raw_params(self) -> BaseRawParams:

@dataclass
class UseParams(PageCustomizer):
params: Type[AbstractParams]
params: type[AbstractParams]

def customize_page_ns(self, page_cls: PageCls, ns: ClsNamespace) -> None:
if page_cls.__params_type__ is not ns["__params_type__"]:
Expand All @@ -187,7 +184,7 @@ def customize_page_ns(self, page_cls: PageCls, ns: ClsNamespace) -> None:
ns["__params_type__"] = self.params


def _get_model_fields(cls: Type[BaseModel]) -> ClsNamespace:
def _get_model_fields(cls: type[BaseModel]) -> ClsNamespace:
if IS_PYDANTIC_V2:
return cls.model_fields # type: ignore

Expand Down Expand Up @@ -219,7 +216,7 @@ def _make_field_optional(field: Any) -> Any:


@no_type_check
def _update_params_fields(cls: Type[AbstractParams], fields: ClsNamespace) -> ClsNamespace:
def _update_params_fields(cls: type[AbstractParams], fields: ClsNamespace) -> ClsNamespace:
if not issubclass(cls, BaseModel):
raise TypeError(f"{cls.__name__} must be subclass of BaseModel")

Expand Down Expand Up @@ -325,7 +322,7 @@ def customize_page_ns(self, page_cls: PageCls, ns: ClsNamespace) -> None:
fields_config[name]["alias"] = alias


_RawFieldDef: TypeAlias = Union[Any, Tuple[Any, Any]]
_RawFieldDef: TypeAlias = Union[Any, tuple[Any, Any]]


class UseAdditionalFields(PageCustomizer):
Expand Down
4 changes: 2 additions & 2 deletions fastapi_pagination/ext/async_sqlmodel.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
__all__ = ["paginate"]

from typing import Any, Optional, Type, TypeVar, overload
from typing import Any, Optional, TypeVar, overload

from sqlmodel import SQLModel
from sqlmodel.ext.asyncio.session import AsyncSession
Expand Down Expand Up @@ -59,7 +59,7 @@ async def paginate(
)
async def paginate(
session: AsyncSession,
query: Type[TSQLModel],
query: type[TSQLModel],
params: Optional[AbstractParams] = None,
*,
transformer: Optional[AsyncItemsTransformer] = None,
Expand Down
6 changes: 3 additions & 3 deletions fastapi_pagination/ext/beanie.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

__all__ = ["paginate"]

from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Type, TypeVar, Union
from typing import TYPE_CHECKING, Any, List, Optional, TypeVar, Union

from beanie import Document
from beanie.odm.enums import SortDirection
Expand All @@ -28,8 +28,8 @@ async def paginate(
*,
transformer: Optional[AsyncItemsTransformer] = None,
additional_data: Optional[AdditionalData] = None,
projection_model: Optional[Type[DocumentProjectionType]] = None,
sort: Union[None, str, List[Tuple[str, SortDirection]]] = None,
projection_model: Optional[type[DocumentProjectionType]] = None,
sort: Union[None, str, List[tuple[str, SortDirection]]] = None,
session: Optional[AsyncIOMotorClientSession] = None,
ignore_cache: bool = False,
fetch_links: bool = False,
Expand Down
6 changes: 3 additions & 3 deletions fastapi_pagination/ext/bunnet.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
__all__ = ["paginate"]

from typing import Any, List, Optional, Tuple, Type, TypeVar, Union
from typing import Any, List, Optional, TypeVar, Union

from bunnet import Document
from bunnet.odm.enums import SortDirection
Expand All @@ -22,8 +22,8 @@ def paginate(
*,
transformer: Optional[SyncItemsTransformer] = None,
additional_data: Optional[AdditionalData] = None,
projection_model: Optional[Type[DocumentProjectionType]] = None,
sort: Union[None, str, List[Tuple[str, SortDirection]]] = None,
projection_model: Optional[type[DocumentProjectionType]] = None,
sort: Union[None, str, List[tuple[str, SortDirection]]] = None,
session: Optional[ClientSession] = None,
ignore_cache: bool = False,
fetch_links: bool = False,
Expand Down
6 changes: 3 additions & 3 deletions fastapi_pagination/ext/cassandra.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
__all__ = ["paginate"]

from typing import Any, Dict, Mapping, Optional, Type, TypeVar
from typing import Any, Mapping, Optional, TypeVar

from cassandra.cluster import SimpleStatement
from cassandra.cqlengine import connection
Expand All @@ -15,8 +15,8 @@


def paginate(
model: Type[Model],
query_filter: Optional[Dict[Any, Any]] = None,
model: type[Model],
query_filter: Optional[dict[Any, Any]] = None,
params: Optional[AbstractParams] = None,
*,
transformer: Optional[SyncItemsTransformer] = None,
Expand Down
Loading
Loading