mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-01-02 15:11:24 -05:00
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
This commit is contained in:
@@ -1 +1,6 @@
|
||||
from .email_service import EmailService, EmailTemplate
|
||||
|
||||
__all__ = [
|
||||
"EmailService",
|
||||
"EmailTemplate",
|
||||
]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
from .process import parse
|
||||
|
||||
__all__ = [
|
||||
"parse",
|
||||
]
|
||||
|
||||
@@ -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]:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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.
|
||||
|
||||
<img height="100" src="https://images.unsplash.com/photo-1567620905732-2d1ec7ab7445?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=960&q=80"></img>
|
||||
|
||||
"""
|
||||
|
||||
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"
|
||||
|
||||
@@ -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.
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user