diff --git a/mealie/core/security/providers/auth_provider.py b/mealie/core/security/providers/auth_provider.py index ce10afd7d..d6dc84128 100644 --- a/mealie/core/security/providers/auth_provider.py +++ b/mealie/core/security/providers/auth_provider.py @@ -1,6 +1,5 @@ import abc from datetime import UTC, datetime, timedelta -from typing import Generic, TypeVar import jwt from sqlalchemy.orm.session import Session @@ -13,10 +12,8 @@ ALGORITHM = "HS256" ISS = "mealie" remember_me_duration = timedelta(days=14) -T = TypeVar("T") - -class AuthProvider(Generic[T], metaclass=abc.ABCMeta): +class AuthProvider[T](metaclass=abc.ABCMeta): """Base Authentication Provider interface""" def __init__(self, session: Session, data: T) -> None: diff --git a/mealie/repos/repository_generic.py b/mealie/repos/repository_generic.py index 35c3616a6..f8ab3f1b4 100644 --- a/mealie/repos/repository_generic.py +++ b/mealie/repos/repository_generic.py @@ -4,7 +4,7 @@ import random from collections.abc import Iterable from datetime import UTC, datetime from math import ceil -from typing import Any, Generic, TypeVar +from typing import Any from fastapi import HTTPException from pydantic import UUID4, BaseModel @@ -28,18 +28,13 @@ from mealie.schema.response.query_search import SearchFilter from ._utils import NOT_SET, NotSet -Schema = TypeVar("Schema", bound=MealieModel) -Model = TypeVar("Model", bound=SqlAlchemyBase) -T = TypeVar("T", bound="RepositoryGeneric") - - -class RepositoryGeneric(Generic[Schema, Model]): +class RepositoryGeneric[Schema: MealieModel, Model: SqlAlchemyBase]: """A Generic BaseAccess Model method to perform common operations on the database Args: - Generic ([Schema]): Represents the Pydantic Model - Generic ([Model]): Represents the SqlAlchemyModel Model + Schema: Represents the Pydantic Model + Model: Represents the SqlAlchemyModel Model """ session: Session @@ -467,7 +462,7 @@ class RepositoryGeneric(Generic[Schema, Model]): return search_filter.filter_query_by_search(query, schema, self.model) -class GroupRepositoryGeneric(RepositoryGeneric[Schema, Model]): +class GroupRepositoryGeneric[Schema: MealieModel, Model: SqlAlchemyBase](RepositoryGeneric[Schema, Model]): def __init__( self, session: Session, @@ -483,7 +478,7 @@ class GroupRepositoryGeneric(RepositoryGeneric[Schema, Model]): self._group_id = group_id if group_id else None -class HouseholdRepositoryGeneric(RepositoryGeneric[Schema, Model]): +class HouseholdRepositoryGeneric[Schema: MealieModel, Model: SqlAlchemyBase](RepositoryGeneric[Schema, Model]): def __init__( self, session: Session, diff --git a/mealie/routes/_base/controller.py b/mealie/routes/_base/controller.py index f7e13f34b..e1c03c160 100644 --- a/mealie/routes/_base/controller.py +++ b/mealie/routes/_base/controller.py @@ -6,20 +6,18 @@ See their repository for details -> https://github.com/dmontagu/fastapi-utils import inspect from collections.abc import Callable -from typing import Any, ClassVar, ForwardRef, TypeVar, cast, get_origin, get_type_hints +from typing import Any, ClassVar, ForwardRef, cast, get_origin, get_type_hints from fastapi import APIRouter, Depends from fastapi.routing import APIRoute from starlette.routing import Route, WebSocketRoute -T = TypeVar("T") - CBV_CLASS_KEY = "__cbv_class__" INCLUDE_INIT_PARAMS_KEY = "__include_init_params__" RETURN_TYPES_FUNC_KEY = "__return_types_func__" -def controller(router: APIRouter, *urls: str) -> Callable[[type[T]], type[T]]: +def controller[T](router: APIRouter, *urls: str) -> Callable[[type[T]], type[T]]: """ This function returns a decorator that converts the decorated into a class-based view for the provided router. Any methods of the decorated class that are decorated as endpoints using the router provided to this function @@ -36,7 +34,7 @@ def controller(router: APIRouter, *urls: str) -> Callable[[type[T]], type[T]]: return decorator -def _cbv(router: APIRouter, cls: type[T], *urls: str, instance: Any | None = None) -> type[T]: +def _cbv[T](router: APIRouter, cls: type[T], *urls: str, instance: Any | None = None) -> type[T]: """ Replaces any methods of the provided class `cls` that are endpoints of routes in `router` with updated function calls that will properly inject an instance of `cls`. diff --git a/mealie/routes/_base/mixins.py b/mealie/routes/_base/mixins.py index 95ec01e4a..e69457c78 100644 --- a/mealie/routes/_base/mixins.py +++ b/mealie/routes/_base/mixins.py @@ -1,6 +1,5 @@ from collections.abc import Callable from logging import Logger -from typing import Generic, TypeVar import sqlalchemy.exc from fastapi import HTTPException, status @@ -9,12 +8,8 @@ from pydantic import UUID4, BaseModel from mealie.repos.repository_generic import RepositoryGeneric from mealie.schema.response import ErrorResponse -C = TypeVar("C", bound=BaseModel) -R = TypeVar("R", bound=BaseModel) -U = TypeVar("U", bound=BaseModel) - -class HttpRepo(Generic[C, R, U]): +class HttpRepo[C: BaseModel, R: BaseModel, U: BaseModel]: """ The HttpRepo[C, R, U] class is a mixin class that provides a common set of methods for CRUD operations. This class is intended to be used in a composition pattern where a class has a mixin property. For example: diff --git a/mealie/schema/mapper.py b/mealie/schema/mapper.py index 6aee59ef6..77c9d4793 100644 --- a/mealie/schema/mapper.py +++ b/mealie/schema/mapper.py @@ -1,12 +1,7 @@ -from typing import TypeVar - from pydantic import BaseModel -T = TypeVar("T", bound=BaseModel) -U = TypeVar("U", bound=BaseModel) - -def mapper(source: U, dest: T, **_) -> T: +def mapper[U: BaseModel, T: BaseModel](source: U, dest: T, **_) -> T: """ Map a source model to a destination model. Only top-level fields are mapped. """ @@ -18,7 +13,7 @@ def mapper(source: U, dest: T, **_) -> T: return dest -def cast(source: U, dest: type[T], **kwargs) -> T: +def cast[U: BaseModel, T: BaseModel](source: U, dest: type[T], **kwargs) -> T: create_data = {field: getattr(source, field) for field in source.model_fields if field in dest.model_fields} create_data.update(kwargs or {}) return dest(**create_data) diff --git a/mealie/schema/response/pagination.py b/mealie/schema/response/pagination.py index 67b69a103..2fb687575 100644 --- a/mealie/schema/response/pagination.py +++ b/mealie/schema/response/pagination.py @@ -1,5 +1,5 @@ import enum -from typing import Annotated, Any, Generic, TypeVar +from typing import Annotated, Any from urllib.parse import parse_qs, urlencode, urlsplit, urlunsplit from humps import camelize @@ -8,8 +8,6 @@ from pydantic_core.core_schema import ValidationInfo from mealie.schema._mealie import MealieModel -DataT = TypeVar("DataT", bound=BaseModel) - class OrderDirection(str, enum.Enum): asc = "asc" @@ -50,7 +48,7 @@ class PaginationQuery(RequestQuery): per_page: int = 50 -class PaginationBase(BaseModel, Generic[DataT]): +class PaginationBase[DataT: BaseModel](BaseModel): page: int = 1 per_page: int = 10 total: int = 0 diff --git a/mealie/schema/user/user.py b/mealie/schema/user/user.py index b05c5e8f0..b0104421a 100644 --- a/mealie/schema/user/user.py +++ b/mealie/schema/user/user.py @@ -1,6 +1,6 @@ from datetime import UTC, datetime, timedelta from pathlib import Path -from typing import Annotated, Any, Generic, TypeVar +from typing import Annotated, Any from uuid import UUID from pydantic import UUID4, BaseModel, ConfigDict, Field, StringConstraints, field_validator @@ -20,7 +20,6 @@ from mealie.schema.response.pagination import PaginationBase from ...db.models.group import Group from ..recipe import CategoryBase -DataT = TypeVar("DataT", bound=BaseModel) DEFAULT_INTEGRATION_ID = "generic" settings = get_app_settings() @@ -102,7 +101,7 @@ class UserRatingOut(UserRatingCreate): ] -class UserRatings(BaseModel, Generic[DataT]): +class UserRatings[DataT: BaseModel](BaseModel): ratings: list[DataT]