├── .gitignore ├── .pre-commit-config.yaml ├── README.md ├── backend ├── .env ├── alembic.ini ├── apps │ ├── __init__.py │ ├── blog │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apis.py │ │ ├── jobs.py │ │ └── models.py │ └── demo │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apis.py │ │ ├── crud.py │ │ ├── jobs.py │ │ ├── models.py │ │ ├── schemas.py │ │ └── tests.py ├── core │ ├── __init__.py │ ├── auth.py │ ├── globals.py │ └── settings.py ├── main.py ├── migrations │ ├── README │ ├── env.py │ └── script.py.mako └── upload │ └── 202203 │ ├── 2967bf3b784b47f38c1111b059aaa472.png │ ├── 81c4e67eb1c04ae1bbaf69f0a7926b45.png │ └── 915aa926ac6b4d649e5acd9b85564186.png ├── pdm.lock └── pyproject.toml /.gitignore: -------------------------------------------------------------------------------- 1 | ### Python template 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | cover/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | .pybuilder/ 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | # For a library or package, you might want to ignore these files since the code is 88 | # intended to run in multiple environments; otherwise, check them in: 89 | # .python-version 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | #Pipfile.lock 97 | 98 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 99 | __pypackages__/ 100 | 101 | # Celery stuff 102 | celerybeat-schedule 103 | celerybeat.pid 104 | 105 | # SageMath parsed files 106 | *.sage.py 107 | 108 | # Environments 109 | #.env 110 | .venv 111 | env/ 112 | venv/ 113 | ENV/ 114 | env.bak/ 115 | venv.bak/ 116 | 117 | # Spyder project settings 118 | .spyderproject 119 | .spyproject 120 | 121 | # Rope project settings 122 | .ropeproject 123 | 124 | # mkdocs documentation 125 | /site 126 | 127 | # mypy 128 | .mypy_cache/ 129 | .dmypy.json 130 | dmypy.json 131 | 132 | # Pyre type checker 133 | .pyre/ 134 | 135 | # pytype static type analyzer 136 | .pytype/ 137 | 138 | # Cython debug symbols 139 | cython_debug/ 140 | 141 | /.idea/ 142 | *.db 143 | /.pdm.toml 144 | /.pdm-python 145 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/psf/black 3 | rev: 23.1.0 4 | hooks: 5 | - id: black 6 | 7 | - repo: https://github.com/charliermarsh/ruff-pre-commit 8 | # Ruff version. 9 | rev: 'v0.0.289' 10 | hooks: 11 | - id: ruff 12 | args: [ --fix, --exit-non-zero-on-fix ] 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FastAPI-User-Auth-Demo 2 | 3 | - [`FastAPI-Amis-Admin-Demo`](https://github.com/amisadmin/fastapi_amis_admin_demo): An example `FastAPI-Amis-Admin` application. 4 | - [`FastAPI-User-Auth-Demo`](https://github.com/amisadmin/fastapi_user_auth_demo): An example `FastAPI-User-Auth` application. 5 | 6 | ## Development 7 | 8 | ### Install command line extension 9 | 10 | `pip install fastapi_amis_admin[cli]` 11 | 12 | ### How to start 13 | 14 | 1. create your app using `faa new app_name` . 15 | 2. writing your apps under `fastapi_user_auth_demo/backend/apps` folder. 16 | 3. run your server using `faa run` . 17 | 18 | ### Documentation 19 | 20 | See [Docs](https://docs.amis.work/) 21 | 22 | ## Deployment 23 | 24 | ### Install and run: 25 | 26 | ```shell 27 | # install pdm 28 | pip install --user pdm 29 | 30 | # install dependencies 31 | pdm install 32 | 33 | # run server 34 | pdm run run 35 | ``` 36 | 37 | ## Demo 38 | 39 | You can check a online demo [here](http://user-auth.demo.amis.work/). 40 | 41 | - admin user: admin----admin 42 | - vip user: vip----vip 43 | 44 | ### Preview 45 | 46 | - Open `http://127.0.0.1:8000/admin/` in your browser: 47 | 48 | ![Login](https://s2.loli.net/2022/03/20/SZy6sjaVlBT8gin.png) 49 | 50 | - Open `http://127.0.0.1:8000/admin/` in your browser: 51 | 52 | ![ModelAdmin](https://s2.loli.net/2022/03/20/ItgFYGUONm1jCz5.png) 53 | 54 | - Open `http://127.0.0.1:6699/admin/docs` in your browser: 55 | 56 | ![Docs](https://s2.loli.net/2022/03/20/1GcCiPdmXayxrbH.png) -------------------------------------------------------------------------------- /backend/.env: -------------------------------------------------------------------------------- 1 | DEBUG=True 2 | HOST=127.0.0.1 3 | PORT=6699 4 | # sqlite 5 | DATABASE_URL=sqlite:///amisadmin.db?check_same_thread=False 6 | DATABASE_URL_ASYNC=sqlite+aiosqlite:///amisadmin.db?check_same_thread=False 7 | # mysql 8 | #DATABASE_URL=mysql+pymysql://root:123456@127.0.0.1:3306/amisadmin?charset=utf8mb4 9 | #DATABASE_URL_ASYNC=mysql+aiomysql://root:123456@127.0.0.1:3306/amisadmin?charset=utf8mb4 10 | # postgresql 11 | # DATABASE_URL=postgresql://postgres:root@127.0.0.1:5432/amisadmin 12 | #DATABASE_URL_ASYNC=postgresql+asyncpg://root:root@127.0.0.1:5432/amisadmin 13 | SECRET_KEY=x3box2cf40b8kkxcy69ltagtwulf3j829nmm10oroavd7mydlvwewcq35swp0fpn 14 | ALLOW_ORIGINS=["*"] 15 | LANGUAGE=zh_CN 16 | #LANGUAGE=en_US 17 | SITE_TITLE=FastAPI-Amis-Admin 18 | SITE_LOGO=https://baidu.gitee.io/amis/static/favicon_b3b0647.png 19 | AMIS_CDN=https://unpkg.com 20 | AMIS_PKG=amis@3.6.3 -------------------------------------------------------------------------------- /backend/alembic.ini: -------------------------------------------------------------------------------- 1 | # A generic, single database configuration. 2 | 3 | [alembic] 4 | # path to migration scripts 5 | script_location = migrations 6 | 7 | # template used to generate migration file names; The default value is %%(rev)s_%%(slug)s 8 | # Uncomment the line below if you want the files to be prepended with date and time 9 | # see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file 10 | # for all available tokens 11 | # file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s 12 | 13 | # sys.path path, will be prepended to sys.path if present. 14 | # defaults to the current working directory. 15 | prepend_sys_path = . 16 | 17 | # timezone to use when rendering the date within the migration file 18 | # as well as the filename. 19 | # If specified, requires the python-dateutil library that can be 20 | # installed by adding `alembic[tz]` to the pip requirements 21 | # string value is passed to dateutil.tz.gettz() 22 | # leave blank for localtime 23 | # timezone = 24 | 25 | # max length of characters to apply to the 26 | # "slug" field 27 | # truncate_slug_length = 40 28 | 29 | # set to 'true' to run the environment during 30 | # the 'revision' command, regardless of autogenerate 31 | # revision_environment = false 32 | 33 | # set to 'true' to allow .pyc and .pyo files without 34 | # a source .py file to be detected as revisions in the 35 | # versions/ directory 36 | # sourceless = false 37 | 38 | # version location specification; This defaults 39 | # to migrations/versions. When using multiple version 40 | # directories, initial revisions must be specified with --version-path. 41 | # The path separator used here should be the separator specified by "version_path_separator" below. 42 | # version_locations = %(here)s/bar:%(here)s/bat:migrations/versions 43 | 44 | # version path separator; As mentioned above, this is the character used to split 45 | # version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. 46 | # If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. 47 | # Valid values for version_path_separator are: 48 | # 49 | # version_path_separator = : 50 | # version_path_separator = ; 51 | # version_path_separator = space 52 | version_path_separator = os # Use os.pathsep. Default configuration used for new projects. 53 | 54 | # the output encoding used when revision files 55 | # are written from script.py.mako 56 | # output_encoding = utf-8 57 | 58 | sqlalchemy.url = '' 59 | 60 | 61 | [post_write_hooks] 62 | # post_write_hooks defines scripts or Python functions that are run 63 | # on newly generated revision scripts. See the documentation for further 64 | # detail and examples 65 | 66 | # format using "black" - use the console_scripts runner, against the "black" entrypoint 67 | # hooks = black 68 | # black.type = console_scripts 69 | # black.entrypoint = black 70 | # black.options = -l 79 REVISION_SCRIPT_FILENAME 71 | 72 | # Logging configuration 73 | [loggers] 74 | keys = root,sqlalchemy,alembic 75 | 76 | [handlers] 77 | keys = console 78 | 79 | [formatters] 80 | keys = generic 81 | 82 | [logger_root] 83 | level = WARN 84 | handlers = console 85 | qualname = 86 | 87 | [logger_sqlalchemy] 88 | level = WARN 89 | handlers = 90 | qualname = sqlalchemy.engine 91 | 92 | [logger_alembic] 93 | level = INFO 94 | handlers = 95 | qualname = alembic 96 | 97 | [handler_console] 98 | class = StreamHandler 99 | args = (sys.stderr,) 100 | level = NOTSET 101 | formatter = generic 102 | 103 | [formatter_generic] 104 | format = %(levelname)-5.5s [%(name)s] %(message)s 105 | datefmt = %H:%M:%S 106 | -------------------------------------------------------------------------------- /backend/apps/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amisadmin/fastapi-user-auth-demo/46618a0ef66027c67e991d2f7cf32d1dcd670775/backend/apps/__init__.py -------------------------------------------------------------------------------- /backend/apps/blog/__init__.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | 3 | 4 | def setup(app: FastAPI): 5 | # 1. 导入管理应用 6 | # 3. 注册普通路由 7 | # 2. 导入定时任务 8 | from . import admin, apis, jobs 9 | 10 | app.include_router(apis.router) 11 | -------------------------------------------------------------------------------- /backend/apps/blog/admin.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Dict, List 2 | 3 | from core.globals import site 4 | from fastapi_amis_admin import admin 5 | from fastapi_amis_admin.admin import ( 6 | AdminApp, 7 | FieldPermEnum, 8 | RecentTimeSelectPerm, 9 | SimpleSelectPerm, 10 | UserSelectPerm, 11 | ) 12 | from fastapi_amis_admin.amis.components import PageSchema, TableColumn 13 | from fastapi_amis_admin.crud.parser import LabelField, PropertyField 14 | from fastapi_amis_admin.models import Field 15 | from fastapi_user_auth.auth.models import User 16 | from fastapi_user_auth.mixins.admin import AuthFieldModelAdmin, AuthSelectModelAdmin 17 | from pydantic import BaseModel 18 | from sqlmodel.sql.expression import Select 19 | from starlette.requests import Request 20 | 21 | from apps.blog.models import Article, ArticleStatus, Category, Tag 22 | 23 | 24 | @site.register_admin 25 | class BlogApp(admin.AdminApp): 26 | page_schema = PageSchema(label="博客应用", icon="fa fa-wordpress") 27 | router_prefix = "/blog" 28 | 29 | def __init__(self, app: "AdminApp"): 30 | super().__init__(app) 31 | self.register_admin(CategoryAdmin, ArticleAdmin, TagAdmin) 32 | 33 | 34 | class CategoryAdmin(admin.ModelAdmin): 35 | page_schema = PageSchema(label="分类管理", icon="fa fa-folder") 36 | model = Category 37 | search_fields = [Category.name] 38 | 39 | 40 | class TagAdmin(admin.ModelAdmin): 41 | page_schema = PageSchema(label="标签管理", icon="fa fa-tags") 42 | model = Tag 43 | search_fields = [Tag.name] 44 | link_model_fields = [Tag.articles] 45 | 46 | 47 | class ArticleAdmin(AuthSelectModelAdmin, AuthFieldModelAdmin): 48 | page_schema = PageSchema(label="文章管理", icon="fa fa-file") 49 | model = Article 50 | # 配置列表展示字段 51 | list_display = [ 52 | Article.id, 53 | Article.title, 54 | Article.img, 55 | Article.status, 56 | Category.name, 57 | User.username, 58 | TableColumn(type="tpl", label="自定义模板列", tpl='ID:${id},Title:${title}'), 59 | Article.create_time, 60 | Article.description, 61 | User.nickname.label("nickname"), # 重命名字段;也可以使用sqlalchemy函数, 例如: 62 | # func.count('*').label('article_count'), 注意在`get_select`中修改对应的sql查询语句 63 | LabelField( 64 | User.nickname.label("nickname2"), 65 | Field("默认用户", title="发布者"), # 通过Field配置Amis表格列信息,Amis表单字段信息. 66 | ), 67 | ] 68 | # 配置模糊搜索字段 69 | search_fields = [Article.title, Category.name, User.username] 70 | # 配置关联模型 71 | link_model_fields = [Article.tags] 72 | # 读取查看表单字段 73 | read_fields = [ 74 | Article, 75 | PropertyField(name="category", type_=Category), 76 | PropertyField(name="user", type_=User), 77 | PropertyField(name="tags", type_=List[Tag]), 78 | ] 79 | # 数据权限 80 | select_permissions = [ 81 | # 最近7天创建的数据. reverse=True表示反向选择,即默认选择最近7天之内的数据 82 | RecentTimeSelectPerm(name="recent7_create", label="最近7天创建", td=60 * 60 * 24 * 7, reverse=True), 83 | # 最近30天创建的数据 84 | RecentTimeSelectPerm(name="recent30_create", label="最近30天创建", td=60 * 60 * 24 * 30), 85 | # 最近3天更新的数据 86 | RecentTimeSelectPerm(name="recent3_update", label="最近3天更新", td=60 * 60 * 24 * 3, time_column="update_time"), 87 | # 只能选择自己创建的数据, reverse=True表示反向选择,即默认选择自己创建的数据 88 | UserSelectPerm(name="self_create", label="自己创建", user_column="user_id", reverse=True), 89 | # # 只能选择自己更新的数据 90 | # UserSelectPerm(name="self_update", label="自己更新", user_column="update_by"), 91 | # 只能选择已发布的数据 92 | SimpleSelectPerm(name="published", label="已发布", column="status", values=[ArticleStatus.published]), 93 | # 只能选择状态为[1,2,3]的数据 94 | SimpleSelectPerm(name="status_1_2", label="状态为1_2", column="status", values=[1, 2]), 95 | ] 96 | # 字段权限 97 | perm_fields_exclude = { 98 | # 全部字段可读,无需验证权限 99 | FieldPermEnum.VIEW: ["__all__"], 100 | # 创建或更新时无需验证权限的字段 101 | FieldPermEnum.EDIT: ["description"], 102 | # 创建时无需验证权限的字段 103 | FieldPermEnum.CREATE: ["title", "description", "content", "img", "tags"], 104 | } 105 | 106 | # 自定义查询选择器 107 | async def get_select(self, request: Request) -> Select: 108 | sel = await super().get_select(request) 109 | return sel.outerjoin(Category).outerjoin(User, Article.user_id == User.id) 110 | 111 | async def on_create_pre(self, request: Request, obj: BaseModel, **kwargs) -> Dict[str, Any]: 112 | data = await super().on_create_pre(request, obj, **kwargs) 113 | # 创建新文章时,设置当前用户为发布者 114 | data["user_id"] = request.user.id 115 | return data 116 | -------------------------------------------------------------------------------- /backend/apps/blog/apis.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | from fastapi import APIRouter 4 | from fastapi_amis_admin.globals.deps import AsyncSess 5 | from fastapi_user_auth.globals.deps import CurrentUser 6 | 7 | from apps.blog.models import Article 8 | 9 | # 通过注册依赖方式验证用户权限,当前路由注册器下全部路由都将进行权限验证. 10 | router = APIRouter(prefix="/articles", tags=["ArticleAPI"]) 11 | 12 | 13 | @router.get("/update/{id}", response_model=Article, summary="更新文章") 14 | async def update_article(id: int, session: AsyncSess, user: CurrentUser): 15 | article = await session.get(Article, id) 16 | if article: 17 | article.user_id = user.id 18 | article.create_time = datetime.datetime.now() 19 | await session.flush() 20 | return article 21 | -------------------------------------------------------------------------------- /backend/apps/blog/jobs.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from core.globals import scheduler 4 | 5 | 6 | # 添加定时任务,参考官方文档: https://apscheduler.readthedocs.io/en/master/ 7 | # use when you want to run the job at fixed intervals of time 8 | @scheduler.scheduled_job("interval", seconds=300) 9 | def interval_task_test(): 10 | print("blog interval task is run...") 11 | 12 | 13 | # use when you want to run the job periodically at certain time(s) of day 14 | @scheduler.scheduled_job("cron", hour=3, minute=30) 15 | def cron_task_test(): 16 | print("blog cron task is run...") 17 | 18 | 19 | # use when you want to run the job just once at a certain point of time 20 | @scheduler.scheduled_job("date", run_date=datetime(2022, 11, 11, 1, 1, 1)) 21 | def date_task_test(): 22 | print("blog date task is run...") 23 | -------------------------------------------------------------------------------- /backend/apps/blog/models.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from typing import List, Optional 3 | 4 | from fastapi_amis_admin.amis.components import ColumnImage, InputImage, InputRichText 5 | from fastapi_amis_admin.models import ChoiceType,IntegerChoices,Field 6 | from fastapi_user_auth.auth.models import User 7 | from fastapi_user_auth.mixins.models import PkMixin 8 | from sqlalchemy import Column, String, select 9 | from sqlalchemy.orm import Session 10 | from sqlmodel import Relationship 11 | from sqlmodelx import SQLModel 12 | 13 | 14 | class ArticleStatus(IntegerChoices): 15 | unpublished = 0, "未发布" 16 | published = 1, "已发布" 17 | inspection = 2, "审核中" 18 | disabled = 3, "已禁用" 19 | 20 | 21 | # Create your models here. 22 | 23 | 24 | class Category(PkMixin, table=True): 25 | __tablename__ = "blog_category" 26 | name: str = Field(title="CategoryName", sa_column=Column(String(100), unique=True, index=True, nullable=False)) 27 | description: str = Field(default="", title="Description", amis_form_item="textarea") 28 | status: bool = Field(False, title="status") 29 | articles: List["Article"] = Relationship(back_populates="category") 30 | 31 | 32 | class ArticleTagLink(SQLModel, table=True): 33 | __tablename__ = "blog_article_tags" 34 | tag_id: Optional[int] = Field(default=None, foreign_key="blog_tag.id", primary_key=True) 35 | article_id: Optional[int] = Field(default=None, foreign_key="blog_article.id", primary_key=True) 36 | 37 | 38 | class Tag(PkMixin, table=True): 39 | __tablename__ = "blog_tag" 40 | name: str = Field(..., title="TagName", sa_column=Column(String(255), unique=True, index=True, nullable=False)) 41 | articles: List["Article"] = Relationship(back_populates="tags", link_model=ArticleTagLink) 42 | 43 | 44 | class Article(PkMixin, table=True): 45 | __tablename__ = "blog_article" 46 | title: str = Field(title="ArticleTitle", max_length=200) 47 | img: Optional[str] = Field( 48 | None, 49 | title="ArticleImage", 50 | max_length=300, 51 | amis_form_item=InputImage(maxLength=1, maxSize=2 * 1024 * 1024, receiver="post:/admin/file/upload"), 52 | amis_table_column=ColumnImage(width=100, height=60, enlargeAble=True), 53 | ) 54 | description: str = Field(default="", title="ArticleDescription", amis_form_item="textarea") 55 | status: ArticleStatus = Field(ArticleStatus.unpublished, title="status",sa_type=ChoiceType(ArticleStatus)) 56 | content: str = Field(..., title="ArticleContent", amis_form_item=InputRichText()) 57 | create_time: Optional[datetime] = Field(default_factory=datetime.utcnow, title="CreateTime") 58 | source: str = Field(default="", title="ArticleSource", max_length=200) 59 | 60 | category_id: Optional[int] = Field(default=None, foreign_key="blog_category.id", title="CategoryId") 61 | 62 | user_id: Optional[int] = Field(default=None, foreign_key="auth_user.id", title="UserId") 63 | 64 | category: Optional[Category] = Relationship(back_populates="articles") 65 | 66 | tags: List[Tag] = Relationship(back_populates="articles", link_model=ArticleTagLink) 67 | 68 | user: User = Relationship() 69 | 70 | @staticmethod 71 | def check_update_permission(session: Session, user: User, item_id: List[str]): 72 | # 管理员可以修改全部文章, 并且可以批量修改. 73 | if user.has_requires(session, roles=["admin"]): 74 | return True 75 | # 非管理员,只能修改自己的文章,并且不可批量修改. 76 | if len(item_id) > 1: 77 | return False 78 | stmt = select(1).where(Article.id == item_id[0], Article.user_id == user.id) 79 | return bool(session.scalar(stmt)) 80 | -------------------------------------------------------------------------------- /backend/apps/demo/__init__.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | 3 | 4 | def setup(app: FastAPI): 5 | # 1. 导入管理应用 6 | # 3. 注册普通路由 7 | # 2. 导入定时任务 8 | from . import admin, apis, jobs 9 | 10 | app.include_router(apis.router) 11 | -------------------------------------------------------------------------------- /backend/apps/demo/admin.py: -------------------------------------------------------------------------------- 1 | from core.globals import site 2 | from fastapi_amis_admin import admin, amis 3 | from fastapi_amis_admin.admin import AdminApp 4 | 5 | # from .models import Category 6 | 7 | 8 | # @site.register_admin 9 | class DemoApp(admin.AdminApp): 10 | page_schema = amis.PageSchema(label="Demo", icon="fa fa-bolt") 11 | router_prefix = "/demo" 12 | 13 | def __init__(self, app: "AdminApp"): 14 | super().__init__(app) 15 | # self.register_admin(CategoryAdmin) 16 | 17 | 18 | # Register your models here. 19 | 20 | # class CategoryAdmin(admin.ModelAdmin): 21 | # page_schema = amis.PageSchema(label='Category', icon='fa fa-folder') 22 | # model = Category 23 | # search_fields = [Category.name] 24 | 25 | 26 | @site.register_admin 27 | class AmisEditorAdmin(admin.IframeAdmin): 28 | page_schema = amis.PageSchema(label="AmisEditorDemo", icon="fa fa-edit", sort=-100) 29 | src = "https://aisuda.github.io/amis-editor-demo/" 30 | -------------------------------------------------------------------------------- /backend/apps/demo/apis.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter 2 | 3 | router = APIRouter(prefix="/demo", tags=["Demo"]) 4 | 5 | 6 | @router.get("/hello") 7 | async def hello(name: str = "") -> str: 8 | return f"hello {name}" 9 | -------------------------------------------------------------------------------- /backend/apps/demo/crud.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amisadmin/fastapi-user-auth-demo/46618a0ef66027c67e991d2f7cf32d1dcd670775/backend/apps/demo/crud.py -------------------------------------------------------------------------------- /backend/apps/demo/jobs.py: -------------------------------------------------------------------------------- 1 | # from datetime import datetime 2 | # 3 | # from core.adminsite import scheduler 4 | 5 | 6 | # # use when you want to run the job at fixed intervals of time 7 | # @scheduler.scheduled_job('interval', seconds=300) 8 | # def interval_task_test(): 9 | # print('interval task is run...') 10 | # 11 | # 12 | # # use when you want to run the job periodically at certain time(s) of day 13 | # @scheduler.scheduled_job('cron', hour=3, minute=30) 14 | # def cron_task_test(): 15 | # print('cron task is run...') 16 | # 17 | # 18 | # # use when you want to run the job just once at a certain point of time 19 | # @scheduler.scheduled_job('date', run_date=datetime(2022, 11, 11, 1, 1, 1)) 20 | # def date_task_test(): 21 | # print('date task is run...') 22 | -------------------------------------------------------------------------------- /backend/apps/demo/models.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Create your models here. 4 | 5 | 6 | # class Category(PkMixin, table=True): 7 | # __tablename__ = 'blog_category' 8 | # name: str = Field( 9 | # title='Category Name', 10 | # sa_column=sqlmodel.Column(sqlmodel.String(100), unique=True, index=True, nullable=False) 11 | # ) 12 | # description: str = Field(default='', title='Description', amis_form_item=amis.Textarea()) 13 | # is_active: bool = Field(None, title='Is Active') 14 | -------------------------------------------------------------------------------- /backend/apps/demo/schemas.py: -------------------------------------------------------------------------------- 1 | 2 | # Create your schemas here. 3 | -------------------------------------------------------------------------------- /backend/apps/demo/tests.py: -------------------------------------------------------------------------------- 1 | # Create your tests here. 2 | -------------------------------------------------------------------------------- /backend/core/__init__.py: -------------------------------------------------------------------------------- 1 | from fastapi_amis_admin import i18n 2 | 3 | from core.settings import settings 4 | 5 | i18n.set_language(settings.language) 6 | -------------------------------------------------------------------------------- /backend/core/auth.py: -------------------------------------------------------------------------------- 1 | from datetime import date 2 | from typing import Optional 3 | 4 | from fastapi_amis_admin.models.fields import Field 5 | from fastapi_user_auth.admin import AuthAdminSite, RoleAdmin, UserAuthApp 6 | from fastapi_user_auth.auth.models import BaseRole, BaseUser 7 | 8 | # 需要安装sqlmodelx. pip install sqlmodelx 9 | class MyUser(BaseUser, table=True): 10 | point: int = Field(default=0, title="积分", description="用户积分") 11 | phone: str = Field("", title="手机号", max_length=15) 12 | parent_id: Optional[int] = Field(None, title="上级", foreign_key="auth_user.id") 13 | birthday: Optional[date] = Field(None, title="出生日期") 14 | location: str = Field("", title="位置") 15 | 16 | 17 | class MyRole(BaseRole, table=True): 18 | __tablename__ = "auth_role" # 数据库表名,必须是这个才能覆盖默认模型 19 | icon: str = Field("", title="图标") 20 | is_active: bool = Field(default=True, title="是否激活") 21 | 22 | 23 | class MyRoleAdmin(RoleAdmin): 24 | model = MyRole 25 | 26 | 27 | # class MyUserRoleLink(UserRoleLink, CreateTimeMixin, table=True): 28 | # # 重写UserRoleLink模型, 并且创建id虚拟主键字段 29 | # id: str = Field( 30 | # None, title="虚拟主键", 31 | # sa_column=column_property( 32 | # # 注意这里的cast, 用于将联合主键转换为字符串.不要使用format, 会导致直接识别为字符串. 33 | # cast(UserRoleLink.user_id, String) + "-" + cast(UserRoleLink.role_id, String) 34 | # ) 35 | # ) 36 | # 37 | # description: str = Field(None, title="描述") 38 | 39 | 40 | class MyUserAuthApp(UserAuthApp): 41 | RoleAdmin = MyRoleAdmin 42 | 43 | 44 | class MyAuthAdminSite(AuthAdminSite): 45 | UserAuthApp = MyUserAuthApp 46 | -------------------------------------------------------------------------------- /backend/core/globals.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from sqlalchemy_database import AsyncDatabase, Database 4 | 5 | from core.auth import MyAuthAdminSite 6 | from core.settings import settings 7 | 8 | # 创建异步数据库引擎 9 | async_db = AsyncDatabase.create( 10 | url=settings.database_url_async, 11 | session_options={ 12 | "expire_on_commit": False, 13 | }, 14 | ) 15 | # 创建同步数据库引擎 16 | sync_db = Database.create( 17 | url=settings.database_url, 18 | session_options={ 19 | "expire_on_commit": False, 20 | }, 21 | ) 22 | 23 | # from fastapi_user_auth.auth import Auth 24 | # from fastapi_user_auth.auth.backends.jwt import JwtTokenStore 25 | # 使用`JwtTokenStore`创建auth对象 26 | # auth = Auth( 27 | # db=async_db, 28 | # token_store=JwtTokenStore(secret_key='09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7') 29 | # ) 30 | # 31 | # # 创建后台管理系统,在导入路由之前先实例化对象 32 | # from fastapi_user_auth.site import AuthAdminSite 33 | # site = AuthAdminSite(settings, auth=auth) 34 | 35 | site = MyAuthAdminSite(settings, engine=async_db) 36 | auth = site.auth 37 | 38 | from fastapi_scheduler import SchedulerAdmin 39 | 40 | # # 自定义定时任务调度器 41 | # from apscheduler.schedulers.asyncio import AsyncIOScheduler 42 | # from apscheduler.jobstores.redis import RedisJobStore 43 | # # 使用`RedisJobStore`创建任务存储 44 | # scheduler = AsyncIOScheduler(jobstores={'default':RedisJobStore(db=2,host="127.0.0.1",port=6379,password="test")}) 45 | # scheduler = SchedulerAdmin.bind(site,scheduler=scheduler) 46 | 47 | # 创建定时任务调度器`SchedulerAdmin`实例 48 | 49 | scheduler = SchedulerAdmin.bind(site) 50 | 51 | 52 | # 添加定时任务,参考官方文档: https://apscheduler.readthedocs.io/en/master/ 53 | # use when you want to run the job at fixed intervals of time 54 | @scheduler.scheduled_job("interval", seconds=300) 55 | def interval_task_test(): 56 | print("interval task is run...") 57 | 58 | 59 | # use when you want to run the job periodically at certain time(s) of day 60 | @scheduler.scheduled_job("cron", hour=3, minute=30) 61 | def cron_task_test(): 62 | print("cron task is run...") 63 | 64 | 65 | # use when you want to run the job just once at a certain point of time 66 | @scheduler.scheduled_job("date", run_date=datetime(2022, 11, 11, 1, 2, 3)) 67 | def date_task_test(): 68 | print("date task is run...") 69 | -------------------------------------------------------------------------------- /backend/core/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from pathlib import Path 4 | from typing import List 5 | 6 | from fastapi_amis_admin import admin 7 | 8 | BACKEND_DIR = Path(__file__).resolve().parent.parent 9 | sys.path.append(BACKEND_DIR.__str__()) 10 | 11 | 12 | class Settings(admin.Settings): 13 | name: str = "FastAPI-User-Auth-Demo" 14 | secret_key: str = "" 15 | allow_origins: List[str] = [] 16 | 17 | 18 | # 设置FAA_GLOBALS环境变量 19 | os.environ.setdefault("FAA_GLOBALS", "core.globals") 20 | 21 | settings = Settings(_env_file=os.path.join(BACKEND_DIR, ".env")) 22 | -------------------------------------------------------------------------------- /backend/main.py: -------------------------------------------------------------------------------- 1 | from core.globals import auth, scheduler, site 2 | from core.settings import settings 3 | from fastapi import FastAPI 4 | from fastapi_amis_admin_nav.admin import NavPageAdmin 5 | from sqlmodel import SQLModel 6 | from starlette.responses import RedirectResponse 7 | 8 | # 创建FastAPI实例 9 | app = FastAPI(debug=settings.debug) 10 | 11 | # 安装应用demo 12 | from apps import demo 13 | 14 | demo.setup(app) 15 | # 安装应用blog 16 | from apps import blog 17 | 18 | blog.setup(app) 19 | 20 | site.register_admin(NavPageAdmin) 21 | 22 | # 挂载后台管理系统 23 | site.mount_app(app) 24 | 25 | 26 | # 注意1: site.mount_app会默认添加site.db的session会话上下文中间件,如果你使用了其他的数据库连接,请自行添加.例如: 27 | # from core.globals import sync_db 28 | # app.add_middleware(sync_db.asgi_middleware) # 注意中间件的注册顺序. 29 | 30 | # 注意2: 非请求上下文中,请自行创建session会话,例如:定时任务,测试脚本等. 31 | # from core.globals import async_db 32 | # async with async_db(): 33 | # async_db.async_get(...) 34 | # async_db.session.get(...) 35 | # # do something 36 | 37 | 38 | # 添加启动运行事件 39 | @app.on_event("startup") 40 | async def startup(): 41 | await site.db.async_run_sync(SQLModel.metadata.create_all, is_session=False) 42 | # 创建默认管理员,用户名: admin,密码: admin, 请及时修改密码!!! 43 | await auth.create_role_user("admin") 44 | # 创建默认超级管理员,用户名: root,密码: root, 请及时修改密码!!! 45 | await auth.create_role_user("root") 46 | # 运行site的startup方法,加载casbin策略等 47 | await site.router.startup() 48 | if not auth.enforcer.enforce("u:admin", site.unique_id, "page", "page"): 49 | await auth.enforcer.add_policy("u:admin", site.unique_id, "page", "page", "allow") 50 | # 启动定时任务 51 | scheduler.start() 52 | 53 | 54 | # 注册首页路由 55 | @app.get("/") 56 | async def index(): 57 | return RedirectResponse(url=site.router_path) 58 | -------------------------------------------------------------------------------- /backend/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration with an async dbapi. -------------------------------------------------------------------------------- /backend/migrations/env.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from logging.config import fileConfig 3 | 4 | from alembic import context 5 | from sqlalchemy import engine_from_config, pool 6 | 7 | # this is the Alembic Config object, which provides 8 | # access to the values within the .ini file in use. 9 | config = context.config 10 | 11 | # Interpret the config file for Python logging. 12 | # This line sets up loggers basically. 13 | if config.config_file_name is not None: 14 | fileConfig(config.config_file_name) 15 | 16 | import main # 导入全部模型 17 | 18 | # 导入SQLModel 19 | from sqlmodel import SQLModel 20 | 21 | # 设置metadata 22 | target_metadata = SQLModel.metadata 23 | 24 | 25 | # other values from the config, defined by the needs of env.py, 26 | # can be acquired: 27 | # my_important_option = config.get_main_option("my_important_option") 28 | # ... etc. 29 | 30 | 31 | def run_migrations_offline(): 32 | """Run migrations in 'offline' mode. 33 | 34 | This configures the context with just a URL 35 | and not an Engine, though an Engine is acceptable 36 | here as well. By skipping the Engine creation 37 | we don't even need a DBAPI to be available. 38 | 39 | Calls to context.execute() here emit the given string to the 40 | script output. 41 | 42 | """ 43 | url = config.get_main_option("sqlalchemy.url") 44 | context.configure( 45 | url=url, 46 | target_metadata=target_metadata, 47 | literal_binds=True, 48 | dialect_opts={"paramstyle": "named"}, 49 | ) 50 | 51 | with context.begin_transaction(): 52 | context.run_migrations() 53 | 54 | 55 | def do_run_migrations(connection): 56 | context.configure(connection=connection, target_metadata=target_metadata) 57 | 58 | with context.begin_transaction(): 59 | context.run_migrations() 60 | 61 | 62 | async def run_migrations_online(): 63 | """Run migrations in 'online' mode. 64 | 65 | In this scenario we need to create an Engine 66 | and associate a connection with the context. 67 | 68 | """ 69 | configuration = config.get_section(config.config_ini_section) 70 | configuration["sqlalchemy.url"] = main.settings.database_url # 更新数据库连接 71 | connectable = engine_from_config( 72 | configuration, 73 | prefix="sqlalchemy.", 74 | poolclass=pool.NullPool, 75 | ) 76 | 77 | with connectable.connect() as connection: 78 | context.configure(connection=connection, target_metadata=target_metadata) 79 | 80 | with context.begin_transaction(): 81 | context.run_migrations() 82 | 83 | 84 | if context.is_offline_mode(): 85 | run_migrations_offline() 86 | else: 87 | asyncio.run(run_migrations_online()) 88 | -------------------------------------------------------------------------------- /backend/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | import sqlmodel 11 | ${imports if imports else ""} 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = ${repr(up_revision)} 15 | down_revision = ${repr(down_revision)} 16 | branch_labels = ${repr(branch_labels)} 17 | depends_on = ${repr(depends_on)} 18 | 19 | 20 | def upgrade(): 21 | ${upgrades if upgrades else "pass"} 22 | 23 | 24 | def downgrade(): 25 | ${downgrades if downgrades else "pass"} 26 | -------------------------------------------------------------------------------- /backend/upload/202203/2967bf3b784b47f38c1111b059aaa472.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amisadmin/fastapi-user-auth-demo/46618a0ef66027c67e991d2f7cf32d1dcd670775/backend/upload/202203/2967bf3b784b47f38c1111b059aaa472.png -------------------------------------------------------------------------------- /backend/upload/202203/81c4e67eb1c04ae1bbaf69f0a7926b45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amisadmin/fastapi-user-auth-demo/46618a0ef66027c67e991d2f7cf32d1dcd670775/backend/upload/202203/81c4e67eb1c04ae1bbaf69f0a7926b45.png -------------------------------------------------------------------------------- /backend/upload/202203/915aa926ac6b4d649e5acd9b85564186.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amisadmin/fastapi-user-auth-demo/46618a0ef66027c67e991d2f7cf32d1dcd670775/backend/upload/202203/915aa926ac6b4d649e5acd9b85564186.png -------------------------------------------------------------------------------- /pdm.lock: -------------------------------------------------------------------------------- 1 | # This file is @generated by PDM. 2 | # It is not intended for manual editing. 3 | 4 | [metadata] 5 | groups = ["default", "dev"] 6 | strategy = ["cross_platform"] 7 | lock_version = "4.4.1" 8 | content_hash = "sha256:6a1f6512c5f4c3c0040e7988ef59670754b526cb0f35106ea1d3808b0d2d7034" 9 | 10 | [[package]] 11 | name = "aiofiles" 12 | version = "23.1.0" 13 | requires_python = ">=3.7,<4.0" 14 | summary = "File support for asyncio." 15 | files = [ 16 | {file = "aiofiles-23.1.0-py3-none-any.whl", hash = "sha256:9312414ae06472eb6f1d163f555e466a23aed1c8f60c30cccf7121dba2e53eb2"}, 17 | {file = "aiofiles-23.1.0.tar.gz", hash = "sha256:edd247df9a19e0db16534d4baaf536d6609a43e1de5401d7a4c1c148753a1635"}, 18 | ] 19 | 20 | [[package]] 21 | name = "aiosqlite" 22 | version = "0.17.0" 23 | requires_python = ">=3.6" 24 | summary = "asyncio bridge to the standard sqlite3 module" 25 | dependencies = [ 26 | "typing-extensions>=3.7.2", 27 | ] 28 | files = [ 29 | {file = "aiosqlite-0.17.0-py3-none-any.whl", hash = "sha256:6c49dc6d3405929b1d08eeccc72306d3677503cc5e5e43771efc1e00232e8231"}, 30 | {file = "aiosqlite-0.17.0.tar.gz", hash = "sha256:f0e6acc24bc4864149267ac82fb46dfb3be4455f99fe21df82609cc6e6baee51"}, 31 | ] 32 | 33 | [[package]] 34 | name = "alembic" 35 | version = "1.7.7" 36 | requires_python = ">=3.6" 37 | summary = "A database migration tool for SQLAlchemy." 38 | dependencies = [ 39 | "Mako", 40 | "SQLAlchemy>=1.3.0", 41 | "importlib-metadata; python_version < \"3.9\"", 42 | "importlib-resources; python_version < \"3.9\"", 43 | ] 44 | files = [ 45 | {file = "alembic-1.7.7-py3-none-any.whl", hash = "sha256:29be0856ec7591c39f4e1cb10f198045d890e6e2274cf8da80cb5e721a09642b"}, 46 | {file = "alembic-1.7.7.tar.gz", hash = "sha256:4961248173ead7ce8a21efb3de378f13b8398e6630fab0eb258dc74a8af24c58"}, 47 | ] 48 | 49 | [[package]] 50 | name = "annotated-types" 51 | version = "0.6.0" 52 | requires_python = ">=3.8" 53 | summary = "Reusable constraint types to use with typing.Annotated" 54 | dependencies = [ 55 | "typing-extensions>=4.0.0; python_version < \"3.9\"", 56 | ] 57 | files = [ 58 | {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, 59 | {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, 60 | ] 61 | 62 | [[package]] 63 | name = "anyio" 64 | version = "3.7.1" 65 | requires_python = ">=3.7" 66 | summary = "High level compatibility layer for multiple asynchronous event loop implementations" 67 | dependencies = [ 68 | "exceptiongroup; python_version < \"3.11\"", 69 | "idna>=2.8", 70 | "sniffio>=1.1", 71 | ] 72 | files = [ 73 | {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, 74 | {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, 75 | ] 76 | 77 | [[package]] 78 | name = "apscheduler" 79 | version = "3.10.0" 80 | requires_python = ">=3.6" 81 | summary = "In-process task scheduler with Cron-like capabilities" 82 | dependencies = [ 83 | "pytz", 84 | "setuptools>=0.7", 85 | "six>=1.4.0", 86 | "tzlocal!=3.*,>=2.0", 87 | ] 88 | files = [ 89 | {file = "APScheduler-3.10.0-py3-none-any.whl", hash = "sha256:575299f20073c60a2cc9d4fa5906024cdde33c5c0ce6087c4e3c14be3b50fdd4"}, 90 | {file = "APScheduler-3.10.0.tar.gz", hash = "sha256:a49fc23269218416f0e41890eea7a75ed6b284f10630dcfe866ab659621a3696"}, 91 | ] 92 | 93 | [[package]] 94 | name = "backports-zoneinfo" 95 | version = "0.2.1" 96 | requires_python = ">=3.6" 97 | summary = "Backport of the standard library zoneinfo module" 98 | files = [ 99 | {file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"}, 100 | {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"}, 101 | {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"}, 102 | {file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"}, 103 | {file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"}, 104 | {file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"}, 105 | ] 106 | 107 | [[package]] 108 | name = "bcrypt" 109 | version = "4.0.1" 110 | requires_python = ">=3.6" 111 | summary = "Modern password hashing for your software and your servers" 112 | files = [ 113 | {file = "bcrypt-4.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:b1023030aec778185a6c16cf70f359cbb6e0c289fd564a7cfa29e727a1c38f8f"}, 114 | {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:08d2947c490093a11416df18043c27abe3921558d2c03e2076ccb28a116cb6d0"}, 115 | {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0eaa47d4661c326bfc9d08d16debbc4edf78778e6aaba29c1bc7ce67214d4410"}, 116 | {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae88eca3024bb34bb3430f964beab71226e761f51b912de5133470b649d82344"}, 117 | {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:a522427293d77e1c29e303fc282e2d71864579527a04ddcfda6d4f8396c6c36a"}, 118 | {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:fbdaec13c5105f0c4e5c52614d04f0bca5f5af007910daa8b6b12095edaa67b3"}, 119 | {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ca3204d00d3cb2dfed07f2d74a25f12fc12f73e606fcaa6975d1f7ae69cacbb2"}, 120 | {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535"}, 121 | {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e9a51bbfe7e9802b5f3508687758b564069ba937748ad7b9e890086290d2f79e"}, 122 | {file = "bcrypt-4.0.1-cp36-abi3-win32.whl", hash = "sha256:2caffdae059e06ac23fce178d31b4a702f2a3264c20bfb5ff541b338194d8fab"}, 123 | {file = "bcrypt-4.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:8a68f4341daf7522fe8d73874de8906f3a339048ba406be6ddc1b3ccb16fc0d9"}, 124 | {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf4fa8b2ca74381bb5442c089350f09a3f17797829d958fad058d6e44d9eb83c"}, 125 | {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:67a97e1c405b24f19d08890e7ae0c4f7ce1e56a712a016746c8b2d7732d65d4b"}, 126 | {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b3b85202d95dd568efcb35b53936c5e3b3600c7cdcc6115ba461df3a8e89f38d"}, 127 | {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbb03eec97496166b704ed663a53680ab57c5084b2fc98ef23291987b525cb7d"}, 128 | {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:5ad4d32a28b80c5fa6671ccfb43676e8c1cc232887759d1cd7b6f56ea4355215"}, 129 | {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b57adba8a1444faf784394de3436233728a1ecaeb6e07e8c22c8848f179b893c"}, 130 | {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b2cea8a9ed3d55b4491887ceadb0106acf7c6387699fca771af56b1cdeeda"}, 131 | {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:2b3ac11cf45161628f1f3733263e63194f22664bf4d0c0f3ab34099c02134665"}, 132 | {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3100851841186c25f127731b9fa11909ab7b1df6fc4b9f8353f4f1fd952fbf71"}, 133 | {file = "bcrypt-4.0.1.tar.gz", hash = "sha256:27d375903ac8261cfe4047f6709d16f7d18d39b1ec92aaf72af989552a650ebd"}, 134 | ] 135 | 136 | [[package]] 137 | name = "casbin" 138 | version = "1.33.0" 139 | requires_python = ">=3.3" 140 | summary = "An authorization library that supports access control models like ACL, RBAC, ABAC in Python" 141 | dependencies = [ 142 | "simpleeval>=0.9.11", 143 | ] 144 | files = [ 145 | {file = "casbin-1.33.0-py3-none-any.whl", hash = "sha256:b5f6f442df4561305d194328ef873b2c7bd48dd5fdcfdd4b0624df75eb451e61"}, 146 | {file = "casbin-1.33.0.tar.gz", hash = "sha256:62021cc8db952cf6f0ebe8d2dad29b6bc34a39a7d31efc7b83b8dc70ca5130f8"}, 147 | ] 148 | 149 | [[package]] 150 | name = "cfgv" 151 | version = "3.3.1" 152 | requires_python = ">=3.6.1" 153 | summary = "Validate configuration and produce human readable error messages." 154 | files = [ 155 | {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, 156 | {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, 157 | ] 158 | 159 | [[package]] 160 | name = "click" 161 | version = "8.1.3" 162 | requires_python = ">=3.7" 163 | summary = "Composable command line interface toolkit" 164 | dependencies = [ 165 | "colorama; platform_system == \"Windows\"", 166 | ] 167 | files = [ 168 | {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, 169 | {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, 170 | ] 171 | 172 | [[package]] 173 | name = "colorama" 174 | version = "0.4.6" 175 | requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 176 | summary = "Cross-platform colored terminal text." 177 | files = [ 178 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 179 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 180 | ] 181 | 182 | [[package]] 183 | name = "distlib" 184 | version = "0.3.6" 185 | summary = "Distribution utilities" 186 | files = [ 187 | {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, 188 | {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, 189 | ] 190 | 191 | [[package]] 192 | name = "dnspython" 193 | version = "2.3.0" 194 | requires_python = ">=3.7,<4.0" 195 | summary = "DNS toolkit" 196 | files = [ 197 | {file = "dnspython-2.3.0-py3-none-any.whl", hash = "sha256:89141536394f909066cabd112e3e1a37e4e654db00a25308b0f130bc3152eb46"}, 198 | {file = "dnspython-2.3.0.tar.gz", hash = "sha256:224e32b03eb46be70e12ef6d64e0be123a64e621ab4c0822ff6d450d52a540b9"}, 199 | ] 200 | 201 | [[package]] 202 | name = "email-validator" 203 | version = "1.3.1" 204 | requires_python = ">=3.5" 205 | summary = "A robust email address syntax and deliverability validation library." 206 | dependencies = [ 207 | "dnspython>=1.15.0", 208 | "idna>=2.0.0", 209 | ] 210 | files = [ 211 | {file = "email_validator-1.3.1-py2.py3-none-any.whl", hash = "sha256:49a72f5fa6ed26be1c964f0567d931d10bf3fdeeacdf97bc26ef1cd2a44e0bda"}, 212 | {file = "email_validator-1.3.1.tar.gz", hash = "sha256:d178c5c6fa6c6824e9b04f199cf23e79ac15756786573c190d2ad13089411ad2"}, 213 | ] 214 | 215 | [[package]] 216 | name = "exceptiongroup" 217 | version = "1.2.0" 218 | requires_python = ">=3.7" 219 | summary = "Backport of PEP 654 (exception groups)" 220 | files = [ 221 | {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, 222 | {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, 223 | ] 224 | 225 | [[package]] 226 | name = "fastapi" 227 | version = "0.105.0" 228 | requires_python = ">=3.8" 229 | summary = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" 230 | dependencies = [ 231 | "anyio<4.0.0,>=3.7.1", 232 | "pydantic!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0,>=1.7.4", 233 | "starlette<0.28.0,>=0.27.0", 234 | "typing-extensions>=4.8.0", 235 | ] 236 | files = [ 237 | {file = "fastapi-0.105.0-py3-none-any.whl", hash = "sha256:f19ebf6fdc82a3281d10f2cb4774bdfa90238e3b40af3525a0c09fd08ad1c480"}, 238 | {file = "fastapi-0.105.0.tar.gz", hash = "sha256:4d12838819aa52af244580675825e750ad67c9df4614f557a769606af902cf22"}, 239 | ] 240 | 241 | [[package]] 242 | name = "fastapi-amis-admin" 243 | version = "0.7.0" 244 | requires_python = ">=3.8" 245 | summary = "FastAPI-Amis-Admin is a high-performance, efficient and easily extensible FastAPI admin framework. Inspired by Django-admin, and has as many powerful functions as Django-admin. " 246 | dependencies = [ 247 | "aiofiles>=0.17.0", 248 | "fastapi>=0.103.2", 249 | "python-multipart>=0.0.5", 250 | "sqlalchemy-database<0.2.0,>=0.1.1", 251 | "sqlmodel<0.1.0,>=0.0.14", 252 | ] 253 | files = [ 254 | {file = "fastapi_amis_admin-0.7.0-py3-none-any.whl", hash = "sha256:ec242e0002a1f51a94655547fa6fd09afb537b2311a02ad4f8d3b391ceefe6ca"}, 255 | {file = "fastapi_amis_admin-0.7.0.tar.gz", hash = "sha256:2c87dc98ffe7eeb8f264bd3e219cfcb1e3b9fb856bef9515e80cca0ba9f174d7"}, 256 | ] 257 | 258 | [[package]] 259 | name = "fastapi-amis-admin-nav" 260 | version = "0.1.3" 261 | requires_python = ">=3.7" 262 | summary = "FastAPI-Amis-Admin-Nav是一个基于FastAPI-Amis-Admin并且为FastAPI-Amis-Admin提供可视化导航页面管理的拓展库." 263 | dependencies = [ 264 | "fastapi-amis-admin>=0.7.0a2", 265 | ] 266 | files = [ 267 | {file = "fastapi_amis_admin_nav-0.1.3-py3-none-any.whl", hash = "sha256:1b7f99e822a3941b655831c7700363403e8af74a3a9ef43e4ede5a0794d7cb61"}, 268 | {file = "fastapi_amis_admin_nav-0.1.3.tar.gz", hash = "sha256:fcd22dbcbca8ae7aae7e09d41352fabd41e546670b20d464d54eb7b05a9e87dc"}, 269 | ] 270 | 271 | [[package]] 272 | name = "fastapi-scheduler" 273 | version = "0.0.15" 274 | requires_python = ">=3.7" 275 | summary = "FastAPI-Scheduler is a simple scheduled task management FastAPI extension based on APScheduler." 276 | dependencies = [ 277 | "APScheduler>=3.8.0", 278 | "fastapi-amis-admin<0.8.0,>=0.6.0", 279 | ] 280 | files = [ 281 | {file = "fastapi_scheduler-0.0.15-py3-none-any.whl", hash = "sha256:704793fda16f948c18dca32ba77aefee7bb762dac55beaf5e7a4224e12bb3a4b"}, 282 | {file = "fastapi_scheduler-0.0.15.tar.gz", hash = "sha256:48dc1201ee13f0cff5c19c45f6180043205d9a48cb0a5940256d8fc299d74d23"}, 283 | ] 284 | 285 | [[package]] 286 | name = "fastapi-user-auth" 287 | version = "0.7.0" 288 | requires_python = ">=3.8" 289 | summary = "FastAPI-User-Auth is a simple and powerful FastAPI user RBAC authentication and authorization library. Based on FastAPI-Amis-Admin and provides a freely extensible visual management interface." 290 | dependencies = [ 291 | "bcrypt<4.1.0,>=4.0.0", 292 | "casbin>=1.29.0", 293 | "email-validator<3.0.0,>=1.3.1", 294 | "fastapi-amis-admin<0.8.0,>=0.7.0", 295 | "passlib>=1.7.4", 296 | ] 297 | files = [ 298 | {file = "fastapi_user_auth-0.7.0-py3-none-any.whl", hash = "sha256:cbd33c9d2cb00933df261d1759b311172cad71eecf7683d3cbf4385d1184e03d"}, 299 | {file = "fastapi_user_auth-0.7.0.tar.gz", hash = "sha256:f692dd2474266239608ed46992e5e295496d779f4d7ffbd27b779cb5a94d5807"}, 300 | ] 301 | 302 | [[package]] 303 | name = "filelock" 304 | version = "3.9.0" 305 | requires_python = ">=3.7" 306 | summary = "A platform independent file lock." 307 | files = [ 308 | {file = "filelock-3.9.0-py3-none-any.whl", hash = "sha256:f58d535af89bb9ad5cd4df046f741f8553a418c01a7856bf0d173bbc9f6bd16d"}, 309 | {file = "filelock-3.9.0.tar.gz", hash = "sha256:7b319f24340b51f55a2bf7a12ac0755a9b03e718311dac567a0f4f7fabd2f5de"}, 310 | ] 311 | 312 | [[package]] 313 | name = "greenlet" 314 | version = "2.0.2" 315 | requires_python = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" 316 | summary = "Lightweight in-process concurrent programming" 317 | files = [ 318 | {file = "greenlet-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d967650d3f56af314b72df7089d96cda1083a7fc2da05b375d2bc48c82ab3f3c"}, 319 | {file = "greenlet-2.0.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:30bcf80dda7f15ac77ba5af2b961bdd9dbc77fd4ac6105cee85b0d0a5fcf74df"}, 320 | {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26fbfce90728d82bc9e6c38ea4d038cba20b7faf8a0ca53a9c07b67318d46088"}, 321 | {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9190f09060ea4debddd24665d6804b995a9c122ef5917ab26e1566dcc712ceeb"}, 322 | {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d75209eed723105f9596807495d58d10b3470fa6732dd6756595e89925ce2470"}, 323 | {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a51c9751078733d88e013587b108f1b7a1fb106d402fb390740f002b6f6551a"}, 324 | {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:76ae285c8104046b3a7f06b42f29c7b73f77683df18c49ab5af7983994c2dd91"}, 325 | {file = "greenlet-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:2d4686f195e32d36b4d7cf2d166857dbd0ee9f3d20ae349b6bf8afc8485b3645"}, 326 | {file = "greenlet-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c4302695ad8027363e96311df24ee28978162cdcdd2006476c43970b384a244c"}, 327 | {file = "greenlet-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d4606a527e30548153be1a9f155f4e283d109ffba663a15856089fb55f933e47"}, 328 | {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c48f54ef8e05f04d6eff74b8233f6063cb1ed960243eacc474ee73a2ea8573ca"}, 329 | {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1846f1b999e78e13837c93c778dcfc3365902cfb8d1bdb7dd73ead37059f0d0"}, 330 | {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a06ad5312349fec0ab944664b01d26f8d1f05009566339ac6f63f56589bc1a2"}, 331 | {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:eff4eb9b7eb3e4d0cae3d28c283dc16d9bed6b193c2e1ace3ed86ce48ea8df19"}, 332 | {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5454276c07d27a740c5892f4907c86327b632127dd9abec42ee62e12427ff7e3"}, 333 | {file = "greenlet-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:7cafd1208fdbe93b67c7086876f061f660cfddc44f404279c1585bbf3cdc64c5"}, 334 | {file = "greenlet-2.0.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:b864ba53912b6c3ab6bcb2beb19f19edd01a6bfcbdfe1f37ddd1778abfe75a30"}, 335 | {file = "greenlet-2.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1087300cf9700bbf455b1b97e24db18f2f77b55302a68272c56209d5587c12d1"}, 336 | {file = "greenlet-2.0.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:ba2956617f1c42598a308a84c6cf021a90ff3862eddafd20c3333d50f0edb45b"}, 337 | {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3a569657468b6f3fb60587e48356fe512c1754ca05a564f11366ac9e306526"}, 338 | {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8eab883b3b2a38cc1e050819ef06a7e6344d4a990d24d45bc6f2cf959045a45b"}, 339 | {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acd2162a36d3de67ee896c43effcd5ee3de247eb00354db411feb025aa319857"}, 340 | {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0bf60faf0bc2468089bdc5edd10555bab6e85152191df713e2ab1fcc86382b5a"}, 341 | {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0ef99cdbe2b682b9ccbb964743a6aca37905fda5e0452e5ee239b1654d37f2a"}, 342 | {file = "greenlet-2.0.2-cp38-cp38-win32.whl", hash = "sha256:b80f600eddddce72320dbbc8e3784d16bd3fb7b517e82476d8da921f27d4b249"}, 343 | {file = "greenlet-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:4d2e11331fc0c02b6e84b0d28ece3a36e0548ee1a1ce9ddde03752d9b79bba40"}, 344 | {file = "greenlet-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8512a0c38cfd4e66a858ddd1b17705587900dd760c6003998e9472b77b56d417"}, 345 | {file = "greenlet-2.0.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:88d9ab96491d38a5ab7c56dd7a3cc37d83336ecc564e4e8816dbed12e5aaefc8"}, 346 | {file = "greenlet-2.0.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:561091a7be172ab497a3527602d467e2b3fbe75f9e783d8b8ce403fa414f71a6"}, 347 | {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:971ce5e14dc5e73715755d0ca2975ac88cfdaefcaab078a284fea6cfabf866df"}, 348 | {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be4ed120b52ae4d974aa40215fcdfde9194d63541c7ded40ee12eb4dda57b76b"}, 349 | {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94c817e84245513926588caf1152e3b559ff794d505555211ca041f032abbb6b"}, 350 | {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1a819eef4b0e0b96bb0d98d797bef17dc1b4a10e8d7446be32d1da33e095dbb8"}, 351 | {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7efde645ca1cc441d6dc4b48c0f7101e8d86b54c8530141b09fd31cef5149ec9"}, 352 | {file = "greenlet-2.0.2-cp39-cp39-win32.whl", hash = "sha256:ea9872c80c132f4663822dd2a08d404073a5a9b5ba6155bea72fb2a79d1093b5"}, 353 | {file = "greenlet-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:db1a39669102a1d8d12b57de2bb7e2ec9066a6f2b3da35ae511ff93b01b5d564"}, 354 | {file = "greenlet-2.0.2.tar.gz", hash = "sha256:e7c8dc13af7db097bed64a051d2dd49e9f0af495c26995c00a9ee842690d34c0"}, 355 | ] 356 | 357 | [[package]] 358 | name = "h11" 359 | version = "0.14.0" 360 | requires_python = ">=3.7" 361 | summary = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" 362 | files = [ 363 | {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, 364 | {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, 365 | ] 366 | 367 | [[package]] 368 | name = "identify" 369 | version = "2.5.18" 370 | requires_python = ">=3.7" 371 | summary = "File identification library for Python" 372 | files = [ 373 | {file = "identify-2.5.18-py2.py3-none-any.whl", hash = "sha256:93aac7ecf2f6abf879b8f29a8002d3c6de7086b8c28d88e1ad15045a15ab63f9"}, 374 | {file = "identify-2.5.18.tar.gz", hash = "sha256:89e144fa560cc4cffb6ef2ab5e9fb18ed9f9b3cb054384bab4b95c12f6c309fe"}, 375 | ] 376 | 377 | [[package]] 378 | name = "idna" 379 | version = "3.4" 380 | requires_python = ">=3.5" 381 | summary = "Internationalized Domain Names in Applications (IDNA)" 382 | files = [ 383 | {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, 384 | {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, 385 | ] 386 | 387 | [[package]] 388 | name = "importlib-metadata" 389 | version = "6.0.0" 390 | requires_python = ">=3.7" 391 | summary = "Read metadata from Python packages" 392 | dependencies = [ 393 | "zipp>=0.5", 394 | ] 395 | files = [ 396 | {file = "importlib_metadata-6.0.0-py3-none-any.whl", hash = "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad"}, 397 | {file = "importlib_metadata-6.0.0.tar.gz", hash = "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d"}, 398 | ] 399 | 400 | [[package]] 401 | name = "importlib-resources" 402 | version = "5.12.0" 403 | requires_python = ">=3.7" 404 | summary = "Read resources from Python packages" 405 | dependencies = [ 406 | "zipp>=3.1.0; python_version < \"3.10\"", 407 | ] 408 | files = [ 409 | {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, 410 | {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, 411 | ] 412 | 413 | [[package]] 414 | name = "mako" 415 | version = "1.2.4" 416 | requires_python = ">=3.7" 417 | summary = "A super-fast templating language that borrows the best ideas from the existing templating languages." 418 | dependencies = [ 419 | "MarkupSafe>=0.9.2", 420 | ] 421 | files = [ 422 | {file = "Mako-1.2.4-py3-none-any.whl", hash = "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818"}, 423 | {file = "Mako-1.2.4.tar.gz", hash = "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34"}, 424 | ] 425 | 426 | [[package]] 427 | name = "markupsafe" 428 | version = "2.1.2" 429 | requires_python = ">=3.7" 430 | summary = "Safely add untrusted strings to HTML/XML markup." 431 | files = [ 432 | {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, 433 | {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, 434 | {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, 435 | {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, 436 | {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, 437 | {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, 438 | {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, 439 | {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, 440 | {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, 441 | {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, 442 | {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, 443 | {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, 444 | {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, 445 | {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, 446 | {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, 447 | {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, 448 | {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, 449 | {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, 450 | {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, 451 | {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, 452 | {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, 453 | {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, 454 | {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, 455 | {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, 456 | {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, 457 | {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, 458 | {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, 459 | {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, 460 | {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, 461 | {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, 462 | {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, 463 | {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, 464 | {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, 465 | {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, 466 | {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, 467 | {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, 468 | {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, 469 | {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, 470 | {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, 471 | {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, 472 | {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, 473 | ] 474 | 475 | [[package]] 476 | name = "nodeenv" 477 | version = "1.7.0" 478 | requires_python = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" 479 | summary = "Node.js virtual environment builder" 480 | dependencies = [ 481 | "setuptools", 482 | ] 483 | files = [ 484 | {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, 485 | {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, 486 | ] 487 | 488 | [[package]] 489 | name = "passlib" 490 | version = "1.7.4" 491 | summary = "comprehensive password hashing framework supporting over 30 schemes" 492 | files = [ 493 | {file = "passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1"}, 494 | {file = "passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04"}, 495 | ] 496 | 497 | [[package]] 498 | name = "platformdirs" 499 | version = "3.0.0" 500 | requires_python = ">=3.7" 501 | summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 502 | files = [ 503 | {file = "platformdirs-3.0.0-py3-none-any.whl", hash = "sha256:b1d5eb14f221506f50d6604a561f4c5786d9e80355219694a1b244bcd96f4567"}, 504 | {file = "platformdirs-3.0.0.tar.gz", hash = "sha256:8a1228abb1ef82d788f74139988b137e78692984ec7b08eaa6c65f1723af28f9"}, 505 | ] 506 | 507 | [[package]] 508 | name = "pre-commit" 509 | version = "3.0.4" 510 | requires_python = ">=3.8" 511 | summary = "A framework for managing and maintaining multi-language pre-commit hooks." 512 | dependencies = [ 513 | "cfgv>=2.0.0", 514 | "identify>=1.0.0", 515 | "nodeenv>=0.11.1", 516 | "pyyaml>=5.1", 517 | "virtualenv>=20.10.0", 518 | ] 519 | files = [ 520 | {file = "pre_commit-3.0.4-py2.py3-none-any.whl", hash = "sha256:9e3255edb0c9e7fe9b4f328cb3dc86069f8fdc38026f1bf521018a05eaf4d67b"}, 521 | {file = "pre_commit-3.0.4.tar.gz", hash = "sha256:bc4687478d55578c4ac37272fe96df66f73d9b5cf81be6f28627d4e712e752d5"}, 522 | ] 523 | 524 | [[package]] 525 | name = "pydantic" 526 | version = "2.5.2" 527 | requires_python = ">=3.7" 528 | summary = "Data validation using Python type hints" 529 | dependencies = [ 530 | "annotated-types>=0.4.0", 531 | "pydantic-core==2.14.5", 532 | "typing-extensions>=4.6.1", 533 | ] 534 | files = [ 535 | {file = "pydantic-2.5.2-py3-none-any.whl", hash = "sha256:80c50fb8e3dcecfddae1adbcc00ec5822918490c99ab31f6cf6140ca1c1429f0"}, 536 | {file = "pydantic-2.5.2.tar.gz", hash = "sha256:ff177ba64c6faf73d7afa2e8cad38fd456c0dbe01c9954e71038001cd15a6edd"}, 537 | ] 538 | 539 | [[package]] 540 | name = "pydantic-core" 541 | version = "2.14.5" 542 | requires_python = ">=3.7" 543 | summary = "" 544 | dependencies = [ 545 | "typing-extensions!=4.7.0,>=4.6.0", 546 | ] 547 | files = [ 548 | {file = "pydantic_core-2.14.5-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:7e88f5696153dc516ba6e79f82cc4747e87027205f0e02390c21f7cb3bd8abfd"}, 549 | {file = "pydantic_core-2.14.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4641e8ad4efb697f38a9b64ca0523b557c7931c5f84e0fd377a9a3b05121f0de"}, 550 | {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:774de879d212db5ce02dfbf5b0da9a0ea386aeba12b0b95674a4ce0593df3d07"}, 551 | {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ebb4e035e28f49b6f1a7032920bb9a0c064aedbbabe52c543343d39341a5b2a3"}, 552 | {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b53e9ad053cd064f7e473a5f29b37fc4cc9dc6d35f341e6afc0155ea257fc911"}, 553 | {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aa1768c151cf562a9992462239dfc356b3d1037cc5a3ac829bb7f3bda7cc1f9"}, 554 | {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eac5c82fc632c599f4639a5886f96867ffced74458c7db61bc9a66ccb8ee3113"}, 555 | {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae91f50ccc5810b2f1b6b858257c9ad2e08da70bf890dee02de1775a387c66"}, 556 | {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6b9ff467ffbab9110e80e8c8de3bcfce8e8b0fd5661ac44a09ae5901668ba997"}, 557 | {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:61ea96a78378e3bd5a0be99b0e5ed00057b71f66115f5404d0dae4819f495093"}, 558 | {file = "pydantic_core-2.14.5-cp310-none-win32.whl", hash = "sha256:bb4c2eda937a5e74c38a41b33d8c77220380a388d689bcdb9b187cf6224c9720"}, 559 | {file = "pydantic_core-2.14.5-cp310-none-win_amd64.whl", hash = "sha256:b7851992faf25eac90bfcb7bfd19e1f5ffa00afd57daec8a0042e63c74a4551b"}, 560 | {file = "pydantic_core-2.14.5-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:4e40f2bd0d57dac3feb3a3aed50f17d83436c9e6b09b16af271b6230a2915459"}, 561 | {file = "pydantic_core-2.14.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab1cdb0f14dc161ebc268c09db04d2c9e6f70027f3b42446fa11c153521c0e88"}, 562 | {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aae7ea3a1c5bb40c93cad361b3e869b180ac174656120c42b9fadebf685d121b"}, 563 | {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60b7607753ba62cf0739177913b858140f11b8af72f22860c28eabb2f0a61937"}, 564 | {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2248485b0322c75aee7565d95ad0e16f1c67403a470d02f94da7344184be770f"}, 565 | {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:823fcc638f67035137a5cd3f1584a4542d35a951c3cc68c6ead1df7dac825c26"}, 566 | {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96581cfefa9123accc465a5fd0cc833ac4d75d55cc30b633b402e00e7ced00a6"}, 567 | {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a33324437018bf6ba1bb0f921788788641439e0ed654b233285b9c69704c27b4"}, 568 | {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9bd18fee0923ca10f9a3ff67d4851c9d3e22b7bc63d1eddc12f439f436f2aada"}, 569 | {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:853a2295c00f1d4429db4c0fb9475958543ee80cfd310814b5c0ef502de24dda"}, 570 | {file = "pydantic_core-2.14.5-cp311-none-win32.whl", hash = "sha256:cb774298da62aea5c80a89bd58c40205ab4c2abf4834453b5de207d59d2e1651"}, 571 | {file = "pydantic_core-2.14.5-cp311-none-win_amd64.whl", hash = "sha256:e87fc540c6cac7f29ede02e0f989d4233f88ad439c5cdee56f693cc9c1c78077"}, 572 | {file = "pydantic_core-2.14.5-cp311-none-win_arm64.whl", hash = "sha256:57d52fa717ff445cb0a5ab5237db502e6be50809b43a596fb569630c665abddf"}, 573 | {file = "pydantic_core-2.14.5-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:e60f112ac88db9261ad3a52032ea46388378034f3279c643499edb982536a093"}, 574 | {file = "pydantic_core-2.14.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6e227c40c02fd873c2a73a98c1280c10315cbebe26734c196ef4514776120aeb"}, 575 | {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0cbc7fff06a90bbd875cc201f94ef0ee3929dfbd5c55a06674b60857b8b85ed"}, 576 | {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:103ef8d5b58596a731b690112819501ba1db7a36f4ee99f7892c40da02c3e189"}, 577 | {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c949f04ecad823f81b1ba94e7d189d9dfb81edbb94ed3f8acfce41e682e48cef"}, 578 | {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1452a1acdf914d194159439eb21e56b89aa903f2e1c65c60b9d874f9b950e5d"}, 579 | {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb4679d4c2b089e5ef89756bc73e1926745e995d76e11925e3e96a76d5fa51fc"}, 580 | {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf9d3fe53b1ee360e2421be95e62ca9b3296bf3f2fb2d3b83ca49ad3f925835e"}, 581 | {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:70f4b4851dbb500129681d04cc955be2a90b2248d69273a787dda120d5cf1f69"}, 582 | {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:59986de5710ad9613ff61dd9b02bdd2f615f1a7052304b79cc8fa2eb4e336d2d"}, 583 | {file = "pydantic_core-2.14.5-cp312-none-win32.whl", hash = "sha256:699156034181e2ce106c89ddb4b6504c30db8caa86e0c30de47b3e0654543260"}, 584 | {file = "pydantic_core-2.14.5-cp312-none-win_amd64.whl", hash = "sha256:5baab5455c7a538ac7e8bf1feec4278a66436197592a9bed538160a2e7d11e36"}, 585 | {file = "pydantic_core-2.14.5-cp312-none-win_arm64.whl", hash = "sha256:e47e9a08bcc04d20975b6434cc50bf82665fbc751bcce739d04a3120428f3e27"}, 586 | {file = "pydantic_core-2.14.5-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:ef98ca7d5995a82f43ec0ab39c4caf6a9b994cb0b53648ff61716370eadc43cf"}, 587 | {file = "pydantic_core-2.14.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6eae413494a1c3f89055da7a5515f32e05ebc1a234c27674a6956755fb2236f"}, 588 | {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcf4e6d85614f7a4956c2de5a56531f44efb973d2fe4a444d7251df5d5c4dcfd"}, 589 | {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6637560562134b0e17de333d18e69e312e0458ee4455bdad12c37100b7cad706"}, 590 | {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77fa384d8e118b3077cccfcaf91bf83c31fe4dc850b5e6ee3dc14dc3d61bdba1"}, 591 | {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16e29bad40bcf97aac682a58861249ca9dcc57c3f6be22f506501833ddb8939c"}, 592 | {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531f4b4252fac6ca476fbe0e6f60f16f5b65d3e6b583bc4d87645e4e5ddde331"}, 593 | {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:074f3d86f081ce61414d2dc44901f4f83617329c6f3ab49d2bc6c96948b2c26b"}, 594 | {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c2adbe22ab4babbca99c75c5d07aaf74f43c3195384ec07ccbd2f9e3bddaecec"}, 595 | {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0f6116a558fd06d1b7c2902d1c4cf64a5bd49d67c3540e61eccca93f41418124"}, 596 | {file = "pydantic_core-2.14.5-cp38-none-win32.whl", hash = "sha256:fe0a5a1025eb797752136ac8b4fa21aa891e3d74fd340f864ff982d649691867"}, 597 | {file = "pydantic_core-2.14.5-cp38-none-win_amd64.whl", hash = "sha256:079206491c435b60778cf2b0ee5fd645e61ffd6e70c47806c9ed51fc75af078d"}, 598 | {file = "pydantic_core-2.14.5-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:a6a16f4a527aae4f49c875da3cdc9508ac7eef26e7977952608610104244e1b7"}, 599 | {file = "pydantic_core-2.14.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:abf058be9517dc877227ec3223f0300034bd0e9f53aebd63cf4456c8cb1e0863"}, 600 | {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49b08aae5013640a3bfa25a8eebbd95638ec3f4b2eaf6ed82cf0c7047133f03b"}, 601 | {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2d97e906b4ff36eb464d52a3bc7d720bd6261f64bc4bcdbcd2c557c02081ed2"}, 602 | {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3128e0bbc8c091ec4375a1828d6118bc20404883169ac95ffa8d983b293611e6"}, 603 | {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88e74ab0cdd84ad0614e2750f903bb0d610cc8af2cc17f72c28163acfcf372a4"}, 604 | {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c339dabd8ee15f8259ee0f202679b6324926e5bc9e9a40bf981ce77c038553db"}, 605 | {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3387277f1bf659caf1724e1afe8ee7dbc9952a82d90f858ebb931880216ea955"}, 606 | {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ba6b6b3846cfc10fdb4c971980a954e49d447cd215ed5a77ec8190bc93dd7bc5"}, 607 | {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ca61d858e4107ce5e1330a74724fe757fc7135190eb5ce5c9d0191729f033209"}, 608 | {file = "pydantic_core-2.14.5-cp39-none-win32.whl", hash = "sha256:ec1e72d6412f7126eb7b2e3bfca42b15e6e389e1bc88ea0069d0cc1742f477c6"}, 609 | {file = "pydantic_core-2.14.5-cp39-none-win_amd64.whl", hash = "sha256:c0b97ec434041827935044bbbe52b03d6018c2897349670ff8fe11ed24d1d4ab"}, 610 | {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:79e0a2cdbdc7af3f4aee3210b1172ab53d7ddb6a2d8c24119b5706e622b346d0"}, 611 | {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:678265f7b14e138d9a541ddabbe033012a2953315739f8cfa6d754cc8063e8ca"}, 612 | {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b15e855ae44f0c6341ceb74df61b606e11f1087e87dcb7482377374aac6abe"}, 613 | {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09b0e985fbaf13e6b06a56d21694d12ebca6ce5414b9211edf6f17738d82b0f8"}, 614 | {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3ad873900297bb36e4b6b3f7029d88ff9829ecdc15d5cf20161775ce12306f8a"}, 615 | {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2d0ae0d8670164e10accbeb31d5ad45adb71292032d0fdb9079912907f0085f4"}, 616 | {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d37f8ec982ead9ba0a22a996129594938138a1503237b87318392a48882d50b7"}, 617 | {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:35613015f0ba7e14c29ac6c2483a657ec740e5ac5758d993fdd5870b07a61d8b"}, 618 | {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab4ea451082e684198636565224bbb179575efc1658c48281b2c866bfd4ddf04"}, 619 | {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ce601907e99ea5b4adb807ded3570ea62186b17f88e271569144e8cca4409c7"}, 620 | {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb2ed8b3fe4bf4506d6dab3b93b83bbc22237e230cba03866d561c3577517d18"}, 621 | {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:70f947628e074bb2526ba1b151cee10e4c3b9670af4dbb4d73bc8a89445916b5"}, 622 | {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4bc536201426451f06f044dfbf341c09f540b4ebdb9fd8d2c6164d733de5e634"}, 623 | {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4791cf0f8c3104ac668797d8c514afb3431bc3305f5638add0ba1a5a37e0d88"}, 624 | {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:038c9f763e650712b899f983076ce783175397c848da04985658e7628cbe873b"}, 625 | {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:27548e16c79702f1e03f5628589c6057c9ae17c95b4c449de3c66b589ead0520"}, 626 | {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97bee68898f3f4344eb02fec316db93d9700fb1e6a5b760ffa20d71d9a46ce3"}, 627 | {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9b759b77f5337b4ea024f03abc6464c9f35d9718de01cfe6bae9f2e139c397e"}, 628 | {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:439c9afe34638ace43a49bf72d201e0ffc1a800295bed8420c2a9ca8d5e3dbb3"}, 629 | {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ba39688799094c75ea8a16a6b544eb57b5b0f3328697084f3f2790892510d144"}, 630 | {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ccd4d5702bb90b84df13bd491be8d900b92016c5a455b7e14630ad7449eb03f8"}, 631 | {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:81982d78a45d1e5396819bbb4ece1fadfe5f079335dd28c4ab3427cd95389944"}, 632 | {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:7f8210297b04e53bc3da35db08b7302a6a1f4889c79173af69b72ec9754796b8"}, 633 | {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:8c8a8812fe6f43a3a5b054af6ac2d7b8605c7bcab2804a8a7d68b53f3cd86e00"}, 634 | {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:206ed23aecd67c71daf5c02c3cd19c0501b01ef3cbf7782db9e4e051426b3d0d"}, 635 | {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2027d05c8aebe61d898d4cffd774840a9cb82ed356ba47a90d99ad768f39789"}, 636 | {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40180930807ce806aa71eda5a5a5447abb6b6a3c0b4b3b1b1962651906484d68"}, 637 | {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:615a0a4bff11c45eb3c1996ceed5bdaa2f7b432425253a7c2eed33bb86d80abc"}, 638 | {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5e412d717366e0677ef767eac93566582518fe8be923361a5c204c1a62eaafe"}, 639 | {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:513b07e99c0a267b1d954243845d8a833758a6726a3b5d8948306e3fe14675e3"}, 640 | {file = "pydantic_core-2.14.5.tar.gz", hash = "sha256:6d30226dfc816dd0fdf120cae611dd2215117e4f9b124af8c60ab9093b6e8e71"}, 641 | ] 642 | 643 | [[package]] 644 | name = "pydantic-settings" 645 | version = "2.1.0" 646 | requires_python = ">=3.8" 647 | summary = "Settings management using Pydantic" 648 | dependencies = [ 649 | "pydantic>=2.3.0", 650 | "python-dotenv>=0.21.0", 651 | ] 652 | files = [ 653 | {file = "pydantic_settings-2.1.0-py3-none-any.whl", hash = "sha256:7621c0cb5d90d1140d2f0ef557bdf03573aac7035948109adf2574770b77605a"}, 654 | {file = "pydantic_settings-2.1.0.tar.gz", hash = "sha256:26b1492e0a24755626ac5e6d715e9077ab7ad4fb5f19a8b7ed7011d52f36141c"}, 655 | ] 656 | 657 | [[package]] 658 | name = "pydantic" 659 | version = "2.5.2" 660 | extras = ["dotenv"] 661 | requires_python = ">=3.7" 662 | summary = "Data validation using Python type hints" 663 | dependencies = [ 664 | "pydantic==2.5.2", 665 | ] 666 | files = [ 667 | {file = "pydantic-2.5.2-py3-none-any.whl", hash = "sha256:80c50fb8e3dcecfddae1adbcc00ec5822918490c99ab31f6cf6140ca1c1429f0"}, 668 | {file = "pydantic-2.5.2.tar.gz", hash = "sha256:ff177ba64c6faf73d7afa2e8cad38fd456c0dbe01c9954e71038001cd15a6edd"}, 669 | ] 670 | 671 | [[package]] 672 | name = "python-dotenv" 673 | version = "0.21.1" 674 | requires_python = ">=3.7" 675 | summary = "Read key-value pairs from a .env file and set them as environment variables" 676 | files = [ 677 | {file = "python-dotenv-0.21.1.tar.gz", hash = "sha256:1c93de8f636cde3ce377292818d0e440b6e45a82f215c3744979151fa8151c49"}, 678 | {file = "python_dotenv-0.21.1-py3-none-any.whl", hash = "sha256:41e12e0318bebc859fcc4d97d4db8d20ad21721a6aa5047dd59f090391cb549a"}, 679 | ] 680 | 681 | [[package]] 682 | name = "python-multipart" 683 | version = "0.0.5" 684 | summary = "A streaming multipart parser for Python" 685 | dependencies = [ 686 | "six>=1.4.0", 687 | ] 688 | files = [ 689 | {file = "python-multipart-0.0.5.tar.gz", hash = "sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"}, 690 | ] 691 | 692 | [[package]] 693 | name = "pytz" 694 | version = "2022.7.1" 695 | summary = "World timezone definitions, modern and historical" 696 | files = [ 697 | {file = "pytz-2022.7.1-py2.py3-none-any.whl", hash = "sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"}, 698 | {file = "pytz-2022.7.1.tar.gz", hash = "sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0"}, 699 | ] 700 | 701 | [[package]] 702 | name = "pytz-deprecation-shim" 703 | version = "0.1.0.post0" 704 | requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" 705 | summary = "Shims to make deprecation of pytz easier" 706 | dependencies = [ 707 | "backports-zoneinfo; python_version >= \"3.6\" and python_version < \"3.9\"", 708 | "tzdata; python_version >= \"3.6\"", 709 | ] 710 | files = [ 711 | {file = "pytz_deprecation_shim-0.1.0.post0-py2.py3-none-any.whl", hash = "sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6"}, 712 | {file = "pytz_deprecation_shim-0.1.0.post0.tar.gz", hash = "sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d"}, 713 | ] 714 | 715 | [[package]] 716 | name = "pyyaml" 717 | version = "6.0" 718 | requires_python = ">=3.6" 719 | summary = "YAML parser and emitter for Python" 720 | files = [ 721 | {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, 722 | {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, 723 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, 724 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, 725 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, 726 | {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, 727 | {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, 728 | {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, 729 | {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, 730 | {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, 731 | {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, 732 | {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, 733 | {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, 734 | {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, 735 | {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, 736 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, 737 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, 738 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, 739 | {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, 740 | {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, 741 | {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, 742 | {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, 743 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, 744 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, 745 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, 746 | {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, 747 | {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, 748 | {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, 749 | ] 750 | 751 | [[package]] 752 | name = "setuptools" 753 | version = "67.3.2" 754 | requires_python = ">=3.7" 755 | summary = "Easily download, build, install, upgrade, and uninstall Python packages" 756 | files = [ 757 | {file = "setuptools-67.3.2-py3-none-any.whl", hash = "sha256:bb6d8e508de562768f2027902929f8523932fcd1fb784e6d573d2cafac995a48"}, 758 | {file = "setuptools-67.3.2.tar.gz", hash = "sha256:95f00380ef2ffa41d9bba85d95b27689d923c93dfbafed4aecd7cf988a25e012"}, 759 | ] 760 | 761 | [[package]] 762 | name = "simpleeval" 763 | version = "0.9.13" 764 | summary = "A simple, safe single expression evaluator library." 765 | files = [ 766 | {file = "simpleeval-0.9.13-py2.py3-none-any.whl", hash = "sha256:22a2701a5006e4188d125d34accf2405c2c37c93f6b346f2484b6422415ae54a"}, 767 | {file = "simpleeval-0.9.13.tar.gz", hash = "sha256:4a30f9cc01825fe4c719c785e3762623e350c4840d5e6855c2a8496baaa65fac"}, 768 | ] 769 | 770 | [[package]] 771 | name = "six" 772 | version = "1.16.0" 773 | requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 774 | summary = "Python 2 and 3 compatibility utilities" 775 | files = [ 776 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 777 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 778 | ] 779 | 780 | [[package]] 781 | name = "sniffio" 782 | version = "1.3.0" 783 | requires_python = ">=3.7" 784 | summary = "Sniff out which async library your code is running under" 785 | files = [ 786 | {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, 787 | {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, 788 | ] 789 | 790 | [[package]] 791 | name = "sqlalchemy" 792 | version = "2.0.23" 793 | requires_python = ">=3.7" 794 | summary = "Database Abstraction Library" 795 | dependencies = [ 796 | "greenlet!=0.4.17; platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\"", 797 | "typing-extensions>=4.2.0", 798 | ] 799 | files = [ 800 | {file = "SQLAlchemy-2.0.23-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:638c2c0b6b4661a4fd264f6fb804eccd392745c5887f9317feb64bb7cb03b3ea"}, 801 | {file = "SQLAlchemy-2.0.23-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3b5036aa326dc2df50cba3c958e29b291a80f604b1afa4c8ce73e78e1c9f01d"}, 802 | {file = "SQLAlchemy-2.0.23-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:787af80107fb691934a01889ca8f82a44adedbf5ef3d6ad7d0f0b9ac557e0c34"}, 803 | {file = "SQLAlchemy-2.0.23-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c14eba45983d2f48f7546bb32b47937ee2cafae353646295f0e99f35b14286ab"}, 804 | {file = "SQLAlchemy-2.0.23-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0666031df46b9badba9bed00092a1ffa3aa063a5e68fa244acd9f08070e936d3"}, 805 | {file = "SQLAlchemy-2.0.23-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:89a01238fcb9a8af118eaad3ffcc5dedaacbd429dc6fdc43fe430d3a941ff965"}, 806 | {file = "SQLAlchemy-2.0.23-cp310-cp310-win32.whl", hash = "sha256:cabafc7837b6cec61c0e1e5c6d14ef250b675fa9c3060ed8a7e38653bd732ff8"}, 807 | {file = "SQLAlchemy-2.0.23-cp310-cp310-win_amd64.whl", hash = "sha256:87a3d6b53c39cd173990de2f5f4b83431d534a74f0e2f88bd16eabb5667e65c6"}, 808 | {file = "SQLAlchemy-2.0.23-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d5578e6863eeb998980c212a39106ea139bdc0b3f73291b96e27c929c90cd8e1"}, 809 | {file = "SQLAlchemy-2.0.23-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62d9e964870ea5ade4bc870ac4004c456efe75fb50404c03c5fd61f8bc669a72"}, 810 | {file = "SQLAlchemy-2.0.23-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c80c38bd2ea35b97cbf7c21aeb129dcbebbf344ee01a7141016ab7b851464f8e"}, 811 | {file = "SQLAlchemy-2.0.23-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75eefe09e98043cff2fb8af9796e20747ae870c903dc61d41b0c2e55128f958d"}, 812 | {file = "SQLAlchemy-2.0.23-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd45a5b6c68357578263d74daab6ff9439517f87da63442d244f9f23df56138d"}, 813 | {file = "SQLAlchemy-2.0.23-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a86cb7063e2c9fb8e774f77fbf8475516d270a3e989da55fa05d08089d77f8c4"}, 814 | {file = "SQLAlchemy-2.0.23-cp311-cp311-win32.whl", hash = "sha256:b41f5d65b54cdf4934ecede2f41b9c60c9f785620416e8e6c48349ab18643855"}, 815 | {file = "SQLAlchemy-2.0.23-cp311-cp311-win_amd64.whl", hash = "sha256:9ca922f305d67605668e93991aaf2c12239c78207bca3b891cd51a4515c72e22"}, 816 | {file = "SQLAlchemy-2.0.23-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d0f7fb0c7527c41fa6fcae2be537ac137f636a41b4c5a4c58914541e2f436b45"}, 817 | {file = "SQLAlchemy-2.0.23-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7c424983ab447dab126c39d3ce3be5bee95700783204a72549c3dceffe0fc8f4"}, 818 | {file = "SQLAlchemy-2.0.23-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f508ba8f89e0a5ecdfd3761f82dda2a3d7b678a626967608f4273e0dba8f07ac"}, 819 | {file = "SQLAlchemy-2.0.23-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6463aa765cf02b9247e38b35853923edbf2f6fd1963df88706bc1d02410a5577"}, 820 | {file = "SQLAlchemy-2.0.23-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e599a51acf3cc4d31d1a0cf248d8f8d863b6386d2b6782c5074427ebb7803bda"}, 821 | {file = "SQLAlchemy-2.0.23-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fd54601ef9cc455a0c61e5245f690c8a3ad67ddb03d3b91c361d076def0b4c60"}, 822 | {file = "SQLAlchemy-2.0.23-cp312-cp312-win32.whl", hash = "sha256:42d0b0290a8fb0165ea2c2781ae66e95cca6e27a2fbe1016ff8db3112ac1e846"}, 823 | {file = "SQLAlchemy-2.0.23-cp312-cp312-win_amd64.whl", hash = "sha256:227135ef1e48165f37590b8bfc44ed7ff4c074bf04dc8d6f8e7f1c14a94aa6ca"}, 824 | {file = "SQLAlchemy-2.0.23-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:64ac935a90bc479fee77f9463f298943b0e60005fe5de2aa654d9cdef46c54df"}, 825 | {file = "SQLAlchemy-2.0.23-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c4722f3bc3c1c2fcc3702dbe0016ba31148dd6efcd2a2fd33c1b4897c6a19693"}, 826 | {file = "SQLAlchemy-2.0.23-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4af79c06825e2836de21439cb2a6ce22b2ca129bad74f359bddd173f39582bf5"}, 827 | {file = "SQLAlchemy-2.0.23-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:683ef58ca8eea4747737a1c35c11372ffeb84578d3aab8f3e10b1d13d66f2bc4"}, 828 | {file = "SQLAlchemy-2.0.23-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d4041ad05b35f1f4da481f6b811b4af2f29e83af253bf37c3c4582b2c68934ab"}, 829 | {file = "SQLAlchemy-2.0.23-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aeb397de65a0a62f14c257f36a726945a7f7bb60253462e8602d9b97b5cbe204"}, 830 | {file = "SQLAlchemy-2.0.23-cp38-cp38-win32.whl", hash = "sha256:42ede90148b73fe4ab4a089f3126b2cfae8cfefc955c8174d697bb46210c8306"}, 831 | {file = "SQLAlchemy-2.0.23-cp38-cp38-win_amd64.whl", hash = "sha256:964971b52daab357d2c0875825e36584d58f536e920f2968df8d581054eada4b"}, 832 | {file = "SQLAlchemy-2.0.23-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:616fe7bcff0a05098f64b4478b78ec2dfa03225c23734d83d6c169eb41a93e55"}, 833 | {file = "SQLAlchemy-2.0.23-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0e680527245895aba86afbd5bef6c316831c02aa988d1aad83c47ffe92655e74"}, 834 | {file = "SQLAlchemy-2.0.23-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9585b646ffb048c0250acc7dad92536591ffe35dba624bb8fd9b471e25212a35"}, 835 | {file = "SQLAlchemy-2.0.23-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4895a63e2c271ffc7a81ea424b94060f7b3b03b4ea0cd58ab5bb676ed02f4221"}, 836 | {file = "SQLAlchemy-2.0.23-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:cc1d21576f958c42d9aec68eba5c1a7d715e5fc07825a629015fe8e3b0657fb0"}, 837 | {file = "SQLAlchemy-2.0.23-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:967c0b71156f793e6662dd839da54f884631755275ed71f1539c95bbada9aaab"}, 838 | {file = "SQLAlchemy-2.0.23-cp39-cp39-win32.whl", hash = "sha256:0a8c6aa506893e25a04233bc721c6b6cf844bafd7250535abb56cb6cc1368884"}, 839 | {file = "SQLAlchemy-2.0.23-cp39-cp39-win_amd64.whl", hash = "sha256:f3420d00d2cb42432c1d0e44540ae83185ccbbc67a6054dcc8ab5387add6620b"}, 840 | {file = "SQLAlchemy-2.0.23-py3-none-any.whl", hash = "sha256:31952bbc527d633b9479f5f81e8b9dfada00b91d6baba021a869095f1a97006d"}, 841 | {file = "SQLAlchemy-2.0.23.tar.gz", hash = "sha256:c1bda93cbbe4aa2aa0aa8655c5aeda505cd219ff3e8da91d1d329e143e4aff69"}, 842 | ] 843 | 844 | [[package]] 845 | name = "sqlalchemy-database" 846 | version = "0.1.1" 847 | requires_python = ">=3.7" 848 | summary = "SQLAlchemy-Database provides shortcut functions to common database operations for SQLAlchemy ORM." 849 | dependencies = [ 850 | "sqlalchemy", 851 | ] 852 | files = [ 853 | {file = "sqlalchemy_database-0.1.1-py3-none-any.whl", hash = "sha256:e6e801af5affa9a0f5e6fdd4fc9626b9f78a040105107fd12588dee21fbe5574"}, 854 | {file = "sqlalchemy_database-0.1.1.tar.gz", hash = "sha256:c4a6bdc9812a42e9a246dfd7c336e2685969f1163cf2777c88127615b65c6539"}, 855 | ] 856 | 857 | [[package]] 858 | name = "sqlmodel" 859 | version = "0.0.14" 860 | requires_python = ">=3.7,<4.0" 861 | summary = "SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness." 862 | dependencies = [ 863 | "SQLAlchemy<2.1.0,>=2.0.0", 864 | "pydantic<3.0.0,>=1.10.13", 865 | ] 866 | files = [ 867 | {file = "sqlmodel-0.0.14-py3-none-any.whl", hash = "sha256:accea3ff5d878e41ac439b11e78613ed61ce300cfcb860e87a2d73d4884cbee4"}, 868 | {file = "sqlmodel-0.0.14.tar.gz", hash = "sha256:0bff8fc94af86b44925aa813f56cf6aabdd7f156b73259f2f60692c6a64ac90e"}, 869 | ] 870 | 871 | [[package]] 872 | name = "sqlmodelx" 873 | version = "0.0.9" 874 | requires_python = ">=3.8" 875 | summary = "SQLModelX is an extension of the SQLModel library." 876 | dependencies = [ 877 | "sqlmodel>=0.0.14", 878 | ] 879 | files = [ 880 | {file = "sqlmodelx-0.0.9-py3-none-any.whl", hash = "sha256:f6cb15a3d4c7eba2603ab6c40d780945bd6acdacbb2d6c213200411e8421d465"}, 881 | {file = "sqlmodelx-0.0.9.tar.gz", hash = "sha256:82ebb86cb6b489a60fc5f04fea5737b771cf023648e751a8a1db7b4c737289a6"}, 882 | ] 883 | 884 | [[package]] 885 | name = "starlette" 886 | version = "0.27.0" 887 | requires_python = ">=3.7" 888 | summary = "The little ASGI library that shines." 889 | dependencies = [ 890 | "anyio<5,>=3.4.0", 891 | "typing-extensions>=3.10.0; python_version < \"3.10\"", 892 | ] 893 | files = [ 894 | {file = "starlette-0.27.0-py3-none-any.whl", hash = "sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91"}, 895 | {file = "starlette-0.27.0.tar.gz", hash = "sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75"}, 896 | ] 897 | 898 | [[package]] 899 | name = "typing-extensions" 900 | version = "4.9.0" 901 | requires_python = ">=3.8" 902 | summary = "Backported and Experimental Type Hints for Python 3.8+" 903 | files = [ 904 | {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, 905 | {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, 906 | ] 907 | 908 | [[package]] 909 | name = "tzdata" 910 | version = "2022.7" 911 | requires_python = ">=2" 912 | summary = "Provider of IANA time zone data" 913 | files = [ 914 | {file = "tzdata-2022.7-py2.py3-none-any.whl", hash = "sha256:2b88858b0e3120792a3c0635c23daf36a7d7eeeca657c323da299d2094402a0d"}, 915 | {file = "tzdata-2022.7.tar.gz", hash = "sha256:fe5f866eddd8b96e9fcba978f8e503c909b19ea7efda11e52e39494bad3a7bfa"}, 916 | ] 917 | 918 | [[package]] 919 | name = "tzlocal" 920 | version = "4.2" 921 | requires_python = ">=3.6" 922 | summary = "tzinfo object for the local timezone" 923 | dependencies = [ 924 | "backports-zoneinfo; python_version < \"3.9\"", 925 | "pytz-deprecation-shim", 926 | "tzdata; platform_system == \"Windows\"", 927 | ] 928 | files = [ 929 | {file = "tzlocal-4.2-py3-none-any.whl", hash = "sha256:89885494684c929d9191c57aa27502afc87a579be5cdd3225c77c463ea043745"}, 930 | {file = "tzlocal-4.2.tar.gz", hash = "sha256:ee5842fa3a795f023514ac2d801c4a81d1743bbe642e3940143326b3a00addd7"}, 931 | ] 932 | 933 | [[package]] 934 | name = "uvicorn" 935 | version = "0.20.0" 936 | requires_python = ">=3.7" 937 | summary = "The lightning-fast ASGI server." 938 | dependencies = [ 939 | "click>=7.0", 940 | "h11>=0.8", 941 | ] 942 | files = [ 943 | {file = "uvicorn-0.20.0-py3-none-any.whl", hash = "sha256:c3ed1598a5668208723f2bb49336f4509424ad198d6ab2615b7783db58d919fd"}, 944 | {file = "uvicorn-0.20.0.tar.gz", hash = "sha256:a4e12017b940247f836bc90b72e725d7dfd0c8ed1c51eb365f5ba30d9f5127d8"}, 945 | ] 946 | 947 | [[package]] 948 | name = "virtualenv" 949 | version = "20.19.0" 950 | requires_python = ">=3.7" 951 | summary = "Virtual Python Environment builder" 952 | dependencies = [ 953 | "distlib<1,>=0.3.6", 954 | "filelock<4,>=3.4.1", 955 | "platformdirs<4,>=2.4", 956 | ] 957 | files = [ 958 | {file = "virtualenv-20.19.0-py3-none-any.whl", hash = "sha256:54eb59e7352b573aa04d53f80fc9736ed0ad5143af445a1e539aada6eb947dd1"}, 959 | {file = "virtualenv-20.19.0.tar.gz", hash = "sha256:37a640ba82ed40b226599c522d411e4be5edb339a0c0de030c0dc7b646d61590"}, 960 | ] 961 | 962 | [[package]] 963 | name = "zipp" 964 | version = "3.14.0" 965 | requires_python = ">=3.7" 966 | summary = "Backport of pathlib-compatible object wrapper for zip files" 967 | files = [ 968 | {file = "zipp-3.14.0-py3-none-any.whl", hash = "sha256:188834565033387710d046e3fe96acfc9b5e86cbca7f39ff69cf21a4128198b7"}, 969 | {file = "zipp-3.14.0.tar.gz", hash = "sha256:9e5421e176ef5ab4c0ad896624e87a7b2f07aca746c9b2aa305952800cb8eecb"}, 970 | ] 971 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "" 3 | version = "" 4 | description = "" 5 | authors = [ 6 | { name = "atomi", email = "1456417373@qq.com" }, 7 | ] 8 | dependencies = [ 9 | "uvicorn>=0.18.3", 10 | "pydantic[dotenv]>=2.0.0", 11 | "pydantic-settings>=2.1.0", 12 | "aiosqlite>=0.15.0", 13 | "fastapi-user-auth<0.8.0,>=0.7.0", 14 | "fastapi-amis-admin-nav<0.2.0,>=0.1.3", 15 | "fastapi-scheduler<0.1.0,>=0.0.15", 16 | "sqlmodelx>=0.0.9", 17 | "alembic>=1.7.6", 18 | ] 19 | requires-python = ">=3.8" 20 | license = { text = "MIT" } 21 | [project.optional-dependencies] 22 | 23 | [build-system] 24 | requires = ["pdm-pep517>=1.0.0"] 25 | build-backend = "pdm.pep517.api" 26 | 27 | [tool] 28 | [tool.pdm] 29 | [tool.pdm.dev-dependencies] 30 | dev = [ 31 | "pre-commit>=2.20.0", 32 | ] 33 | [tool.pdm.scripts] 34 | #_.env_file = "./backend/.env" 35 | run = "faa run" 36 | stop = "faa stop" 37 | lint = "pre-commit run --all-files" 38 | 39 | 40 | [tool.isort] 41 | profile = "black" 42 | atomic = true 43 | filter_files = true 44 | 45 | [tool.black] 46 | line-length = 130 47 | include = '\.pyi?$' 48 | 49 | 50 | [tool.ruff] 51 | select = [ 52 | "E", # pycodestyle errors 53 | "W", # pycodestyle warnings 54 | "F", # pyflakes 55 | "I", # isort 56 | "C", # flake8-comprehensions 57 | "B", # flake8-bugbear 58 | ] 59 | ignore = [ 60 | "B008", # do not perform function calls in argument defaults 61 | "C901", # too complex 62 | "E711", # sqlalchmey comparison 63 | ] 64 | # Same as Black. 65 | line-length = 130 66 | 67 | [tool.ruff.per-file-ignores] 68 | "__init__.py" = ["F401"] 69 | 70 | 71 | [tool.pytest.ini_options] 72 | minversion = "6.0" 73 | python_files = [ 74 | "test_*", 75 | "*_test" 76 | ] 77 | # pytest-asyncio 78 | asyncio_mode = "auto" --------------------------------------------------------------------------------