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:
Hayden
2022-11-30 20:20:28 -09:00
committed by GitHub
parent fd0e02a5c6
commit 82dc586bac
62 changed files with 362 additions and 536 deletions

View File

@@ -1 +1,6 @@
from .email_service import EmailService, EmailTemplate
__all__ = [
"EmailService",
"EmailTemplate",
]

View File

@@ -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)

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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):

View File

@@ -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

View File

@@ -1 +1,5 @@
from .process import parse
__all__ = [
"parse",
]

View File

@@ -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]:

View File

@@ -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.

View File

@@ -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):

View File

@@ -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"

View File

@@ -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.
"""

View File

@@ -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

View File

@@ -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}")

View File

@@ -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