From 82dc586baccffbc390ca52cf645b50cdaf7dfb19 Mon Sep 17 00:00:00 2001 From: Hayden <64056131+hay-kot@users.noreply.github.com> Date: Wed, 30 Nov 2022 20:20:28 -0900 Subject: [PATCH] chores: updates-and-linters (#1868) * switch to ruff * add ruff * run ruff --fix * update ruff * resolve ruff errors * drop isort from CI * fix decorator order --- .flake8 | 10 - .github/workflows/partial-backend.yml | 3 +- .pre-commit-config.yaml | 17 +- .vscode/settings.json | 4 +- makefile | 7 +- mealie/app.py | 13 +- mealie/core/dependencies/dependencies.py | 5 +- mealie/core/root_logger.py | 2 +- mealie/core/settings/db_providers.py | 10 +- mealie/core/settings/settings.py | 17 +- mealie/db/models/_model_utils/__init__.py | 6 + mealie/lang/providers.py | 2 +- mealie/pkgs/dev/__init__.py | 7 - mealie/pkgs/dev/lifespan_tracker.py | 26 -- mealie/pkgs/dev/timer.py | 12 - mealie/repos/repository_generic.py | 2 +- mealie/repos/repository_group.py | 4 +- mealie/repos/repository_recipes.py | 12 +- mealie/routes/_base/controller.py | 12 +- mealie/routes/_base/routers.py | 5 +- mealie/routes/auth/auth.py | 5 +- mealie/routes/comments/__init__.py | 2 - mealie/routes/groups/controller_mealplan.py | 11 +- mealie/routes/groups/controller_migrations.py | 2 +- mealie/routes/media/media_recipe.py | 4 +- mealie/routes/recipe/recipe_crud_routes.py | 2 +- mealie/schema/_mealie/types.py | 4 +- mealie/schema/admin/backup.py | 5 +- mealie/schema/admin/restore.py | 6 +- mealie/schema/admin/settings.py | 4 +- mealie/schema/group/group.py | 4 +- mealie/schema/meal_plan/meal.py | 11 +- mealie/schema/meal_plan/new_meal.py | 5 +- mealie/schema/meal_plan/shopping_list.py | 6 +- mealie/schema/recipe/recipe_asset.py | 4 +- mealie/schema/recipe/recipe_category.py | 2 +- mealie/schema/recipe/recipe_comments.py | 3 +- mealie/schema/recipe/recipe_nutrition.py | 16 +- mealie/schema/recipe/recipe_step.py | 7 +- mealie/schema/recipe/recipe_tool.py | 4 +- mealie/schema/response/query_filter.py | 14 +- mealie/schema/response/responses.py | 6 +- mealie/schema/user/auth.py | 6 +- mealie/schema/user/user.py | 20 +- mealie/services/email/__init__.py | 5 + .../event_bus_service/event_bus_listeners.py | 2 +- .../event_bus_service/event_bus_service.py | 6 +- mealie/services/exporter/_abc_exporter.py | 4 +- .../services/group_services/shopping_lists.py | 11 +- mealie/services/migrations/nextcloud.py | 3 +- .../migrations/utils/migration_alias.py | 3 +- .../parser_services/brute/__init__.py | 4 + .../services/parser_services/brute/process.py | 4 +- .../parser_services/crfpp/pre_processor.py | 3 +- mealie/services/recipe/recipe_data_service.py | 11 +- mealie/services/recipe/recipe_service.py | 9 +- mealie/services/scheduler/runner.py | 10 +- .../services/scheduler/tasks/post_webhooks.py | 3 +- mealie/services/scraper/cleaner.py | 10 +- .../user_services/password_reset_service.py | 2 +- poetry.lock | 331 +++++------------- pyproject.toml | 148 +++++--- 62 files changed, 362 insertions(+), 536 deletions(-) delete mode 100644 .flake8 delete mode 100644 mealie/pkgs/dev/__init__.py delete mode 100644 mealie/pkgs/dev/lifespan_tracker.py delete mode 100644 mealie/pkgs/dev/timer.py diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 57e78331..00000000 --- a/.flake8 +++ /dev/null @@ -1,10 +0,0 @@ -[flake8] -extend-ignore = [ - E501 # Line Length - See Black Config in pyproject.toml - E402 # Import Not at Top of File -] -exclude = _all_models.py - - -per-file-ignores = - __init__.py:F403,F401 diff --git a/.github/workflows/partial-backend.yml b/.github/workflows/partial-backend.yml index a7db5aa7..c2866e33 100644 --- a/.github/workflows/partial-backend.yml +++ b/.github/workflows/partial-backend.yml @@ -66,10 +66,9 @@ jobs: poetry add "psycopg2-binary==2.8.6" if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' || steps.cache-validate.outputs.cache-hit-success != 'true' - - name: Formatting (Black & isort) + - name: Formatting (Black) run: | poetry run black . --check - poetry run isort . --check-only - name: Lint (Flake8) run: | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 03dd88a9..92b1344e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,22 +10,11 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace exclude: ^tests/data/ - - repo: https://github.com/asottile/pyupgrade - rev: v3.1.0 - hooks: - - id: pyupgrade - - repo: https://github.com/pycqa/isort - rev: 5.10.1 - hooks: - - id: isort - name: isort (python) - repo: https://github.com/psf/black rev: 22.3.0 hooks: - id: black - - repo: https://github.com/pycqa/flake8 - rev: "4.0.1" + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: v0.0.149 hooks: - - id: flake8 - additional_dependencies: - - "flake8-print==4.0.0" + - id: ruff diff --git a/.vscode/settings.json b/.vscode/settings.json index d287b996..a91a9b7b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -29,7 +29,7 @@ "i18n-ally.sourceLanguage": "en-US", "python.formatting.provider": "black", "python.linting.enabled": true, - "python.linting.flake8Enabled": true, + "python.linting.flake8Enabled": false, "python.linting.pylintEnabled": false, "python.linting.pylintArgs": ["--rcfile=${workspaceFolder}/.pylintrc"], "python.testing.autoTestDiscoverOnSaveEnabled": false, @@ -44,7 +44,7 @@ "explorer.fileNesting.enabled": true, "explorer.fileNesting.patterns": { "package.json": "package-lock.json, yarn.lock, .eslintrc.js, tsconfig.json, .prettierrc, .editorconfig", - "pyproject.toml": "poetry.lock, alembic.ini, .pylintrc, .flake8", + "pyproject.toml": "poetry.lock, alembic.ini, .pylintrc", "netlify.toml": "runtime.txt", "docker-compose.yml": "Dockerfile, .dockerignore, docker-compose.dev.yml, docker-compose.yml", "README.md": "LICENSE, SECURITY.md" diff --git a/makefile b/makefile index c43b44c8..23cbb860 100644 --- a/makefile +++ b/makefile @@ -84,12 +84,11 @@ backend-typecheck: backend-test: ## 🧪 Run tests quickly with the default Python poetry run pytest -backend-format: ## 🧺 Format, Check and Flake8 - poetry run isort . +backend-format: ## 🧺 Format the codebase poetry run black . -backend-lint: - poetry run flake8 mealie tests +backend-lint: ## 🧹 Lint the codebase (Ruff) + poetry run ruff mealie backend-all: backend-format backend-lint backend-typecheck backend-test ## 🧪 Runs all the backend checks and tests diff --git a/mealie/app.py b/mealie/app.py index 7dca49df..84a34147 100644 --- a/mealie/app.py +++ b/mealie/app.py @@ -15,15 +15,18 @@ settings = get_app_settings() description = f""" Mealie is a web application for managing your recipes, meal plans, and shopping lists. This is the Restful API interactive documentation that can be used to explore the API. If you're justing getting started with -the API and want to get started quickly, you can use the [API Usage | Mealie Docs](https://hay-kot.github.io/mealie/documentation/getting-started/api-usage/) +the API and want to get started quickly, you can use the +[API Usage | Mealie Docs](https://hay-kot.github.io/mealie/documentation/getting-started/api-usage/) as a reference for how to get started. -As of this release {APP_VERSION}, Mealie is still in rapid development and therefore some of these APIs may change from version to version. +As of this release {APP_VERSION}, Mealie is still in rapid development and therefore some of these APIs may +change from version to version. -If you have any questions or comments about mealie, please use the discord server to talk to the developers or other community members. -If you'd like to file an issue, please use the [GitHub Issue Tracker | Mealie](https://github.com/hay-kot/mealie/issues/new/choose) +If you have any questions or comments about mealie, please use the discord server to talk to the developers or other +community members. If you'd like to file an issue, please use the +[GitHub Issue Tracker | Mealie](https://github.com/hay-kot/mealie/issues/new/choose) ## Helpful Links @@ -32,8 +35,6 @@ If you'd like to file an issue, please use the [GitHub Issue Tracker | Mealie](h - [Discord](https://discord.gg/QuStdQGSGK) - [Demo](https://demo.mealie.io) - [Beta](https://demo.mealie.io) - - """ app = FastAPI( diff --git a/mealie/core/dependencies/dependencies.py b/mealie/core/dependencies/dependencies.py index 183f9a56..8477f0cd 100644 --- a/mealie/core/dependencies/dependencies.py +++ b/mealie/core/dependencies/dependencies.py @@ -2,7 +2,6 @@ import shutil import tempfile from collections.abc import AsyncGenerator, Callable, Generator from pathlib import Path -from typing import Optional from uuid import uuid4 from fastapi import Depends, HTTPException, status @@ -116,7 +115,7 @@ def validate_long_live_token(session: Session, client_token: str, user_id: str) raise HTTPException(status.HTTP_401_UNAUTHORIZED) from e -def validate_file_token(token: Optional[str] = None) -> Path: +def validate_file_token(token: str | None = None) -> Path: """ Args: token (Optional[str], optional): _description_. Defaults to None. @@ -143,7 +142,7 @@ def validate_file_token(token: Optional[str] = None) -> Path: return file_path -def validate_recipe_token(token: Optional[str] = None) -> str: +def validate_recipe_token(token: str | None = None) -> str: """ Args: token (Optional[str], optional): _description_. Defaults to None. diff --git a/mealie/core/root_logger.py b/mealie/core/root_logger.py index 518cf01b..a04d7954 100644 --- a/mealie/core/root_logger.py +++ b/mealie/core/root_logger.py @@ -7,7 +7,7 @@ from mealie.core.config import determine_data_dir DATA_DIR = determine_data_dir() -from .config import get_app_settings +from .config import get_app_settings # noqa E402 LOGGER_FILE = DATA_DIR.joinpath("mealie.log") DATE_FORMAT = "%d-%b-%y %H:%M:%S" diff --git a/mealie/core/settings/db_providers.py b/mealie/core/settings/db_providers.py index 08bfc38a..f8f0e40e 100644 --- a/mealie/core/settings/db_providers.py +++ b/mealie/core/settings/db_providers.py @@ -1,17 +1,19 @@ -from abc import ABC, abstractproperty +from abc import ABC, abstractmethod from pathlib import Path from pydantic import BaseModel, BaseSettings, PostgresDsn class AbstractDBProvider(ABC): - @abstractproperty + @property + @abstractmethod def db_url(self) -> str: - pass + ... @property + @abstractmethod def db_url_public(self) -> str: - pass + ... class SQLiteProvider(AbstractDBProvider, BaseModel): diff --git a/mealie/core/settings/settings.py b/mealie/core/settings/settings.py index 3932d823..f22e64c3 100644 --- a/mealie/core/settings/settings.py +++ b/mealie/core/settings/settings.py @@ -1,6 +1,5 @@ import secrets from pathlib import Path -from typing import Optional from pydantic import BaseSettings, NoneStr @@ -54,7 +53,7 @@ class AppSettings(BaseSettings): # Database Configuration DB_ENGINE: str = "sqlite" # Options: 'sqlite', 'postgres' - DB_PROVIDER: Optional[AbstractDBProvider] = None + DB_PROVIDER: AbstractDBProvider | None = None @property def DB_URL(self) -> str | None: @@ -71,13 +70,13 @@ class AppSettings(BaseSettings): # =============================================== # Email Configuration - SMTP_HOST: Optional[str] - SMTP_PORT: Optional[str] = "587" - SMTP_FROM_NAME: Optional[str] = "Mealie" - SMTP_FROM_EMAIL: Optional[str] - SMTP_USER: Optional[str] - SMTP_PASSWORD: Optional[str] - SMTP_AUTH_STRATEGY: Optional[str] = "TLS" # Options: 'TLS', 'SSL', 'NONE' + SMTP_HOST: str | None + SMTP_PORT: str | None = "587" + SMTP_FROM_NAME: str | None = "Mealie" + SMTP_FROM_EMAIL: str | None + SMTP_USER: str | None + SMTP_PASSWORD: str | None + SMTP_AUTH_STRATEGY: str | None = "TLS" # Options: 'TLS', 'SSL', 'NONE' @property def SMTP_ENABLE(self) -> bool: diff --git a/mealie/db/models/_model_utils/__init__.py b/mealie/db/models/_model_utils/__init__.py index 0b643318..367b06c9 100644 --- a/mealie/db/models/_model_utils/__init__.py +++ b/mealie/db/models/_model_utils/__init__.py @@ -1,2 +1,8 @@ from .auto_init import auto_init from .guid import GUID + + +__all__ = [ + "auto_init", + "GUID", +] diff --git a/mealie/lang/providers.py b/mealie/lang/providers.py index c8cbcd54..b263e656 100644 --- a/mealie/lang/providers.py +++ b/mealie/lang/providers.py @@ -17,7 +17,7 @@ class Translator(Protocol): pass -@lru_cache() +@lru_cache def _load_factory() -> i18n.ProviderFactory: return i18n.ProviderFactory( directory=TRANSLATIONS, diff --git a/mealie/pkgs/dev/__init__.py b/mealie/pkgs/dev/__init__.py deleted file mode 100644 index 16550066..00000000 --- a/mealie/pkgs/dev/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -""" -This package containers helpful development tools to be used for development and testing. It shouldn't be used for or imported -in production -""" - -from .lifespan_tracker import * -from .timer import * diff --git a/mealie/pkgs/dev/lifespan_tracker.py b/mealie/pkgs/dev/lifespan_tracker.py deleted file mode 100644 index 86f76d1e..00000000 --- a/mealie/pkgs/dev/lifespan_tracker.py +++ /dev/null @@ -1,26 +0,0 @@ -import time - - -# log_lifetime is a class decorator that logs the creation and destruction of a class -# It is used to track the lifespan of a class during development or testing. -# It SHOULD NOT be used in production code. -def log_lifetime(cls): - class LifeTimeClass(cls): - def __init__(self, *args, **kwargs): - print(f"Creating an instance of {cls.__name__}") # noqa: T001 - self.__lifespan_timer_start = time.perf_counter() - - super().__init__(*args, **kwargs) - - def __del__(self): - toc = time.perf_counter() - print(f"Downloaded the tutorial in {toc - self.__lifespan_timer_start:0.4f} seconds") # noqa: T001 - - print(f"Deleting an instance of {cls.__name__}") # noqa: T001 - - try: - super().__del__() - except AttributeError: - pass - - return LifeTimeClass diff --git a/mealie/pkgs/dev/timer.py b/mealie/pkgs/dev/timer.py deleted file mode 100644 index 42b97994..00000000 --- a/mealie/pkgs/dev/timer.py +++ /dev/null @@ -1,12 +0,0 @@ -import time - - -def timer(func): - def wrapper(*args, **kwargs): - start = time.time() - result = func(*args, **kwargs) - end = time.time() - print(f"{func.__name__} took {end - start} seconds") # noqa: T001 - return result - - return wrapper diff --git a/mealie/repos/repository_generic.py b/mealie/repos/repository_generic.py index 4f185770..8c10cb11 100644 --- a/mealie/repos/repository_generic.py +++ b/mealie/repos/repository_generic.py @@ -283,7 +283,7 @@ class RepositoryGeneric(Generic[Schema, Model]): except ValueError as e: self.logger.error(e) - raise HTTPException(status_code=400, detail=str(e)) + raise HTTPException(status_code=400, detail=str(e)) from e count = query.count() diff --git a/mealie/repos/repository_group.py b/mealie/repos/repository_group.py index a0c9f151..69d51d0d 100644 --- a/mealie/repos/repository_group.py +++ b/mealie/repos/repository_group.py @@ -1,5 +1,3 @@ -from typing import Union - from pydantic import UUID4 from mealie.db.models.group import Group @@ -15,7 +13,7 @@ from .repository_generic import RepositoryGeneric class RepositoryGroup(RepositoryGeneric[GroupInDB, Group]): - def get_by_name(self, name: str, limit=1) -> Union[GroupInDB, Group, None]: + def get_by_name(self, name: str, limit=1) -> GroupInDB | Group | None: dbgroup = self.session.query(self.model).filter_by(**{"name": name}).one_or_none() if dbgroup is None: return None diff --git a/mealie/repos/repository_recipes.py b/mealie/repos/repository_recipes.py index 6f3fed69..790a0b50 100644 --- a/mealie/repos/repository_recipes.py +++ b/mealie/repos/repository_recipes.py @@ -1,5 +1,5 @@ from random import randint -from typing import Any, Optional +from typing import Any from uuid import UUID from pydantic import UUID4 @@ -135,10 +135,10 @@ class RepositoryRecipes(RepositoryGeneric[Recipe, RecipeModel]): pagination: PaginationQuery, override=None, load_food=False, - cookbook: Optional[ReadCookBook] = None, - categories: Optional[list[UUID4 | str]] = None, - tags: Optional[list[UUID4 | str]] = None, - tools: Optional[list[UUID4 | str]] = None, + cookbook: ReadCookBook | None = None, + categories: list[UUID4 | str] | None = None, + tags: list[UUID4 | str] | None = None, + tools: list[UUID4 | str] | None = None, ) -> RecipePagination: q = self.session.query(self.model) @@ -307,7 +307,7 @@ class RepositoryRecipes(RepositoryGeneric[Recipe, RecipeModel]): .limit(limit) ] - def get_by_slug(self, group_id: UUID4, slug: str, limit=1) -> Optional[Recipe]: + def get_by_slug(self, group_id: UUID4, slug: str, limit=1) -> Recipe | None: dbrecipe = ( self.session.query(RecipeModel) .filter(RecipeModel.group_id == group_id, RecipeModel.slug == slug) diff --git a/mealie/routes/_base/controller.py b/mealie/routes/_base/controller.py index 8d110e43..551ec6d9 100644 --- a/mealie/routes/_base/controller.py +++ b/mealie/routes/_base/controller.py @@ -5,7 +5,7 @@ See their repository for details -> https://github.com/dmontagu/fastapi-utils """ import inspect from collections.abc import Callable -from typing import Any, TypeVar, Union, cast, get_type_hints +from typing import Any, TypeVar, cast, get_type_hints from fastapi import APIRouter, Depends from fastapi.routing import APIRoute @@ -92,8 +92,8 @@ def _init_cbv(cls: type[Any], instance: Any = None) -> None: else: old_init(self, *args, **kwargs) - setattr(cls, "__signature__", new_signature) - setattr(cls, "__init__", new_init) + cls.__signature__ = new_signature + cls.__init__ = new_init setattr(cls, CBV_CLASS_KEY, True) for name in private_attributes: @@ -122,7 +122,7 @@ def _register_endpoints(router: APIRouter, cls: type[Any], *urls: str) -> None: } prefix_length = len(router.prefix) - routes_to_append: list[tuple[int, Union[Route, WebSocketRoute]]] = [] + routes_to_append: list[tuple[int, Route | WebSocketRoute]] = [] for _, func in function_members: index_route = numbered_routes_by_endpoint.get(func) @@ -177,7 +177,7 @@ def _allocate_routes_by_method_name(router: APIRouter, url: str, function_member api_resource(func) -def _update_cbv_route_endpoint_signature(cls: type[Any], route: Union[Route, WebSocketRoute]) -> None: +def _update_cbv_route_endpoint_signature(cls: type[Any], route: Route | WebSocketRoute) -> None: """ Fixes the endpoint signature for a cbv route to ensure FastAPI performs dependency injection properly. """ @@ -191,4 +191,4 @@ def _update_cbv_route_endpoint_signature(cls: type[Any], route: Union[Route, Web ] new_signature = old_signature.replace(parameters=new_parameters) - setattr(route.endpoint, "__signature__", new_signature) + route.endpoint.__signature__ = new_signature diff --git a/mealie/routes/_base/routers.py b/mealie/routes/_base/routers.py index 4efdac95..e4b7d446 100644 --- a/mealie/routes/_base/routers.py +++ b/mealie/routes/_base/routers.py @@ -3,7 +3,6 @@ import json from collections.abc import Callable from enum import Enum from json.decoder import JSONDecodeError -from typing import Optional, Union from fastapi import APIRouter, Depends, Request, Response from fastapi.routing import APIRoute @@ -14,14 +13,14 @@ from mealie.core.dependencies import get_admin_user, get_current_user class AdminAPIRouter(APIRouter): """Router for functions to be protected behind admin authentication""" - def __init__(self, tags: Optional[list[Union[str, Enum]]] = None, prefix: str = "", **kwargs): + def __init__(self, tags: list[str | Enum] | None = None, prefix: str = "", **kwargs): super().__init__(tags=tags, prefix=prefix, dependencies=[Depends(get_admin_user)], **kwargs) class UserAPIRouter(APIRouter): """Router for functions to be protected behind user authentication""" - def __init__(self, tags: Optional[list[Union[str, Enum]]] = None, prefix: str = "", **kwargs): + def __init__(self, tags: list[str | Enum] | None = None, prefix: str = "", **kwargs): super().__init__(tags=tags, prefix=prefix, dependencies=[Depends(get_current_user)], **kwargs) diff --git a/mealie/routes/auth/auth.py b/mealie/routes/auth/auth.py index 3c69ecd9..aeba6b0c 100644 --- a/mealie/routes/auth/auth.py +++ b/mealie/routes/auth/auth.py @@ -1,5 +1,4 @@ from datetime import timedelta -from typing import Optional from fastapi import APIRouter, Depends, Form, status from fastapi.exceptions import HTTPException @@ -27,8 +26,8 @@ class CustomOAuth2Form(OAuth2PasswordRequestForm): password: str = Form(...), remember_me: bool = Form(False), scope: str = Form(""), - client_id: Optional[str] = Form(None), - client_secret: Optional[str] = Form(None), + client_id: str | None = Form(None), + client_secret: str | None = Form(None), ): self.grant_type = grant_type self.username = username diff --git a/mealie/routes/comments/__init__.py b/mealie/routes/comments/__init__.py index 7ab13a78..9d357b3d 100644 --- a/mealie/routes/comments/__init__.py +++ b/mealie/routes/comments/__init__.py @@ -1,10 +1,8 @@ from functools import cached_property -from typing import Type from fastapi import APIRouter, Depends, HTTPException from pydantic import UUID4 -from mealie.core.exceptions import mealie_registered_exceptions from mealie.routes._base.base_controllers import BaseUserController from mealie.routes._base.controller import controller from mealie.routes._base.mixins import HttpRepo diff --git a/mealie/routes/groups/controller_mealplan.py b/mealie/routes/groups/controller_mealplan.py index f312b9d8..e4699096 100644 --- a/mealie/routes/groups/controller_mealplan.py +++ b/mealie/routes/groups/controller_mealplan.py @@ -1,6 +1,5 @@ from datetime import date from functools import cached_property -from typing import Optional from fastapi import APIRouter, Depends, HTTPException @@ -84,15 +83,17 @@ class GroupMealplanController(BaseUserController): return self.mixins.create_one( SavePlanEntry(date=data.date, entry_type=data.entry_type, recipe_id=recipe.id, group_id=self.group_id) ) - except IndexError: - raise HTTPException(status_code=404, detail=ErrorResponse.respond(message="No recipes match your rules")) + except IndexError as e: + raise HTTPException( + status_code=404, detail=ErrorResponse.respond(message="No recipes match your rules") + ) from e @router.get("", response_model=PlanEntryPagination) def get_all( self, q: PaginationQuery = Depends(PaginationQuery), - start_date: Optional[date] = None, - end_date: Optional[date] = None, + start_date: date | None = None, + end_date: date | None = None, ): # merge start and end dates into pagination query only if either is provided if start_date or end_date: diff --git a/mealie/routes/groups/controller_migrations.py b/mealie/routes/groups/controller_migrations.py index 846df502..91804c4b 100644 --- a/mealie/routes/groups/controller_migrations.py +++ b/mealie/routes/groups/controller_migrations.py @@ -45,7 +45,7 @@ class GroupMigrationController(BaseUserController): migrator: BaseMigrator - match migration_type: + match migration_type: # noqa match not supported by ruff case SupportedMigrations.chowdown: migrator = ChowdownMigrator(**args) case SupportedMigrations.mealie_alpha: diff --git a/mealie/routes/media/media_recipe.py b/mealie/routes/media/media_recipe.py index ddd6f001..d0f152a6 100644 --- a/mealie/routes/media/media_recipe.py +++ b/mealie/routes/media/media_recipe.py @@ -41,5 +41,5 @@ async def get_recipe_asset(recipe_id: UUID4, file_name: str): try: return FileResponse(file) - except Exception: - raise HTTPException(status.HTTP_404_NOT_FOUND) + except Exception as e: + raise HTTPException(status.HTTP_404_NOT_FOUND) from e diff --git a/mealie/routes/recipe/recipe_crud_routes.py b/mealie/routes/recipe/recipe_crud_routes.py index 734b52e7..ebb55dad 100644 --- a/mealie/routes/recipe/recipe_crud_routes.py +++ b/mealie/routes/recipe/recipe_crud_routes.py @@ -140,7 +140,7 @@ router = UserAPIRouter(prefix="/recipes", tags=["Recipe: CRUD"], route_class=Mea @controller(router) class RecipeController(BaseRecipeController): def handle_exceptions(self, ex: Exception) -> None: - match type(ex): + match type(ex): # noqa match statement not supported case exceptions.PermissionDenied: self.logger.error("Permission Denied on recipe controller action") raise HTTPException(status_code=403, detail=ErrorResponse.respond(message="Permission Denied")) diff --git a/mealie/schema/_mealie/types.py b/mealie/schema/_mealie/types.py index 211b44da..0d21c7a9 100644 --- a/mealie/schema/_mealie/types.py +++ b/mealie/schema/_mealie/types.py @@ -1,3 +1 @@ -from typing import Optional - -NoneFloat = Optional[float] +NoneFloat = float | None diff --git a/mealie/schema/admin/backup.py b/mealie/schema/admin/backup.py index 03d7b586..c59cd40f 100644 --- a/mealie/schema/admin/backup.py +++ b/mealie/schema/admin/backup.py @@ -1,5 +1,4 @@ from datetime import datetime -from typing import Optional from pydantic import BaseModel @@ -20,9 +19,9 @@ class ImportJob(BackupOptions): class CreateBackup(BaseModel): - tag: Optional[str] + tag: str | None options: BackupOptions - templates: Optional[list[str]] + templates: list[str] | None class BackupFile(BaseModel): diff --git a/mealie/schema/admin/restore.py b/mealie/schema/admin/restore.py index 4e716409..4983f8b3 100644 --- a/mealie/schema/admin/restore.py +++ b/mealie/schema/admin/restore.py @@ -1,16 +1,14 @@ -from typing import Optional - from pydantic.main import BaseModel class ImportBase(BaseModel): name: str status: bool - exception: Optional[str] + exception: str | None class RecipeImport(ImportBase): - slug: Optional[str] + slug: str | None class CommentImport(ImportBase): diff --git a/mealie/schema/admin/settings.py b/mealie/schema/admin/settings.py index 83fadbbb..baef0b7e 100644 --- a/mealie/schema/admin/settings.py +++ b/mealie/schema/admin/settings.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import validator from slugify import slugify @@ -10,7 +8,7 @@ from ..recipe.recipe_category import RecipeCategoryResponse class CustomPageBase(MealieModel): name: str - slug: Optional[str] + slug: str | None position: int categories: list[RecipeCategoryResponse] = [] diff --git a/mealie/schema/group/group.py b/mealie/schema/group/group.py index 6f8d39cf..ba234c37 100644 --- a/mealie/schema/group/group.py +++ b/mealie/schema/group/group.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import UUID4 from mealie.schema._mealie import MealieModel @@ -10,4 +8,4 @@ from .group_preferences import UpdateGroupPreferences class GroupAdminUpdate(MealieModel): id: UUID4 name: str - preferences: Optional[UpdateGroupPreferences] = None + preferences: UpdateGroupPreferences | None = None diff --git a/mealie/schema/meal_plan/meal.py b/mealie/schema/meal_plan/meal.py index c8a125d5..718bc766 100644 --- a/mealie/schema/meal_plan/meal.py +++ b/mealie/schema/meal_plan/meal.py @@ -1,5 +1,4 @@ from datetime import date -from typing import Optional from pydantic import validator @@ -7,16 +6,16 @@ from mealie.schema._mealie import MealieModel class MealIn(MealieModel): - slug: Optional[str] - name: Optional[str] - description: Optional[str] + slug: str | None + name: str | None + description: str | None class Config: orm_mode = True class MealDayIn(MealieModel): - date: Optional[date] + date: date | None meals: list[MealIn] class Config: @@ -48,7 +47,7 @@ class MealPlanIn(MealieModel): class MealPlanOut(MealPlanIn): id: int - shopping_list: Optional[int] + shopping_list: int | None class Config: orm_mode = True diff --git a/mealie/schema/meal_plan/new_meal.py b/mealie/schema/meal_plan/new_meal.py index 221bfb5a..b8868dab 100644 --- a/mealie/schema/meal_plan/new_meal.py +++ b/mealie/schema/meal_plan/new_meal.py @@ -1,6 +1,5 @@ from datetime import date from enum import Enum -from typing import Optional from uuid import UUID from pydantic import validator @@ -27,7 +26,7 @@ class CreatePlanEntry(MealieModel): entry_type: PlanEntryType = PlanEntryType.breakfast title: str = "" text: str = "" - recipe_id: Optional[UUID] + recipe_id: UUID | None @validator("recipe_id", always=True) @classmethod @@ -51,7 +50,7 @@ class SavePlanEntry(CreatePlanEntry): class ReadPlanEntry(UpdatePlanEntry): - recipe: Optional[RecipeSummary] + recipe: RecipeSummary | None class Config: orm_mode = True diff --git a/mealie/schema/meal_plan/shopping_list.py b/mealie/schema/meal_plan/shopping_list.py index 6e4d86e2..3630ba57 100644 --- a/mealie/schema/meal_plan/shopping_list.py +++ b/mealie/schema/meal_plan/shopping_list.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic.utils import GetterDict from mealie.db.models.group.shopping_list import ShoppingList @@ -7,7 +5,7 @@ from mealie.schema._mealie import MealieModel class ListItem(MealieModel): - title: Optional[str] + title: str | None text: str = "" quantity: int = 1 checked: bool = False @@ -18,7 +16,7 @@ class ListItem(MealieModel): class ShoppingListIn(MealieModel): name: str - group: Optional[str] + group: str | None items: list[ListItem] diff --git a/mealie/schema/recipe/recipe_asset.py b/mealie/schema/recipe/recipe_asset.py index 5dfcd4dc..8abdcf45 100644 --- a/mealie/schema/recipe/recipe_asset.py +++ b/mealie/schema/recipe/recipe_asset.py @@ -1,12 +1,10 @@ -from typing import Optional - from mealie.schema._mealie import MealieModel class RecipeAsset(MealieModel): name: str icon: str - file_name: Optional[str] + file_name: str | None class Config: orm_mode = True diff --git a/mealie/schema/recipe/recipe_category.py b/mealie/schema/recipe/recipe_category.py index a8d4ceaf..75cb1c93 100644 --- a/mealie/schema/recipe/recipe_category.py +++ b/mealie/schema/recipe/recipe_category.py @@ -65,7 +65,7 @@ class RecipeTagResponse(RecipeCategoryResponse): pass -from mealie.schema.recipe.recipe import RecipeSummary +from mealie.schema.recipe.recipe import RecipeSummary # noqa: E402 RecipeCategoryResponse.update_forward_refs() RecipeTagResponse.update_forward_refs() diff --git a/mealie/schema/recipe/recipe_comments.py b/mealie/schema/recipe/recipe_comments.py index 54a9f420..902c738e 100644 --- a/mealie/schema/recipe/recipe_comments.py +++ b/mealie/schema/recipe/recipe_comments.py @@ -1,5 +1,4 @@ from datetime import datetime -from typing import Optional from pydantic import UUID4 @@ -9,7 +8,7 @@ from mealie.schema.response.pagination import PaginationBase class UserBase(MealieModel): id: UUID4 - username: Optional[str] + username: str | None admin: bool class Config: diff --git a/mealie/schema/recipe/recipe_nutrition.py b/mealie/schema/recipe/recipe_nutrition.py index 02682050..776b5bf4 100644 --- a/mealie/schema/recipe/recipe_nutrition.py +++ b/mealie/schema/recipe/recipe_nutrition.py @@ -1,16 +1,14 @@ -from typing import Optional - from mealie.schema._mealie import MealieModel class Nutrition(MealieModel): - calories: Optional[str] - fat_content: Optional[str] - protein_content: Optional[str] - carbohydrate_content: Optional[str] - fiber_content: Optional[str] - sodium_content: Optional[str] - sugar_content: Optional[str] + calories: str | None + fat_content: str | None + protein_content: str | None + carbohydrate_content: str | None + fiber_content: str | None + sodium_content: str | None + sugar_content: str | None class Config: orm_mode = True diff --git a/mealie/schema/recipe/recipe_step.py b/mealie/schema/recipe/recipe_step.py index 71a7dfd1..6db07c21 100644 --- a/mealie/schema/recipe/recipe_step.py +++ b/mealie/schema/recipe/recipe_step.py @@ -1,4 +1,3 @@ -from typing import Optional from uuid import UUID, uuid4 from pydantic import UUID4, Field @@ -11,15 +10,15 @@ class IngredientReferences(MealieModel): A list of ingredient references. """ - reference_id: Optional[UUID4] + reference_id: UUID4 | None class Config: orm_mode = True class RecipeStep(MealieModel): - id: Optional[UUID] = Field(default_factory=uuid4) - title: Optional[str] = "" + id: UUID | None = Field(default_factory=uuid4) + title: str | None = "" text: str ingredient_references: list[IngredientReferences] = [] diff --git a/mealie/schema/recipe/recipe_tool.py b/mealie/schema/recipe/recipe_tool.py index 3a4615e5..d5af0dda 100644 --- a/mealie/schema/recipe/recipe_tool.py +++ b/mealie/schema/recipe/recipe_tool.py @@ -1,5 +1,3 @@ -import typing - from pydantic import UUID4 from mealie.schema._mealie import MealieModel @@ -23,7 +21,7 @@ class RecipeToolOut(RecipeToolCreate): class RecipeToolResponse(RecipeToolOut): - recipes: typing.List["Recipe"] = [] + recipes: list["Recipe"] = [] class Config: orm_mode = True diff --git a/mealie/schema/response/query_filter.py b/mealie/schema/response/query_filter.py index 3f46a4cf..ee6cf5d9 100644 --- a/mealie/schema/response/query_filter.py +++ b/mealie/schema/response/query_filter.py @@ -62,7 +62,14 @@ class QueryFilter: self.filter_components = QueryFilter._parse_base_components_into_filter_components(base_components) def __repr__(self) -> str: - return f'<<{" ".join([str(component.value if isinstance(component, LogicalOperator) else component) for component in self.filter_components])}>>' + joined = " ".join( + [ + str(component.value if isinstance(component, LogicalOperator) else component) + for component in self.filter_components + ], + ) + + return f"<<{joined}>>" def filter_query(self, query: Query, model: type[Model]) -> Query: segments: list[str] = [] @@ -76,8 +83,9 @@ class QueryFilter: segments.append(component.value) continue - # for some reason typing doesn't like the lsep and rsep literals, so we explicitly mark this as a filter component instead - # cast doesn't actually do anything at runtime + # for some reason typing doesn't like the lsep and rsep literals, so + # we explicitly mark this as a filter component instead cast doesn't + # actually do anything at runtime component = cast(QueryFilterComponent, component) if not hasattr(model, component.attribute_name): diff --git a/mealie/schema/response/responses.py b/mealie/schema/response/responses.py index 237f174c..b5b781c5 100644 --- a/mealie/schema/response/responses.py +++ b/mealie/schema/response/responses.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import BaseModel from mealie.schema._mealie import MealieModel @@ -8,10 +6,10 @@ from mealie.schema._mealie import MealieModel class ErrorResponse(BaseModel): message: str error: bool = True - exception: Optional[str] = None + exception: str | None = None @classmethod - def respond(cls, message: str, exception: Optional[str] = None) -> dict: + def respond(cls, message: str, exception: str | None = None) -> dict: """ This method is an helper to create an object and convert to a dictionary in the same call, for use while providing details to a HTTPException diff --git a/mealie/schema/user/auth.py b/mealie/schema/user/auth.py index 97abe7c3..657eab59 100644 --- a/mealie/schema/user/auth.py +++ b/mealie/schema/user/auth.py @@ -1,5 +1,3 @@ -from typing import Optional - from pydantic import UUID4, BaseModel from pydantic.types import constr @@ -12,8 +10,8 @@ class Token(BaseModel): class TokenData(BaseModel): - user_id: Optional[UUID4] - username: Optional[constr(to_lower=True, strip_whitespace=True)] = None # type: ignore + user_id: UUID4 | None + username: constr(to_lower=True, strip_whitespace=True) | None = None # type: ignore class UnlockResults(MealieModel): diff --git a/mealie/schema/user/user.py b/mealie/schema/user/user.py index b3faffee..6c9855ac 100644 --- a/mealie/schema/user/user.py +++ b/mealie/schema/user/user.py @@ -1,6 +1,6 @@ from datetime import datetime, timedelta from pathlib import Path -from typing import Any, Optional +from typing import Any from uuid import UUID from pydantic import UUID4, Field, validator @@ -62,13 +62,13 @@ class GroupBase(MealieModel): class UserBase(MealieModel): - username: Optional[str] - full_name: Optional[str] = None + username: str | None + full_name: str | None = None email: constr(to_lower=True, strip_whitespace=True) # type: ignore admin: bool = False - group: Optional[str] + group: str | None advanced: bool = False - favorite_recipes: Optional[list[str]] = [] + favorite_recipes: list[str] | None = [] can_invite: bool = False can_manage: bool = False @@ -103,9 +103,9 @@ class UserOut(UserBase): id: UUID4 group: str group_id: UUID4 - tokens: Optional[list[LongLiveTokenOut]] + tokens: list[LongLiveTokenOut] | None cache_key: str - favorite_recipes: Optional[list[str]] = [] + favorite_recipes: list[str] | None = [] class Config: orm_mode = True @@ -171,14 +171,14 @@ class PrivateUser(UserOut): class UpdateGroup(GroupBase): id: UUID4 name: str - categories: Optional[list[CategoryBase]] = [] + categories: list[CategoryBase] | None = [] webhooks: list[Any] = [] class GroupInDB(UpdateGroup): - users: Optional[list[UserOut]] - preferences: Optional[ReadGroupPreferences] = None + users: list[UserOut] | None + preferences: ReadGroupPreferences | None = None class Config: orm_mode = True diff --git a/mealie/services/email/__init__.py b/mealie/services/email/__init__.py index 2cf20aec..4bbe47ed 100644 --- a/mealie/services/email/__init__.py +++ b/mealie/services/email/__init__.py @@ -1 +1,6 @@ from .email_service import EmailService, EmailTemplate + +__all__ = [ + "EmailService", + "EmailTemplate", +] diff --git a/mealie/services/event_bus_service/event_bus_listeners.py b/mealie/services/event_bus_service/event_bus_listeners.py index 7df0b26d..77ad8d0a 100644 --- a/mealie/services/event_bus_service/event_bus_listeners.py +++ b/mealie/services/event_bus_service/event_bus_listeners.py @@ -130,7 +130,7 @@ class WebhookEventListener(EventListenerBase): return scheduled_webhooks def publish_to_subscribers(self, event: Event, subscribers: list[ReadWebhook]) -> None: - match event.document_data.document_type: + match event.document_data.document_type: # noqa - match statement not supported by ruff case EventDocumentType.mealplan: # TODO: limit mealplan data to a date range instead of returning all mealplans meal_repo = self.repos.meals.by_group(self.group_id) diff --git a/mealie/services/event_bus_service/event_bus_service.py b/mealie/services/event_bus_service/event_bus_service.py index 1fd0c59b..4af00ecc 100644 --- a/mealie/services/event_bus_service/event_bus_service.py +++ b/mealie/services/event_bus_service/event_bus_service.py @@ -1,5 +1,3 @@ -from typing import Optional - from fastapi import BackgroundTasks, Depends from pydantic import UUID4 from sqlalchemy.orm.session import Session @@ -45,7 +43,7 @@ class EventBusService: group_id: UUID4 | None def __init__( - self, bg: Optional[BackgroundTasks] = None, session: Optional[Session] = None, group_id: UUID4 | None = None + self, bg: BackgroundTasks | None = None, session: Session | None = None, group_id: UUID4 | None = None ) -> None: self.bg = bg self.session = session @@ -61,7 +59,7 @@ class EventBusService: integration_id: str, group_id: UUID4, event_type: EventTypes, - document_data: Optional[EventDocumentDataBase], + document_data: EventDocumentDataBase | None, message: str = "", ) -> None: self.group_id = group_id diff --git a/mealie/services/exporter/_abc_exporter.py b/mealie/services/exporter/_abc_exporter.py index 5511581c..2e375275 100644 --- a/mealie/services/exporter/_abc_exporter.py +++ b/mealie/services/exporter/_abc_exporter.py @@ -3,7 +3,7 @@ from abc import abstractmethod, abstractproperty from collections.abc import Iterator from dataclasses import dataclass from pathlib import Path -from typing import Callable, Optional +from typing import Callable from uuid import UUID from pydantic import BaseModel @@ -28,7 +28,7 @@ class ExportedItem: class ABCExporter(BaseService): - write_dir_to_zip: Callable[[Path, str, Optional[set[str]]], None] | None + write_dir_to_zip: Callable[[Path, str, set[str] | None], None] | None def __init__(self, db: AllRepositories, group_id: UUID) -> None: self.logger = get_logger() diff --git a/mealie/services/group_services/shopping_lists.py b/mealie/services/group_services/shopping_lists.py index dc49ba3e..9cfbe361 100644 --- a/mealie/services/group_services/shopping_lists.py +++ b/mealie/services/group_services/shopping_lists.py @@ -169,7 +169,9 @@ class ShoppingListService: if not updated_shopping_list: raise UnexpectedNone("Shopping List not found") - updated_shopping_list_items, deleted_shopping_list_items = self.consolidate_and_save(updated_shopping_list.list_items) # type: ignore + updated_shopping_list_items, deleted_shopping_list_items = self.consolidate_and_save( + updated_shopping_list.list_items, # type: ignore + ) updated_shopping_list.list_items = updated_shopping_list_items not_found = True @@ -268,5 +270,8 @@ class ShoppingListService: self.list_refs.update(recipe_ref.id, ref) break - # Save Changes - return self.shopping_lists.get_one(shopping_list.id), updated_shopping_list_items, deleted_shopping_list_items # type: ignore + return ( + self.shopping_lists.get_one(shopping_list.id), + updated_shopping_list_items, + deleted_shopping_list_items, + ) # type: ignore diff --git a/mealie/services/migrations/nextcloud.py b/mealie/services/migrations/nextcloud.py index c8450482..661a3a97 100644 --- a/mealie/services/migrations/nextcloud.py +++ b/mealie/services/migrations/nextcloud.py @@ -2,7 +2,6 @@ import tempfile import zipfile from dataclasses import dataclass from pathlib import Path -from typing import Optional from slugify import slugify @@ -15,7 +14,7 @@ from .utils.migration_helpers import MigrationReaders, glob_walker, import_image class NextcloudDir: name: str recipe: dict - image: Optional[Path] + image: Path | None @property def slug(self): diff --git a/mealie/services/migrations/utils/migration_alias.py b/mealie/services/migrations/utils/migration_alias.py index 4e36f402..c7d6ecd0 100644 --- a/mealie/services/migrations/utils/migration_alias.py +++ b/mealie/services/migrations/utils/migration_alias.py @@ -1,5 +1,4 @@ from collections.abc import Callable -from typing import Optional from pydantic import BaseModel @@ -12,4 +11,4 @@ class MigrationAlias(BaseModel): key: str alias: str - func: Optional[Callable] = None + func: Callable | None = None diff --git a/mealie/services/parser_services/brute/__init__.py b/mealie/services/parser_services/brute/__init__.py index 016e9f2d..d423c487 100644 --- a/mealie/services/parser_services/brute/__init__.py +++ b/mealie/services/parser_services/brute/__init__.py @@ -1 +1,5 @@ from .process import parse + +__all__ = [ + "parse", +] diff --git a/mealie/services/parser_services/brute/process.py b/mealie/services/parser_services/brute/process.py index 0ef4f5e3..ade777cf 100644 --- a/mealie/services/parser_services/brute/process.py +++ b/mealie/services/parser_services/brute/process.py @@ -26,8 +26,8 @@ def parse_fraction(x): raise ValueError try: return int(frac_split[0]) / int(frac_split[1]) - except ZeroDivisionError: - raise ValueError + except ZeroDivisionError as e: + raise ValueError from e def parse_amount(ing_str) -> tuple[float, str, str]: diff --git a/mealie/services/parser_services/crfpp/pre_processor.py b/mealie/services/parser_services/crfpp/pre_processor.py index 9da8b4f1..c95dee5d 100644 --- a/mealie/services/parser_services/crfpp/pre_processor.py +++ b/mealie/services/parser_services/crfpp/pre_processor.py @@ -52,7 +52,8 @@ def wrap_or_clause(string: str): Attempts to wrap or clauses in () Examples: - '1 tsp. Diamond Crystal or ½ tsp. Morton kosher salt, plus more' -> '1 teaspoon diamond crystal (or 1/2 teaspoon morton kosher salt), plus more' + '1 tsp. Diamond Crystal or ½ tsp. Morton kosher salt, plus more' + -> '1 teaspoon diamond crystal (or 1/2 teaspoon morton kosher salt), plus more' """ # TODO: Needs more adequite testing to be sure this doesn't have side effects. diff --git a/mealie/services/recipe/recipe_data_service.py b/mealie/services/recipe/recipe_data_service.py index 3967fa22..c21ead0a 100644 --- a/mealie/services/recipe/recipe_data_service.py +++ b/mealie/services/recipe/recipe_data_service.py @@ -17,14 +17,17 @@ async def largest_content_len(urls: list[str]) -> tuple[str, int]: largest_url = "" largest_len = 0 + def do(session: requests.Session, url: str): + def _do() -> requests.Response: + return session.head(url, headers={"User-Agent": _FIREFOX_UA}) + + return _do + with ThreadPoolExecutor(max_workers=10) as executor: with requests.Session() as session: loop = asyncio.get_event_loop() - tasks = [ - loop.run_in_executor(executor, lambda: session.head(url, headers={"User-Agent": _FIREFOX_UA})) - for url in urls - ] + tasks = [loop.run_in_executor(executor, do(session, url)) for url in urls] response: requests.Response # required for type hinting within the loop for response in await asyncio.gather(*tasks): diff --git a/mealie/services/recipe/recipe_service.py b/mealie/services/recipe/recipe_service.py index d7f958e6..49f481be 100644 --- a/mealie/services/recipe/recipe_service.py +++ b/mealie/services/recipe/recipe_service.py @@ -3,7 +3,6 @@ import shutil from datetime import datetime from pathlib import Path from shutil import copytree, rmtree -from typing import Union from zipfile import ZipFile from fastapi import UploadFile @@ -27,12 +26,6 @@ step_text = """Recipe steps as well as other fields in the recipe page support m [My Link](https://demo.mealie.io) -**Embed an image** - -Use the `height="100"` or `width="100"` attributes to set the size of the image. - - - """ ingredient_note = "1 Cup Flour" @@ -110,7 +103,7 @@ class RecipeService(BaseService): return Recipe(**additional_attrs) - def create_one(self, create_data: Union[Recipe, CreateRecipe]) -> Recipe: + def create_one(self, create_data: Recipe | CreateRecipe) -> Recipe: if create_data.name is None: create_data.name = "New Recipe" diff --git a/mealie/services/scheduler/runner.py b/mealie/services/scheduler/runner.py index 990c2f7d..53d0485d 100644 --- a/mealie/services/scheduler/runner.py +++ b/mealie/services/scheduler/runner.py @@ -6,22 +6,22 @@ import logging from asyncio import ensure_future from functools import wraps from traceback import format_exception -from typing import Any, Callable, Coroutine, Optional, Union +from typing import Any, Callable, Coroutine from starlette.concurrency import run_in_threadpool NoArgsNoReturnFuncT = Callable[[], None] NoArgsNoReturnAsyncFuncT = Callable[[], Coroutine[Any, Any, None]] -NoArgsNoReturnDecorator = Callable[[Union[NoArgsNoReturnFuncT, NoArgsNoReturnAsyncFuncT]], NoArgsNoReturnAsyncFuncT] +NoArgsNoReturnDecorator = Callable[[NoArgsNoReturnFuncT | NoArgsNoReturnAsyncFuncT], NoArgsNoReturnAsyncFuncT] def repeat_every( *, minutes: float, wait_first: bool = False, - logger: Optional[logging.Logger] = None, + logger: logging.Logger | None = None, raise_exceptions: bool = False, - max_repetitions: Optional[int] = None, + max_repetitions: int | None = None, ) -> NoArgsNoReturnDecorator: """ This function returns a decorator that modifies a function so it is periodically re-executed after its first call. @@ -46,7 +46,7 @@ def repeat_every( The maximum number of times to call the repeated function. If `None`, the function is repeated forever. """ - def decorator(func: Union[NoArgsNoReturnAsyncFuncT, NoArgsNoReturnFuncT]) -> NoArgsNoReturnAsyncFuncT: + def decorator(func: NoArgsNoReturnAsyncFuncT | NoArgsNoReturnFuncT) -> NoArgsNoReturnAsyncFuncT: """ Converts the decorated function into a repeated, periodically-called version of itself. """ diff --git a/mealie/services/scheduler/tasks/post_webhooks.py b/mealie/services/scheduler/tasks/post_webhooks.py index b62b5f2c..a898775c 100644 --- a/mealie/services/scheduler/tasks/post_webhooks.py +++ b/mealie/services/scheduler/tasks/post_webhooks.py @@ -1,5 +1,4 @@ from datetime import datetime, timezone -from typing import Optional from pydantic import UUID4 @@ -18,7 +17,7 @@ from mealie.services.event_bus_service.event_types import ( last_ran = datetime.now(timezone.utc) -def post_group_webhooks(start_dt: Optional[datetime] = None, group_id: Optional[UUID4] = None) -> None: +def post_group_webhooks(start_dt: datetime | None = None, group_id: UUID4 | None = None) -> None: """Post webhook events to specified group, or all groups""" global last_ran diff --git a/mealie/services/scraper/cleaner.py b/mealie/services/scraper/cleaner.py index 9e44b84f..f490e7bf 100644 --- a/mealie/services/scraper/cleaner.py +++ b/mealie/services/scraper/cleaner.py @@ -95,7 +95,7 @@ def clean_image(image: str | list | dict | None = None, default="no image") -> s if not image: return default - match image: + match image: # noqa - match statement not supported case str(image): return image case list(image): @@ -189,7 +189,13 @@ def clean_instructions(steps_object: list | dict | str, default: list | None = N # } # steps_object = typing.cast(list[dict[str, str]], steps_object) - return clean_instructions(functools.reduce(operator.concat, [x["itemListElement"] for x in steps_object], [])) # type: ignore + return clean_instructions( + functools.reduce( + operator.concat, # type: ignore + [x["itemListElement"] for x in steps_object], + [], + ) + ) case _: raise TypeError(f"Unexpected type for instructions: {type(steps_object)}, {steps_object}") diff --git a/mealie/services/user_services/password_reset_service.py b/mealie/services/user_services/password_reset_service.py index 4f64d163..1fe69653 100644 --- a/mealie/services/user_services/password_reset_service.py +++ b/mealie/services/user_services/password_reset_service.py @@ -42,7 +42,7 @@ class PasswordResetService(BaseService): email_servive.send_forgot_password(email, reset_url) except Exception as e: self.logger.error(f"failed to send reset email: {e}") - raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR, "Failed to send reset email") + raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR, "Failed to send reset email") from e def reset_password(self, token: str, new_password: str): # Validate Token diff --git a/poetry.lock b/poetry.lock index 54bfc893..fa2b7152 100644 --- a/poetry.lock +++ b/poetry.lock @@ -56,19 +56,19 @@ python-versions = "*" [[package]] name = "apprise" -version = "0.9.9" +version = "1.2.0" description = "Push Notifications that work with just about every platform!" category = "main" optional = false -python-versions = ">=2.7" +python-versions = ">=3.6" [package.dependencies] +certifi = "*" click = ">=5.0" markdown = "*" PyYAML = "*" requests = "*" requests-oauthlib = "*" -six = "*" [[package]] name = "astroid" @@ -83,14 +83,6 @@ lazy-object-proxy = ">=1.4.0" setuptools = ">=20.0" wrapt = ">=1.11,<2" -[[package]] -name = "atomicwrites" -version = "1.4.1" -description = "Atomic file writes." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - [[package]] name = "attrs" version = "21.4.0" @@ -107,15 +99,12 @@ tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy" [[package]] name = "bcrypt" -version = "3.2.2" +version = "4.0.1" description = "Modern password hashing for your software and your servers" category = "main" optional = false python-versions = ">=3.6" -[package.dependencies] -cffi = ">=1.1" - [package.extras] tests = ["pytest (>=3.2.1,!=3.3.0)"] typecheck = ["mypy"] @@ -169,22 +158,11 @@ category = "main" optional = false python-versions = ">=3.6" -[[package]] -name = "cffi" -version = "1.15.1" -description = "Foreign Function Interface for Python calling C code." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -pycparser = "*" - [[package]] name = "cfgv" version = "3.3.1" description = "Validate configuration and produce human readable error messages." -category = "dev" +category = "main" optional = false python-versions = ">=3.6.1" @@ -264,7 +242,7 @@ graph = ["objgraph (>=1.7.2)"] name = "distlib" version = "0.3.4" description = "Distribution utilities" -category = "dev" +category = "main" optional = false python-versions = "*" @@ -283,6 +261,17 @@ six = ">=1.9.0" gmpy = ["gmpy"] gmpy2 = ["gmpy2"] +[[package]] +name = "exceptiongroup" +version = "1.0.4" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "extruct" version = "0.13.0" @@ -327,7 +316,7 @@ test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.8.0)", "databases[sqlite] ( name = "filelock" version = "3.7.1" description = "A platform independent file lock." -category = "dev" +category = "main" optional = false python-versions = ">=3.7" @@ -335,32 +324,6 @@ python-versions = ">=3.7" docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"] -[[package]] -name = "flake8" -version = "4.0.1" -description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.8.0,<2.9.0" -pyflakes = ">=2.4.0,<2.5.0" - -[[package]] -name = "flake8-print" -version = "4.0.1" -description = "print statement checker plugin for flake8" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -flake8 = ">=3.0" -pycodestyle = "*" -six = "*" - [[package]] name = "ghp-import" version = "2.1.0" @@ -455,7 +418,7 @@ test = ["Cython (==0.29.22)"] name = "identify" version = "2.5.1" description = "File identification library for Python" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" @@ -720,7 +683,7 @@ python-versions = "*" name = "nodeenv" version = "1.7.0" description = "Node.js virtual environment builder" -category = "dev" +category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" @@ -830,7 +793,7 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa name = "platformdirs" version = "2.5.2" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" +category = "main" optional = false python-versions = ">=3.7" @@ -852,9 +815,9 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "2.19.0" +version = "2.20.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." -category = "dev" +category = "main" optional = false python-versions = ">=3.7" @@ -874,14 +837,6 @@ category = "main" optional = true python-versions = ">=3.6" -[[package]] -name = "py" -version = "1.11.0" -description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - [[package]] name = "pyasn1" version = "0.4.8" @@ -901,22 +856,6 @@ python-versions = "*" [package.dependencies] pyasn1 = ">=0.4.6,<0.5.0" -[[package]] -name = "pycodestyle" -version = "2.8.0" -description = "Python style guide checker" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "pycparser" -version = "2.21" -description = "C parser in Python" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - [[package]] name = "pydantic" version = "1.10.2" @@ -944,14 +883,6 @@ python-versions = "*" click = "*" pydantic = "*" -[[package]] -name = "pyflakes" -version = "2.4.0" -description = "passive checker of Python programs" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - [[package]] name = "pygments" version = "2.12.0" @@ -1046,40 +977,23 @@ Pillow = ">=8.0.0" [[package]] name = "pytest" -version = "6.2.5" +version = "7.2.0" description = "pytest: simple powerful testing with Python" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] -atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" -py = ">=1.8.2" -toml = "*" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] - -[[package]] -name = "pytest-cov" -version = "2.12.1" -description = "Pytest plugin for measuring coverage." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.dependencies] -coverage = ">=5.2.1" -pytest = ">=4.6" -toml = "*" - -[package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] [[package]] name = "python-dateutil" @@ -1208,7 +1122,7 @@ rdflib = ">=5.0.0" [[package]] name = "recipe-scrapers" -version = "14.23.0" +version = "14.24.0" description = "Python package, scraping recipes from all over the internet" category = "main" optional = false @@ -1280,6 +1194,14 @@ python-versions = ">=3.6,<4" [package.dependencies] pyasn1 = ">=0.1.3" +[[package]] +name = "ruff" +version = "0.0.149" +description = "An extremely fast Python linter, written in Rust." +category = "dev" +optional = false +python-versions = ">=3.7" + [[package]] name = "setuptools" version = "65.4.0" @@ -1375,7 +1297,7 @@ python-versions = "*" name = "toml" version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" -category = "dev" +category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" @@ -1506,7 +1428,7 @@ test = ["aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOp name = "virtualenv" version = "20.15.1" description = "Virtual Python Environment builder" -category = "dev" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" @@ -1595,7 +1517,7 @@ pgsql = ["psycopg2-binary"] [metadata] lock-version = "1.1" python-versions = "^3.10" -content-hash = "9b8033d971c49138bf8eb949e57e104b82b795ad45551586c1624f494388b5ef" +content-hash = "39816ee8fea6764e99683a670968b08acb6887c529d5fe7af864b0ad9fbafb4e" [metadata.files] aiofiles = [ @@ -1619,32 +1541,39 @@ appdirs = [ {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, ] apprise = [ - {file = "apprise-0.9.9-py2.py3-none-any.whl", hash = "sha256:afc4a637598b984b2bda3d997a362f52906ef9e3167f81001440153a4d57b871"}, - {file = "apprise-0.9.9.tar.gz", hash = "sha256:6ba3d0e8307e2647c3240ec1a0d5571f3a453f9143d94834ecb02660b0e8d244"}, + {file = "apprise-1.2.0-py2.py3-none-any.whl", hash = "sha256:9f7dd12e8cebeef7268c87b64f04a22c4a99ca3af17113af91985501e1a7d478"}, + {file = "apprise-1.2.0.tar.gz", hash = "sha256:6e31afa18f47452eaccd56fb7ee83d92452c534d15f392407ed1a0e3c465244b"}, ] astroid = [ {file = "astroid-2.11.7-py3-none-any.whl", hash = "sha256:86b0a340a512c65abf4368b80252754cda17c02cdbbd3f587dddf98112233e7b"}, {file = "astroid-2.11.7.tar.gz", hash = "sha256:bb24615c77f4837c707669d16907331374ae8a964650a66999da3f5ca68dc946"}, ] -atomicwrites = [ - {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, -] attrs = [ {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, ] bcrypt = [ - {file = "bcrypt-3.2.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:7180d98a96f00b1050e93f5b0f556e658605dd9f524d0b0e68ae7944673f525e"}, - {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:61bae49580dce88095d669226d5076d0b9d927754cedbdf76c6c9f5099ad6f26"}, - {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88273d806ab3a50d06bc6a2fc7c87d737dd669b76ad955f449c43095389bc8fb"}, - {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6d2cb9d969bfca5bc08e45864137276e4c3d3d7de2b162171def3d188bf9d34a"}, - {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b02d6bfc6336d1094276f3f588aa1225a598e27f8e3388f4db9948cb707b521"}, - {file = "bcrypt-3.2.2-cp36-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a2c46100e315c3a5b90fdc53e429c006c5f962529bc27e1dfd656292c20ccc40"}, - {file = "bcrypt-3.2.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7d9ba2e41e330d2af4af6b1b6ec9e6128e91343d0b4afb9282e54e5508f31baa"}, - {file = "bcrypt-3.2.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cd43303d6b8a165c29ec6756afd169faba9396a9472cdff753fe9f19b96ce2fa"}, - {file = "bcrypt-3.2.2-cp36-abi3-win32.whl", hash = "sha256:4e029cef560967fb0cf4a802bcf4d562d3d6b4b1bf81de5ec1abbe0f1adb027e"}, - {file = "bcrypt-3.2.2-cp36-abi3-win_amd64.whl", hash = "sha256:7ff2069240c6bbe49109fe84ca80508773a904f5a8cb960e02a977f7f519b129"}, - {file = "bcrypt-3.2.2.tar.gz", hash = "sha256:433c410c2177057705da2a9f2cd01dd157493b2a7ac14c8593a16b3dab6b6bfb"}, + {file = "bcrypt-4.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:b1023030aec778185a6c16cf70f359cbb6e0c289fd564a7cfa29e727a1c38f8f"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:08d2947c490093a11416df18043c27abe3921558d2c03e2076ccb28a116cb6d0"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0eaa47d4661c326bfc9d08d16debbc4edf78778e6aaba29c1bc7ce67214d4410"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae88eca3024bb34bb3430f964beab71226e761f51b912de5133470b649d82344"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:a522427293d77e1c29e303fc282e2d71864579527a04ddcfda6d4f8396c6c36a"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:fbdaec13c5105f0c4e5c52614d04f0bca5f5af007910daa8b6b12095edaa67b3"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ca3204d00d3cb2dfed07f2d74a25f12fc12f73e606fcaa6975d1f7ae69cacbb2"}, + {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535"}, + {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e9a51bbfe7e9802b5f3508687758b564069ba937748ad7b9e890086290d2f79e"}, + {file = "bcrypt-4.0.1-cp36-abi3-win32.whl", hash = "sha256:2caffdae059e06ac23fce178d31b4a702f2a3264c20bfb5ff541b338194d8fab"}, + {file = "bcrypt-4.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:8a68f4341daf7522fe8d73874de8906f3a339048ba406be6ddc1b3ccb16fc0d9"}, + {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf4fa8b2ca74381bb5442c089350f09a3f17797829d958fad058d6e44d9eb83c"}, + {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:67a97e1c405b24f19d08890e7ae0c4f7ce1e56a712a016746c8b2d7732d65d4b"}, + {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b3b85202d95dd568efcb35b53936c5e3b3600c7cdcc6115ba461df3a8e89f38d"}, + {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbb03eec97496166b704ed663a53680ab57c5084b2fc98ef23291987b525cb7d"}, + {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:5ad4d32a28b80c5fa6671ccfb43676e8c1cc232887759d1cd7b6f56ea4355215"}, + {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b57adba8a1444faf784394de3436233728a1ecaeb6e07e8c22c8848f179b893c"}, + {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b2cea8a9ed3d55b4491887ceadb0106acf7c6387699fca771af56b1cdeeda"}, + {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:2b3ac11cf45161628f1f3733263e63194f22664bf4d0c0f3ab34099c02134665"}, + {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3100851841186c25f127731b9fa11909ab7b1df6fc4b9f8353f4f1fd952fbf71"}, + {file = "bcrypt-4.0.1.tar.gz", hash = "sha256:27d375903ac8261cfe4047f6709d16f7d18d39b1ec92aaf72af989552a650ebd"}, ] beautifulsoup4 = [ {file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"}, @@ -1658,72 +1587,6 @@ certifi = [ {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, ] -cffi = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, -] cfgv = [ {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, @@ -1814,6 +1677,10 @@ ecdsa = [ {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, ] +exceptiongroup = [ + {file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"}, + {file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"}, +] extruct = [ {file = "extruct-0.13.0-py2.py3-none-any.whl", hash = "sha256:fe19b9aefdb4dfbf828c2b082b81a363a03a44c7591c2d6b62ca225cb8f8c0be"}, {file = "extruct-0.13.0.tar.gz", hash = "sha256:50a5b5bac4c5e19ecf682bf63a28fde0b1bb57433df7057371f60b58c94a2c64"}, @@ -1826,14 +1693,6 @@ filelock = [ {file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"}, {file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"}, ] -flake8 = [ - {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, - {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, -] -flake8-print = [ - {file = "flake8-print-4.0.1.tar.gz", hash = "sha256:12b3c3bf65329d8ca9acde949fb3b932ec113e9e5ffa6cb7cd55a7dbcd67dae1"}, - {file = "flake8_print-4.0.1-py3-none-any.whl", hash = "sha256:e246bcd5b07d5259af460b7eff148052c49114640380d7f22340f30920fabf02"}, -] ghp-import = [ {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, @@ -2203,10 +2062,7 @@ orjson = [ {file = "orjson-3.8.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b68a42a31f8429728183c21fb440c21de1b62e5378d0d73f280e2d894ef8942e"}, {file = "orjson-3.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ff13410ddbdda5d4197a4a4c09969cb78c722a67550f0a63c02c07aadc624833"}, {file = "orjson-3.8.0-cp310-none-win_amd64.whl", hash = "sha256:2d81e6e56bbea44be0222fb53f7b255b4e7426290516771592738ca01dbd053b"}, - {file = "orjson-3.8.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:200eae21c33f1f8b02a11f5d88d76950cd6fd986d88f1afe497a8ae2627c49aa"}, - {file = "orjson-3.8.0-cp311-cp311-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:9529990f3eab54b976d327360aa1ff244a4b12cb5e4c5b3712fcdd96e8fe56d4"}, {file = "orjson-3.8.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e2defd9527651ad39ec20ae03c812adf47ef7662bdd6bc07dabb10888d70dc62"}, - {file = "orjson-3.8.0-cp311-none-win_amd64.whl", hash = "sha256:b21c7af0ff6228ca7105f54f0800636eb49201133e15ddb80ac20c1ce973ef07"}, {file = "orjson-3.8.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:9e6ac22cec72d5b39035b566e4b86c74b84866f12b5b0b6541506a080fb67d6d"}, {file = "orjson-3.8.0-cp37-cp37m-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:e2f4a5542f50e3d336a18cb224fc757245ca66b1fd0b70b5dd4471b8ff5f2b0e"}, {file = "orjson-3.8.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1418feeb8b698b9224b1f024555895169d481604d5d884498c1838d7412794c"}, @@ -2320,13 +2176,12 @@ pluggy = [ {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] pre-commit = [ - {file = "pre_commit-2.19.0-py2.py3-none-any.whl", hash = "sha256:10c62741aa5704faea2ad69cb550ca78082efe5697d6f04e5710c3c229afdd10"}, - {file = "pre_commit-2.19.0.tar.gz", hash = "sha256:4233a1e38621c87d9dda9808c6606d7e7ba0e087cd56d3fe03202a01d2919615"}, + {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"}, + {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"}, ] psycopg2-binary = [ {file = "psycopg2-binary-2.9.3.tar.gz", hash = "sha256:761df5313dc15da1502b21453642d7599d26be88bff659382f8f9747c7ebea4e"}, {file = "psycopg2_binary-2.9.3-cp310-cp310-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:539b28661b71da7c0e428692438efbcd048ca21ea81af618d845e06ebfd29478"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f2534ab7dc7e776a263b463a16e189eb30e85ec9bbe1bff9e78dae802608932"}, {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e82d38390a03da28c7985b394ec3f56873174e2c88130e6966cb1c946508e65"}, {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57804fc02ca3ce0dbfbef35c4b3a4a774da66d66ea20f4bda601294ad2ea6092"}, {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:083a55275f09a62b8ca4902dd11f4b33075b743cf0d360419e2051a8a5d5ff76"}, @@ -2360,7 +2215,6 @@ psycopg2-binary = [ {file = "psycopg2_binary-2.9.3-cp37-cp37m-win32.whl", hash = "sha256:adf20d9a67e0b6393eac162eb81fb10bc9130a80540f4df7e7355c2dd4af9fba"}, {file = "psycopg2_binary-2.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2f9ffd643bc7349eeb664eba8864d9e01f057880f510e4681ba40a6532f93c71"}, {file = "psycopg2_binary-2.9.3-cp38-cp38-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:def68d7c21984b0f8218e8a15d514f714d96904265164f75f8d3a70f9c295667"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e6aa71ae45f952a2205377773e76f4e3f27951df38e69a4c95440c779e013560"}, {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dffc08ca91c9ac09008870c9eb77b00a46b3378719584059c034b8945e26b272"}, {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:280b0bb5cbfe8039205c7981cceb006156a675362a00fe29b16fbc264e242834"}, {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:af9813db73395fb1fc211bac696faea4ca9ef53f32dc0cfa27e4e7cf766dcf24"}, @@ -2372,7 +2226,6 @@ psycopg2-binary = [ {file = "psycopg2_binary-2.9.3-cp38-cp38-win32.whl", hash = "sha256:6472a178e291b59e7f16ab49ec8b4f3bdada0a879c68d3817ff0963e722a82ce"}, {file = "psycopg2_binary-2.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:35168209c9d51b145e459e05c31a9eaeffa9a6b0fd61689b48e07464ffd1a83e"}, {file = "psycopg2_binary-2.9.3-cp39-cp39-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:47133f3f872faf28c1e87d4357220e809dfd3fa7c64295a4a148bcd1e6e34ec9"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b3a24a1982ae56461cc24f6680604fffa2c1b818e9dc55680da038792e004d18"}, {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91920527dea30175cc02a1099f331aa8c1ba39bf8b7762b7b56cbf54bc5cce42"}, {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:887dd9aac71765ac0d0bac1d0d4b4f2c99d5f5c1382d8b770404f0f3d0ce8a39"}, {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:1f14c8b0942714eb3c74e1e71700cbbcb415acbc311c730370e70c578a44a25c"}, @@ -2384,10 +2237,6 @@ psycopg2-binary = [ {file = "psycopg2_binary-2.9.3-cp39-cp39-win32.whl", hash = "sha256:46f0e0a6b5fa5851bbd9ab1bc805eef362d3a230fbdfbc209f4a236d0a7a990d"}, {file = "psycopg2_binary-2.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:accfe7e982411da3178ec690baaceaad3c278652998b2c45828aaac66cd8285f"}, ] -py = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] pyasn1 = [ {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, @@ -2396,14 +2245,6 @@ pyasn1-modules = [ {file = "pyasn1-modules-0.2.8.tar.gz", hash = "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e"}, {file = "pyasn1_modules-0.2.8-py2.py3-none-any.whl", hash = "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74"}, ] -pycodestyle = [ - {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, - {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, -] -pycparser = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, -] pydantic = [ {file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"}, {file = "pydantic-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98"}, @@ -2446,10 +2287,6 @@ pydantic-to-typescript = [ {file = "pydantic-to-typescript-1.0.8.tar.gz", hash = "sha256:609fd9ce891840311e2e98a315cd273375ab3dc9b21018823c0095303f06c581"}, {file = "pydantic_to_typescript-1.0.8-py3-none-any.whl", hash = "sha256:39653c323b35fdd07ee0e1650a480e68cc091814701b05a04a6f77c60acb6262"}, ] -pyflakes = [ - {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, - {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, -] pygments = [ {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"}, {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"}, @@ -2502,12 +2339,8 @@ pytesseract = [ {file = "pytesseract-0.3.9.tar.gz", hash = "sha256:7e2bafc7f48d1bb71443ce4633a56f5e21925a98f220a36c336297edcd1956d0"}, ] pytest = [ - {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, - {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, -] -pytest-cov = [ - {file = "pytest-cov-2.12.1.tar.gz", hash = "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7"}, - {file = "pytest_cov-2.12.1-py2.py3-none-any.whl", hash = "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a"}, + {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, + {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, ] python-dateutil = [ {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, @@ -2575,8 +2408,8 @@ rdflib-jsonld = [ {file = "rdflib_jsonld-0.6.2-py2.py3-none-any.whl", hash = "sha256:011afe67672353ca9978ab9a4bee964dff91f14042f2d8a28c22a573779d2f8b"}, ] recipe-scrapers = [ - {file = "recipe_scrapers-14.23.0-py3-none-any.whl", hash = "sha256:a90e560c8efd41ff8528badf0ddab5d0410c40509e29554e9b93f05571ef2014"}, - {file = "recipe_scrapers-14.23.0.tar.gz", hash = "sha256:507ad4611cd39b55c1a8a09d974f39c9e8b3d1e70eadfaecc1447f47be2e81c7"}, + {file = "recipe_scrapers-14.24.0-py3-none-any.whl", hash = "sha256:1eed9d9010a771387a37aaec27b7c199cb3c4e74c6b9afaa00f1af6f43dc3e26"}, + {file = "recipe_scrapers-14.24.0.tar.gz", hash = "sha256:37ad2b8c113b4d530450ccb7406330e105ccad0dbc5666eb6fafa5ef4e16c408"}, ] requests = [ {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, @@ -2594,6 +2427,24 @@ rsa = [ {file = "rsa-4.8-py3-none-any.whl", hash = "sha256:95c5d300c4e879ee69708c428ba566c59478fd653cc3a22243eeb8ed846950bb"}, {file = "rsa-4.8.tar.gz", hash = "sha256:5c6bd9dc7a543b7fe4304a631f8a8a3b674e2bbfc49c2ae96200cdbe55df6b17"}, ] +ruff = [ + {file = "ruff-0.0.149-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:e56d77e2a33067e480d6e9ecc328b066c9f8a96ec0f049f7c6107ff1d7106f7f"}, + {file = "ruff-0.0.149-py3-none-macosx_10_9_x86_64.macosx_10_9_arm64.macosx_10_9_universal2.whl", hash = "sha256:ad6e65923abbc59cb677c6d3bbfd778f87a95942c5499eb595afd6803999bf80"}, + {file = "ruff-0.0.149-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eab933a922721930fd6fd02b1001355ee8b81e8810796d7e5d16d2e72a14dc88"}, + {file = "ruff-0.0.149-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:89c5bbc74e2a79f853c3477f1b87846a77b489029d49e4ff53cfcb3d41217b8c"}, + {file = "ruff-0.0.149-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:afa90ff653ef1354ffeaca92c8fb57a8aac16019a702c1c1cf50625b580eb421"}, + {file = "ruff-0.0.149-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9dc2710115b3006231ab6843d5dc1719618cd7972fa69fcd30e8b3496f380cd9"}, + {file = "ruff-0.0.149-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d79584f96f133f5856d846a162a9f083ac116b890707ef32b4446f4a5b15ccec"}, + {file = "ruff-0.0.149-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8efb6b2f96b127239ee17de9eb9f858b01c72483a9a2c100126ec54d4313f63d"}, + {file = "ruff-0.0.149-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f198fc1cb6cf66e864156449079921a846abe8b6488674f646ea09bccd81457"}, + {file = "ruff-0.0.149-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7e7bde713fd6a6305078dbf54c61e403d120b29093a3e365ec71607574ad1caa"}, + {file = "ruff-0.0.149-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5437e96025dc4427e77e8cf2b49c5ac04b2f3c9e9c62799d60f5f0d5bfc20be8"}, + {file = "ruff-0.0.149-py3-none-musllinux_1_2_i686.whl", hash = "sha256:481b5093db3dd59cf31c192bfe8c1594cebd46e28a36f86d9a1683802a73e9a7"}, + {file = "ruff-0.0.149-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a02c571dbb09c3ee0a17be507ddeec5cdbca2ab098f26aeead2fe691843a837a"}, + {file = "ruff-0.0.149-py3-none-win32.whl", hash = "sha256:58605add6c6f4b5d8306290c66965609da386c5b686a30c535a319dfba3c9af7"}, + {file = "ruff-0.0.149-py3-none-win_amd64.whl", hash = "sha256:58f640e94d5473db4cde84211659576aeccb88ae459e273f204683596b5ec205"}, + {file = "ruff-0.0.149.tar.gz", hash = "sha256:b6707c1ecc9730900138eb92dd0e7cd9123364ae9b33ba2cc9d623990129a29e"}, +] setuptools = [ {file = "setuptools-65.4.0-py3-none-any.whl", hash = "sha256:c2d2709550f15aab6c9110196ea312f468f41cd546bceb24127a1be6fdcaeeb1"}, {file = "setuptools-65.4.0.tar.gz", hash = "sha256:a8f6e213b4b0661f590ccf40de95d28a177cd747d098624ad3f69c40287297e9"}, diff --git a/pyproject.toml b/pyproject.toml index 6e3f8bb1..11efca5f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,72 +1,71 @@ [tool.poetry] +authors = ["Hayden "] +description = "A Recipe Manager" +license = "AGPL" name = "mealie" version = "1.0.0b" -description = "A Recipe Manager" -authors = ["Hayden "] -license = "AGPL" [tool.poetry.scripts] start = "mealie.app:main" [tool.poetry.dependencies] -python = "^3.10" +Jinja2 = "^3.1.2" +Pillow = "^9.2.0" +PyYAML = "^5.3.1" +SQLAlchemy = "^1.4.29" aiofiles = "0.5.0" +alembic = "^1.7.5" aniso8601 = "7.0.0" appdirs = "1.4.4" -fastapi = "^0.85.1" -uvicorn = {extras = ["standard"], version = "^0.13.0"} -SQLAlchemy = "^1.4.29" -alembic = "^1.7.5" -Jinja2 = "^3.1.2" -python-dotenv = "^0.15.0" -python-slugify = "^6.1.2" -requests = "^2.25.1" -PyYAML = "^5.3.1" +apprise = "^1.2.0" +bcrypt = "^4.0.1" extruct = "^0.13.0" -python-multipart = "^0.0.5" -bcrypt = "^3.2.0" -python-jose = "^3.3.0" -passlib = "^1.7.4" -lxml = "^4.7.1" -Pillow = "^9.2.0" -apprise = "^0.9.6" -recipe-scrapers = "^14.20.0" -psycopg2-binary = {version = "^2.9.1", optional = true} +fastapi = "^0.85.1" gunicorn = "^20.1.0" -python-ldap = "^3.3.1" +lxml = "^4.7.1" +orjson = "^3.8.0" +passlib = "^1.7.4" +psycopg2-binary = {version = "^2.9.1", optional = true} pydantic = "^1.10.2" -tzdata = "^2021.5" pyhumps = "^3.5.3" pytesseract = "^0.3.9" -orjson = "^3.8.0" +python = "^3.10" python-dateutil = "^2.8.2" +python-dotenv = "^0.15.0" +python-jose = "^3.3.0" +python-ldap = "^3.3.1" +python-multipart = "^0.0.5" +python-slugify = "^6.1.2" +recipe-scrapers = "^14.24.0" +requests = "^2.25.1" +tzdata = "^2021.5" +uvicorn = {extras = ["standard"], version = "^0.13.0"} +pre-commit = "^2.20.0" [tool.poetry.dev-dependencies] -pylint = "^2.6.0" -pytest = "^6.2.1" -pytest-cov = "^2.11.0" -mkdocs-material = "^8.2.3" -flake8 = "^4.0.1" -coverage = "^5.5" -pydantic-to-typescript = "^1.0.7" -rich = "^10.7.0" -isort = "^5.9.3" -flake8-print = "^4.0.0" black = "^21.12b0" +coverage = "^5.5" coveragepy-lcov = "^0.1.1" +mkdocs-material = "^8.2.3" mypy = "^0.960" -types-python-slugify = "^5.0.3" +openapi-spec-validator = "^0.4.0" +pre-commit = "^2.20.0" +pydantic-to-typescript = "^1.0.7" +pylint = "^2.6.0" +pytest = "^7.2.0" +rich = "^10.7.0" +ruff = "^0.0.149" types-PyYAML = "^6.0.4" +types-python-dateutil = "^2.8.18" +types-python-slugify = "^5.0.3" types-requests = "^2.27.12" types-urllib3 = "^1.26.11" -pre-commit = "^2.17.0" -types-python-dateutil = "^2.8.18" -openapi-spec-validator = "^0.4.0" +[tool.poetry.group.dev.dependencies] [build-system] -requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" +requires = ["poetry-core>=1.0.0"] [tool.black] line-length = 120 @@ -80,19 +79,14 @@ min_confidence = 60 paths = ["mealie"] sort_by_size = true -[tool.isort] -profile = "black" -line_length = 120 -multi_line_output = 3 - [tool.pytest.ini_options] +addopts = "-ra -q" minversion = "6.0" -addopts = "-ra -q --cov=mealie" -python_files = 'test_*' python_classes = '*Tests' +python_files = 'test_*' python_functions = 'test_*' testpaths = [ - "tests", + "tests", ] [tool.coverage.report] @@ -102,8 +96,60 @@ skip_empty = true pgsql = ["psycopg2-binary"] [tool.mypy] -python_version = "3.10" -ignore_missing_imports = true follow_imports = "skip" -strict_optional = true +ignore_missing_imports = true plugins = "pydantic.mypy" +python_version = "3.10" +strict_optional = true + +[tool.ruff] +line-length = 120 +format = "text" + +# Enable Pyflakes `E` and `F` codes by default. +ignore = ["F403", "I252","B008"] +select = [ + "E", # pycodestyles + "F", # pyflakes + "I", # isort + "T", # flake8-print + "U", # pyupgrade + "B", # flake8-bugbear + # "ANN", # flake8-annotations + # "C", # McCabe complexity + # "RUF", # Ruff specific + # "BLE", # blind-except +] + +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".hg", + ".mypy_cache", + ".nox", + ".pants.d", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "venv", +] + +# Assume Python 3.10. +target-version = "py310" + +[tool.ruff.per-file-ignores] +"__init__.py" = ["E402","E501"] + +[tool.ruff.mccabe] +# Unlike Flake8, default to a complexity level of 10. +max-complexity = 10