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