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
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
62 changed files with 362 additions and 536 deletions

10
.flake8
View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 <b>{APP_VERSION}</b>, Mealie is still in rapid development and therefore some of these APIs may change from version to version.
As of this release <b>{APP_VERSION}</b>, 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(

View file

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

View file

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

View file

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

View file

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

View file

@ -1,2 +1,8 @@
from .auto_init import auto_init
from .guid import GUID
__all__ = [
"auto_init",
"GUID",
]

View file

@ -17,7 +17,7 @@ class Translator(Protocol):
pass
@lru_cache()
@lru_cache
def _load_factory() -> i18n.ProviderFactory:
return i18n.ProviderFactory(
directory=TRANSLATIONS,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,3 +1 @@
from typing import Optional
NoneFloat = Optional[float]
NoneFloat = float | None

View file

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

View file

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

View file

@ -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] = []

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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] = []

View file

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

View file

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

View file

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

View file

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

View file

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

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

331
poetry.lock generated
View file

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

View file

@ -1,72 +1,71 @@
[tool.poetry]
authors = ["Hayden <hay-kot@pm.me>"]
description = "A Recipe Manager"
license = "AGPL"
name = "mealie"
version = "1.0.0b"
description = "A Recipe Manager"
authors = ["Hayden <hay-kot@pm.me>"]
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,16 +79,11 @@ 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",
@ -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