diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index fb0b361..fe1cc3e 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,9 +2,9 @@ "name": "Default Linux Universal", "image": "mcr.microsoft.com/devcontainers/universal:2-linux", "features": { - "ghcr.io/devcontainers-contrib/features/poetry:1": {} + "ghcr.io/devcontainers-contrib/features/pdm:2": {} }, - "postCreateCommand": "poetry config virtualenvs.in-project true && poetry install && poetry run pre-commit install", + "postCreateCommand": "pdm install && pdm run pre-commit install", "customizations": { "vscode": { "settings": { diff --git a/.github/actions/setup-python/action.yml b/.github/actions/setup-python/action.yml index 515d089..2f8e106 100644 --- a/.github/actions/setup-python/action.yml +++ b/.github/actions/setup-python/action.yml @@ -10,15 +10,11 @@ inputs: runs: using: "composite" steps: - - name: Install poetry - run: pipx install poetry - shell: bash - - - uses: actions/setup-python@v4 + - uses: pdm-project/setup-pdm@v3 with: python-version: ${{ inputs.python-version }} architecture: "x64" - cache: "poetry" + cache: true - - run: poetry install - shell: bash + - name: Install dependencies + run: pdm install diff --git a/.gitignore b/.gitignore index 2a53280..beea4cb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,310 +1,3 @@ -# ----- Project ----- - -# Created by https://www.toptal.com/developers/gitignore/api/python,node,visualstudiocode,jetbrains,macos,windows,linux -# Edit at https://www.toptal.com/developers/gitignore?templates=python,node,visualstudiocode,jetbrains,macos,windows,linux - -### JetBrains ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# AWS User-specific -.idea/**/aws.xml - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/artifacts -# .idea/compiler.xml -# .idea/jarRepositories.xml -# .idea/modules.xml -# .idea/*.iml -# .idea/modules -# *.iml -# *.ipr - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format -*.iws - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# SonarLint plugin -.idea/sonarlint/ - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based Rest Client -.idea/httpRequests - -# Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser - -### JetBrains Patch ### -# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 - -# *.iml -# modules.xml -# .idea/misc.xml -# *.ipr - -# Sonarlint plugin -# https://plugins.jetbrains.com/plugin/7973-sonarlint -.idea/**/sonarlint/ - -# SonarQube Plugin -# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin -.idea/**/sonarIssues.xml - -# Markdown Navigator plugin -# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced -.idea/**/markdown-navigator.xml -.idea/**/markdown-navigator-enh.xml -.idea/**/markdown-navigator/ - -# Cache file creation bug -# See https://youtrack.jetbrains.com/issue/JBR-2257 -.idea/$CACHE_FILE$ - -# CodeStream plugin -# https://plugins.jetbrains.com/plugin/12206-codestream -.idea/codestream.xml - -# Azure Toolkit for IntelliJ plugin -# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij -.idea/**/azureSettings.xml - -### Linux ### -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -### macOS ### -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### macOS Patch ### -# iCloud generated files -*.icloud - -### Node ### -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variable files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next -out - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# vuepress v2.x temp and cache directory -.temp - -# Docusaurus cache and generated files -.docusaurus - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# yarn v2 -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* - -### Node Patch ### -# Serverless Webpack directories -.webpack/ - -# Optional stylelint cache - -# SvelteKit build / generate output -.svelte-kit - -### Python ### # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -349,6 +42,7 @@ htmlcov/ .nox/ .coverage .coverage.* +.cache nosetests.xml coverage.xml *.cover @@ -362,6 +56,7 @@ cover/ *.pot # Django stuff: +*.log local_settings.py db.sqlite3 db.sqlite3-journal @@ -390,7 +85,7 @@ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: -# .python-version +.python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. @@ -412,7 +107,7 @@ ipython_config.py # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it # in version control. # https://pdm.fming.dev/#use-with-ide -.pdm.toml +.pdm-python # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ @@ -425,6 +120,7 @@ celerybeat.pid *.sage.py # Environments +.env .venv env/ venv/ @@ -462,55 +158,3 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ - -### Python Patch ### -# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration -poetry.toml - - -### VisualStudioCode ### -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -!.vscode/*.code-snippets - -# Local History for Visual Studio Code -.history/ - -# Built Visual Studio Code Extensions -*.vsix - -### VisualStudioCode Patch ### -# Ignore all local history of files -.history -.ionide - -### Windows ### -# Windows thumbnail cache files -Thumbs.db -Thumbs.db:encryptable -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# End of https://www.toptal.com/developers/gitignore/api/python,node,visualstudiocode,jetbrains,macos,windows,linux diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..41c2fcb --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +The MIT License (MIT) +Copyright (c) 2023 NoneBot Team + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6d8c54a --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# nonebot-plugin-orm diff --git a/nonebot_plugin_orm/__init__.py b/nonebot_plugin_orm/__init__.py new file mode 100644 index 0000000..59671d8 --- /dev/null +++ b/nonebot_plugin_orm/__init__.py @@ -0,0 +1,190 @@ +from __future__ import annotations + +from typing import Any +from functools import wraps, partial +from contextlib import AsyncExitStack + +from nonebot import logger, get_driver +from nonebot.plugin import PluginMetadata +from sqlalchemy.util import greenlet_spawn +from nonebot.matcher import current_matcher +from sqlalchemy import URL, Table, MetaData, make_url +from sqlalchemy.ext.asyncio import ( + AsyncEngine, + AsyncSession, + async_sessionmaker, + create_async_engine, + async_scoped_session, +) + +from . import migrate +from .model import Model +from .utils import StreamToLogger +from .config import Config, plugin_config + +__all__ = ( + # __init__ + "init_orm", + "get_scoped_session", + # sql + "one", + "all_", + "first", + "select", + "scalars", + "scalar_all", + "scalar_one", + "one_or_none", + "scalar_first", + "one_or_create", + "scalar_one_or_none", + # model + "Model", + # config + "Config", + "config", + # migrate + "AlembicConfig", + "list_templates", + "init", + "revision", + "check", + "merge", + "upgrade", + "downgrade", + "show", + "history", + "heads", + "branches", + "current", + "stamp", + "edit", + "ensure_version", +) +__plugin_meta__ = PluginMetadata( + name="nonebot-plugin-orm", + description="SQLAlchemy ORM support for nonebot", + usage="https://github.com/nonebot/plugin-orm", + type="library", + homepage="https://github.com/nonebot/plugin-orm", + config=Config, +) + + +_binds: dict[type[Model], AsyncEngine] = None # type:ignore +_engines: dict[str, AsyncEngine] = None # type: ignore +_metadatas: dict[str, MetaData] = None # type: ignore +_session_factory: async_sessionmaker[AsyncSession] = None # type: ignore + + +@get_driver().on_startup +async def init_orm(): + global _session_factory + + _init_engines() + _init_table() + _session_factory = async_sessionmaker( + _engines[""], binds=_binds, **plugin_config.sqlalchemy_session_options + ) + + with migrate.AlembicConfig(stdout=StreamToLogger()) as alembic_config: + if plugin_config.alembic_startup_check: + await greenlet_spawn(migrate.check, alembic_config) + else: + logger.warning("跳过启动检查,直接创建所有表并标记数据库为最新修订版本") + + async with AsyncExitStack() as stack: + for name, engine in _engines.items(): + connection = await stack.enter_async_context(engine.begin()) + await connection.run_sync(_metadatas[name].create_all) + + await greenlet_spawn(migrate.stamp, alembic_config) + + +@wraps(lambda: None) # NOTE: for dependency injection +def get_session(**local_kw: Any) -> AsyncSession: + if _session_factory is None: + raise RuntimeError("nonebot-plugin-orm 未初始化") + return _session_factory(**local_kw) + + +def get_scoped_session() -> async_scoped_session[AsyncSession]: + if _session_factory is None: + raise RuntimeError("nonebot-plugin-orm 未初始化") + return async_scoped_session( + _session_factory, scopefunc=partial(current_matcher.get, None) + ) + + +def _create_engine(engine: str | URL | dict[str, Any] | AsyncEngine) -> AsyncEngine: + if isinstance(engine, AsyncEngine): + return engine + + options = plugin_config.sqlalchemy_engine_options.copy() + + if plugin_config.sqlalchemy_echo: + options["echo"] = options["echo_pool"] = True + + if isinstance(engine, dict): + url: str | URL = engine.pop("url") + options.update(engine) + else: + url = engine + + return create_async_engine(make_url(url), **options) + + +def _init_engines(): + global _engines, _metadatas + + _engines = {} + _metadatas = {} + for name, engine in plugin_config.sqlalchemy_binds.items(): + _engines[name] = _create_engine(engine) + _metadatas[name] = MetaData() + + if plugin_config.sqlalchemy_database_url: + _engines[""] = _create_engine(plugin_config.sqlalchemy_database_url) + + if "" in _engines: + return + + try: + import aiosqlite + from nonebot_plugin_localstore import get_data_file + + del aiosqlite + except ImportError: + raise ValueError( + '必须指定一个默认数据库引擎 (SQLALCHEMY_DATABASE_URL 或 SQLALCHEMY_BINDS[""])' + ) from None + + _engines[""] = create_async_engine( + f"sqlite+aiosqlite:///{get_data_file(__plugin_meta__.name, 'db.sqlite3')}" + ) + _metadatas[""] = MetaData() + + +def _init_table(): + global _binds, _metadatas + + _binds = {} + + if len(_engines) == 1: # NOTE: common case: only default engine + _metadatas = {"": Model.metadata} + return + + for model in Model.__subclasses__(): + table: Table | None = getattr(model, "__table__", None) + + if table is None or (bind_key := table.info.get("bind_key")) is None: + continue + + _binds[model] = _engines.get(bind_key, _engines[""]) + table.to_metadata(_metadatas.get(bind_key, _metadatas[""])) + + +from .sql import * +from .model import * +from .config import * +from .migrate import * diff --git a/nonebot_plugin_orm/__main__.py b/nonebot_plugin_orm/__main__.py new file mode 100644 index 0000000..18a7f5b --- /dev/null +++ b/nonebot_plugin_orm/__main__.py @@ -0,0 +1,220 @@ +from __future__ import annotations + +from pathlib import Path +from typing import Iterable +from argparse import Namespace + +import click +from alembic.script import Script + +from . import migrate +from .config import plugin_config +from .migrate import AlembicConfig + + +@click.group() +@click.option( + "-c", + "--config", + envvar="ALEMBIC_CONFIG", + type=click.Path(exists=True, dir_okay=False, path_type=Path), + help='可选的配置文件;默认为 ALEMBIC_CONFIG 环境变量的值,或者 "alembic.ini"(如果存在)', +) +@click.option( + "-n", + "--name", + default="alembic", + show_default=True, + help=".ini 文件中用于 Alembic 配置的小节的名称", +) +@click.option( + "-x", + multiple=True, + help="自定义 env.py 脚本使用的其他参数,例如:-x setting1=somesetting -x setting2=somesetting", +) +@click.option("-q", "--quite", is_flag=True, help="不要输出日志到标准输出") +@click.pass_context +def orm( + ctx: click.Context, config: Path, name: str, x: tuple[str, ...], quite: bool +) -> None: + ctx.show_default = True + + if isinstance(plugin_config.alembic_config, AlembicConfig): + ctx.obj = plugin_config.alembic_config + else: + ctx.obj = AlembicConfig( + config, name, cmd_opts=Namespace(config=config, name=name, x=x, quite=quite) + ) + + ctx.with_resource(ctx.obj) + + +@orm.result_callback() +@click.pass_obj +def move_script(config_: AlembicConfig, scripts: Iterable[Script] | None, **_) -> None: + if not scripts: + return + + for script in scripts: + config_.move_script(script) + + +@orm.command("list_templates") +@click.pass_obj +def list_templates(*args, **kwargs) -> None: + """列出所有可用的模板。""" + return migrate.list_templates(*args, **kwargs) + + +@orm.command() +@click.argument( + "directory", + default=Path("migrations"), + type=click.Path(file_okay=False, writable=True, resolve_path=True, path_type=Path), +) +@click.option("-t", "--template", default="generic", help="使用的迁移环境模板") +@click.option("--package", is_flag=True, help="在脚本目录和版本目录中创建 __init__.py 文件") +@click.pass_obj +def init(*args, **kwargs) -> None: + """初始化脚本目录。""" + return migrate.init(*args, **kwargs) + + +@orm.command() +@click.option("-m", "--message", help="描述") +@click.option("--sql", is_flag=True, help="以 SQL 的形式输出修订脚本") +@click.option("--head", default="head", help="基准版本") +@click.option("--splice", is_flag=True, help="允许非头部修订作为基准版本") +@click.option("--branch-label", help="分支标签") +@click.option( + "--version-path", + default=None, + type=click.Path(file_okay=False, writable=True, resolve_path=True, path_type=Path), + help="存放修订文件的目录", +) +@click.option("--rev-id", help="指定而不是使用生成的修订 ID") +@click.option("--depends-on", help="依赖的修订") +@click.pass_obj +def revision(*args, **kwargs) -> Iterable[Script]: + """创建一个新修订文件。""" + return migrate.revision(*args, **kwargs) + + +@orm.command() +@click.pass_obj +def check(*args, **kwargs) -> None: + """检查数据库是否与模型定义一致。""" + return migrate.check(*args, **kwargs) + + +@orm.command() +@click.argument("revisions", nargs=-1) +@click.option("-m", "--message", help="描述") +@click.option("--branch-label", help="分支标签") +@click.option("--rev-id", help="指定而不是使用生成的修订 ID") +@click.pass_obj +def merge(*args, **kwargs) -> Iterable[Script]: + """合并多个修订。创建一个新的修订文件。""" + return migrate.merge(*args, **kwargs) + + +@orm.command() +@click.argument("revision", required=False) +@click.option("--sql", is_flag=True, help="以 SQL 的形式输出修订脚本") +@click.option("--tag", help="一个任意的字符串, 可在自定义的 env.py 中使用") +@click.pass_obj +def upgrade(*args, **kwargs) -> None: + """升级到较新版本。""" + return migrate.upgrade(*args, **kwargs) + + +@orm.command() +@click.argument("revision") +@click.option("--sql", is_flag=True, help="以 SQL 的形式输出修订脚本") +@click.option("--tag", help="一个任意的字符串, 可在自定义的 env.py 中使用") +@click.pass_obj +def downgrade(*args, **kwargs) -> None: + """回退到先前版本。""" + return migrate.downgrade(*args, **kwargs) + + +@orm.command() +@click.argument("rev", nargs=-1) +@click.pass_obj +def show(*args, **kwargs) -> None: + """显示修订的信息。""" + return migrate.show(*args, **kwargs) + + +@orm.command() +@click.option("-r", "--rev-range", required=False, help="范围") +@click.option("-v", "--verbose", is_flag=True, help="显示详细信息") +@click.option("-i", "--indicate-current", is_flag=True, help="指示出当前修订") +@click.pass_obj +def history(*args, **kwargs) -> None: + """显示修订的历史。""" + return migrate.history(*args, **kwargs) + + +@orm.command() +@click.option("-v", "--verbose", is_flag=True, help="显示详细信息") +@click.option("--resolve-dependencies", is_flag=True, help="将依赖的修订视作父修订") +@click.pass_obj +def heads(*args, **kwargs) -> None: + """显示所有的分支头。""" + return migrate.heads(*args, **kwargs) + + +@orm.command() +@click.option("-v", "--verbose", is_flag=True, help="显示详细信息") +@click.pass_obj +def branches(*args, **kwargs) -> None: + """显示所有的分支。""" + return migrate.branches(*args, **kwargs) + + +@orm.command() +@click.option("-v", "--verbose", is_flag=True, help="显示详细信息") +@click.pass_obj +def current(*args, **kwargs) -> None: + """显示当前的修订。""" + return migrate.current(*args, **kwargs) + + +@orm.command() +@click.argument("revisions", nargs=-1) +@click.option("--sql", is_flag=True, help="以 SQL 的形式输出修订脚本") +@click.option("--tag", help="一个任意的字符串, 可在自定义的 env.py 中使用") +@click.option("--purge", is_flag=True, help="在标记前清空数据库版本表") +@click.pass_obj +def stamp(*args, **kwargs) -> None: + """将数据库标记为特定的修订版本,不运行任何迁移。""" + return migrate.stamp(*args, **kwargs) + + +@orm.command() +@click.argument("rev", default="current") +@click.pass_obj +def edit(*args, **kwargs): + """使用 $EDITOR 编辑修订文件。""" + return migrate.edit(*args, **kwargs) + + +@orm.command("ensure_version") +@click.option("--sql", is_flag=True, help="以 SQL 的形式输出修订脚本") +@click.pass_obj +def ensure_version(*args, **kwargs) -> None: + """创建版本表。""" + return migrate.ensure_version(*args, **kwargs) + + +def main(): + from . import _init_table, _init_engines + + _init_engines() + _init_table() + orm(prog_name="nb orm") + + +if __name__ == "__main__": + main() diff --git a/nonebot_plugin_orm/config.py b/nonebot_plugin_orm/config.py new file mode 100644 index 0000000..b72a05c --- /dev/null +++ b/nonebot_plugin_orm/config.py @@ -0,0 +1,33 @@ +import os +from typing import Any, Union + +from sqlalchemy import URL +from nonebot import get_driver +from pydantic import BaseModel +from sqlalchemy.ext.asyncio import AsyncEngine + +from .migrate import AlembicConfig + +__all__ = ( + "Config", + "plugin_config", +) + + +class Config(BaseModel, arbitrary_types_allowed=True): + sqlalchemy_database_url: Union[str, URL, AsyncEngine] = "" + sqlalchemy_binds: dict[str, Union[str, URL, dict[str, Any], AsyncEngine]] = {} + sqlalchemy_echo: bool = False + sqlalchemy_engine_options: dict[str, Any] = {} + sqlalchemy_session_options: dict[str, Any] = {} + + alembic_config: Union[str, os.PathLike[str], AlembicConfig] = "" + alembic_script_location: Union[str, os.PathLike[str]] = "" + alembic_version_locations: Union[ + str, os.PathLike[str], dict[str, os.PathLike[str]] + ] = {} + alembic_context: dict[str, Any] = {"render_as_batch": True} + alembic_startup_check: bool = True + + +plugin_config = Config.parse_obj(get_driver().config) diff --git a/nonebot_plugin_orm/migrate.py b/nonebot_plugin_orm/migrate.py new file mode 100644 index 0000000..ef3bb18 --- /dev/null +++ b/nonebot_plugin_orm/migrate.py @@ -0,0 +1,850 @@ +from __future__ import annotations + +import os +import sys +import shutil +from pathlib import Path +from argparse import Namespace +from typing import Any, TextIO, cast +from tempfile import TemporaryDirectory +from configparser import DuplicateSectionError +from contextlib import suppress, contextmanager +from collections.abc import Mapping, Iterable, Sequence, Generator + +import click +from alembic.config import Config +from sqlalchemy.util import asbool +from alembic.operations.ops import UpgradeOps +from alembic.util.editor import open_in_editor +from nonebot import logger, get_loaded_plugins +from alembic.script import Script, ScriptDirectory +from alembic.util.messaging import obfuscate_url_pw +from alembic.autogenerate.api import RevisionContext +from alembic.util.langhelpers import rev_id as _rev_id +from alembic.runtime.environment import EnvironmentContext, ProcessRevisionDirectiveFn + +from .utils import is_editable + +if sys.version_info >= (3, 12): + from typing import Self +else: + from typing_extensions import Self + + +__all__ = ( + "AlembicConfig", + "list_templates", + "init", + "revision", + "check", + "merge", + "upgrade", + "downgrade", + "show", + "history", + "heads", + "branches", + "current", + "stamp", + "edit", + "ensure_version", +) + + +_SPLIT_ON_PATH = { + None: " ", + "space": " ", + "os": os.pathsep, + ":": ":", + ";": ";", +} + + +class AlembicConfig(Config): + _temp_dir: TemporaryDirectory + _plugin_version_locations: dict[str, Path] + + def __init__( + self, + file_: str | os.PathLike[str] | None = None, + ini_section: str = "alembic", + output_buffer: TextIO | None = None, + stdout: TextIO = sys.stdout, + cmd_opts: Namespace | None = None, + config_args: Mapping[str, Any] = {}, + attributes: dict = {}, + ) -> None: + from . import _engines, _metadatas, plugin_config + + if file_ is None and Path("alembic.ini").is_file(): + file_ = "alembic.ini" + + if plugin_config.alembic_script_location: + script_location = plugin_config.alembic_script_location + elif ( + Path("migrations/env.py").is_file() + and Path("migrations/script.py.mako").is_file() + ): + script_location = "migrations" + elif len(_engines) == 1: + script_location = str(Path(__file__).parent / "templates" / "generic") + else: + script_location = str(Path(__file__).parent / "templates" / "multidb") + + super().__init__( + file_, + ini_section, + output_buffer, + stdout, + cmd_opts, + { + "script_location": script_location, + "prepend_sys_path": ".", + "revision_environment": "true", + "version_path_separator": "os", + **config_args, + }, + { + "engines": _engines, + "metadatas": _metadatas, + **attributes, + }, + ) + + self._init_post_write_hooks() + + def __enter__(self) -> Self: + from . import plugin_config + + self._temp_dir = TemporaryDirectory() + + if self.get_main_option("version_locations"): + # NOTE: skip if explicitly set + return self + + if not plugin_config.alembic_version_locations: + self._plugin_version_locations = {"": Path("migrations/versions")} + elif isinstance(plugin_config.alembic_version_locations, dict): + self._plugin_version_locations = { + name: Path(path) + for name, path in plugin_config.alembic_version_locations.items() + } + else: + # NOTE: special case: mono repo with a central version location + self._plugin_version_locations = { + "": Path(plugin_config.alembic_version_locations) + } + + temp_version_locations = Path(self._temp_dir.name) + self._add_version_location(temp_version_locations) + + for plugin in get_loaded_plugins(): + if not plugin.metadata or not ( + version_module := plugin.metadata.extra.get("orm_version_location") + ): + continue + + version_location = version_module.__path__[0] + if is_editable(plugin): + self._add_version_location(version_location) + else: + # NOTE: read-only, copy to temp dir + self._add_version_location(temp_version_locations / plugin.name) + shutil.copytree(version_location, temp_version_locations / plugin.name) + + for plugin_name, version_location in self._plugin_version_locations.items(): + with suppress(FileNotFoundError): + shutil.copytree( + version_location, + temp_version_locations / plugin_name, + dirs_exist_ok=True, + ) + + return self + + def __exit__(self, *_) -> None: + self._temp_dir.cleanup() + + def get_template_directory(self) -> str: + return str(Path(__file__).parent / "templates") + + def print_stdout(self, text: str, *arg, **kwargs) -> None: + if not getattr(self.cmd_opts, "quite", False): + click.secho(text % arg, self.stdout, **kwargs) + + @contextmanager + def status(self, status_msg: str) -> Generator[None, Any, None]: + if getattr(self.cmd_opts, "quite", False): + yield + return + + try: + yield + except: + logger.error(f"{status_msg} 失败") + raise + else: + logger.success(f"{status_msg} 成功") + + def move_script(self, script: Script) -> Path: + script_path = Path(script.path) + + try: + script_path = script_path.relative_to(self._temp_dir.name) + except ValueError: + return script_path + + plugin_name = (script_path.parent.parts or ("",))[0] + if version_location := self._plugin_version_locations.get(plugin_name): + pass + elif version_location := self._plugin_version_locations.get(""): + plugin_name = "" + else: + logger.warning( + f'无法找到 {plugin_name or ""} 对应的版本目录,忽略 "{script.path}"' + ) + return script_path + + (version_location / script_path.relative_to(plugin_name).parent).mkdir( + parents=True, exist_ok=True + ) + return shutil.move( + script.path, version_location / script_path.relative_to(plugin_name) + ) + + def _add_post_write_hook(self, name: str, **kwargs: str) -> None: + self.set_section_option( + "post_write_hooks", + "hooks", + f"{self.get_section_option('post_write_hooks', 'hooks', '')}, {name}", + ) + for key, value in kwargs.items(): + self.set_section_option("post_write_hooks", f"{name}.{key}", value) + + def _init_post_write_hooks(self) -> None: + with suppress(DuplicateSectionError): + self.file_config.add_section("post_write_hooks") + + if self.get_section_option("post_write_hooks", "hooks"): + return + + with suppress(ImportError): + import isort + + del isort + self._add_post_write_hook( + "isort", + type="console_scripts", + entrypoint="isort", + options="REVISION_SCRIPT_FILENAME --profile black", + ) + + with suppress(ImportError): + import black + + del black + self._add_post_write_hook( + "black", + type="console_scripts", + entrypoint="black", + options="REVISION_SCRIPT_FILENAME", + ) + + def _add_version_location(self, path: str | Path) -> None: + pathsep = _SPLIT_ON_PATH[self.get_main_option("version_path_separator")] + self.set_main_option( + "version_locations", + f"{self.get_main_option('version_locations', '')}{pathsep}{path}", + ) + + +def list_templates(config: AlembicConfig) -> None: + """列出所有可用的模板。 + + 参数: + config: `AlembicConfig` 对象 + """ + + config.print_stdout("可用的模板:\n") + for tempname in Path(config.get_template_directory()).iterdir(): + with (tempname / "README").open() as readme: + synopsis = readme.readline().rstrip() + + config.print_stdout(f"{tempname.name} - {synopsis}") + + config.print_stdout('\n可以通过 "init" 命令使用模板,例如:') + config.print_stdout("\n nb orm init --template generic ./scripts") + + +def init( + config: AlembicConfig, + directory: Path = Path("migrations"), + template: str = "generic", + package: bool = False, +) -> None: + """初始化脚本目录。 + + 参数: + config: `AlembicConfig` 对象 + directory: 目标目录路径 + template: 使用的迁移环境模板 + package: 为 True 时,在脚本目录和版本目录中创建 `__init__.py` 文件 + """ + + if directory.is_dir() and next(directory.iterdir(), False): + raise click.BadParameter(f'目录 "{directory}" 已存在并且不为空', param_hint="DIRECTORY") + + template_dir = Path(config.get_template_directory()) / template + if not template_dir.is_dir(): + raise click.BadParameter(f"模板 {template} 不存在", param_hint="--template") + + with config.status(f'生成目录 "{directory}"'): + shutil.copytree( + template_dir, + directory, + ignore=None if package else shutil.ignore_patterns("__init__.py"), + dirs_exist_ok=True, + ) + + +def revision( + config: AlembicConfig, + message: str | None = None, + sql: bool | None = False, + head: str = "head", + splice: bool = False, + branch_label: str | None = None, + version_path: Path | None = None, + rev_id: str | None = None, + depends_on: str | None = None, + process_revision_directives: ProcessRevisionDirectiveFn | None = None, +) -> Iterable[Script]: + """创建一个新修订文件。 + + 参数: + config: `AlembicConfig` 对象 + message: 修订的描述 + sql: 是否以 SQL 的形式输出修订脚本 + head: 修订的基准版本 + splice: 是否将修订作为一个新的分支的头; 当 `head` 不是一个分支的头时, 此项必须为 `True` + branch_label: 修订的分支标签 + version_path: 存放修订文件的目录 + rev_id: 修订的 ID + depends_on: 修订的依赖 + process_revision_directives: 修订的处理函数, 参见: `alembic.EnvironmentContext.configure.process_revision_directives` + """ + if version_path is not None: + if version_path.is_dir() and next(version_path.iterdir(), False): + raise click.BadParameter( + f'目录 "{version_path}" 已存在并且不为空', param_hint="--version-path" + ) + config._add_version_location(version_path) + logger.warning( + f'临时将目录 "{version_path}" 添加到版本目录中,请稍后将其添加到 ALEMBIC_VERSION_LOCATIONS 中' + ) + + script_directory = ScriptDirectory.from_config(config) + + revision_context = RevisionContext( + config, + script_directory, + dict( + message=message, + autogenerate=not sql, + sql=sql, + head=head, + splice=splice, + branch_label=branch_label, + version_path=version_path, + rev_id=rev_id, + depends_on=depends_on, + ), + process_revision_directives=process_revision_directives, + ) + + if sql: + + def retrieve_migrations(rev, context): + revision_context.run_no_autogenerate(rev, context) + return () + + else: + + def retrieve_migrations(rev, context): + if set(script_directory.get_revisions(rev)) != set( + script_directory.get_revisions("heads") + ): + raise click.UsageError("目标数据库未更新到最新修订") + revision_context.run_autogenerate(rev, context) + return () + + with EnvironmentContext( + config, + script_directory, + fn=retrieve_migrations, + as_sql=sql, + template_args=revision_context.template_args, + revision_context=revision_context, + ): + script_directory.run_env() + + return filter(None, revision_context.generate_scripts()) + + +def check(config: AlembicConfig) -> None: + """检查数据库是否与模型定义一致。 + + 参数: + config: `AlembicConfig` 对象 + """ + + script_directory = ScriptDirectory.from_config(config) + + command_args = dict( + message=None, + autogenerate=True, + sql=False, + head="head", + splice=False, + branch_label=None, + version_path=None, + rev_id=None, + depends_on=None, + ) + revision_context = RevisionContext( + config, + script_directory, + command_args, + ) + + def retrieve_migrations(rev, context): + if set(script_directory.get_revisions(rev)) != set( + script_directory.get_revisions("heads") + ): + raise click.UsageError("目标数据库未更新到最新修订") + revision_context.run_autogenerate(rev, context) + return () + + with EnvironmentContext( + config, + script_directory, + fn=retrieve_migrations, + as_sql=False, + template_args=revision_context.template_args, + revision_context=revision_context, + ): + script_directory.run_env() + + # the revision_context now has MigrationScript structure(s) present. + + migration_script = revision_context.generated_revisions[-1] + diffs = cast(UpgradeOps, migration_script.upgrade_ops).as_diffs() + if diffs: + raise click.UsageError(f"检测到新的升级操作:{diffs}") + else: + config.print_stdout("没有检测到新的升级操作") + + +def merge( + config: AlembicConfig, + revisions: tuple[str, ...], + message: str | None = None, + branch_label: str | None = None, + rev_id: str | None = None, +) -> Iterable[Script]: + """合并多个修订。创建一个新的修订文件。 + + 参数: + config: `AlembicConfig` 对象 + revisions: 要合并的修订 + message: 修订的描述 + branch_label: 修订的分支标签 + rev_id: 修订的 ID + """ + + script_directory = ScriptDirectory.from_config(config) + template_args = {"config": config} + + environment = asbool(config.get_main_option("revision_environment")) + + if environment: + with EnvironmentContext( + config, + script_directory, + fn=lambda *_: (), + as_sql=False, + template_args=template_args, + ): + script_directory.run_env() + + script = script_directory.generate_revision( + rev_id or _rev_id(), + message, + refresh=True, + head=revisions, # type: ignore[arg-type] + branch_labels=branch_label, + **template_args, # type: ignore[arg-type] + ) + return (script,) if script else () + + +def upgrade( + config: AlembicConfig, + revision: str | None = None, + sql: bool = False, + tag: str | None = None, +) -> None: + """升级到较新版本。 + + 参数: + config: `AlembicConfig` 对象 + revision: 目标修订 + sql: 是否以 SQL 的形式输出修订脚本 + tag: 一个任意的字符串, 可在自定义的 `env.py` 中通过 `alembic.EnvironmentContext.get_tag_argument` 获得 + """ + + script = ScriptDirectory.from_config(config) + + if revision is None: + revision = "head" if len(script.get_heads()) == 1 else "heads" + + starting_rev = None + if ":" in revision: + if not sql: + raise click.BadParameter("不允许在非 --sql 模式下使用修订范围", param_hint="REVISION") + starting_rev, revision = revision.split(":", 2) + + def upgrade(rev, _): + return script._upgrade_revs(revision, rev) + + with EnvironmentContext( + config, + script, + fn=upgrade, + as_sql=sql, + starting_rev=starting_rev, + destination_rev=revision, + tag=tag, + ): + script.run_env() + + +def downgrade( + config: AlembicConfig, + revision: str, + sql: bool = False, + tag: str | None = None, +) -> None: + """回退到先前版本。 + + 参数: + config: `AlembicConfig` 对象 + revision: 目标修订 + sql: 是否以 SQL 的形式输出修订脚本 + tag: 一个任意的字符串, 可在自定义的 `env.py` 中通过 `alembic.EnvironmentContext.get_tag_argument` 获得 + """ + + script = ScriptDirectory.from_config(config) + starting_rev = None + if ":" in revision: + if not sql: + raise click.BadParameter("不允许在非 --sql 模式下使用修订范围", param_hint="REVISION") + starting_rev, revision = revision.split(":", 2) + elif sql: + raise click.BadParameter( + "--sql 模式下降级必须指定修订范围 :", param_hint="REVISION" + ) + + def downgrade(rev, _): + return script._downgrade_revs(revision, rev) + + with EnvironmentContext( + config, + script, + fn=downgrade, + as_sql=sql, + starting_rev=starting_rev, + destination_rev=revision, + tag=tag, + ): + script.run_env() + + +def show(config: AlembicConfig, revs: str | Sequence[str] = "current") -> None: + """显示修订的信息。 + + 参数: + config: `AlembicConfig` 对象 + revs: 目标修订范围 + """ + + script = ScriptDirectory.from_config(config) + + if revs in {(), "current", ("current",)}: + revs = [] + + with EnvironmentContext( + config, script, fn=lambda rev, _: revs.append(rev) or () + ): + script.run_env() + + for sc in cast("tuple[Script]", script.get_revisions(revs)): + config.print_stdout(sc.log_entry) + + +def history( + config: AlembicConfig, + rev_range: str | None = None, + verbose: bool = False, + indicate_current: bool = False, +) -> None: + """显示修订的历史。 + + 参数: + config: `AlembicConfig` 对象 + rev_range: 修订范围 + verbose: 是否显示详细信息 + indicate_current: 指示出当前修订 + """ + + script = ScriptDirectory.from_config(config) + if rev_range is not None: + if ":" not in rev_range: + raise click.BadParameter( + "历史范围应为 [start]:[end]、[start]: 或 :[end]", param_hint="REV_RANGE" + ) + base, head = rev_range.strip().split(":") + else: + base = head = None + + environment = ( + asbool(config.get_main_option("revision_environment")) or indicate_current + ) + + def _display_history(config, script, base, head, currents=()): + for sc in script.walk_revisions(base=base or "base", head=head or "heads"): + if indicate_current: + sc._db_current_indicator = sc.revision in currents + + config.print_stdout( + sc.cmd_format( + verbose=verbose, + include_branches=True, + include_doc=True, + include_parents=True, + ) + ) + + def _display_history_w_current(config, script, base, head): + def _display_current_history(rev): + if head == "current": + _display_history(config, script, base, rev, rev) + elif base == "current": + _display_history(config, script, rev, head, rev) + else: + _display_history(config, script, base, head, rev) + + revs = [] + with EnvironmentContext( + config, script, fn=lambda rev, _: revs.append(rev) or () + ): + script.run_env() + + for rev in revs: + _display_current_history(rev) + + if base == "current" or head == "current" or environment: + _display_history_w_current(config, script, base, head) + else: + _display_history(config, script, base, head) + + +def heads( + config: AlembicConfig, verbose: bool = False, resolve_dependencies: bool = False +) -> None: + """显示所有的分支头。 + + 参数: + config: `AlembicConfig` 对象 + verbose: 是否显示详细信息 + resolve_dependencies: 是否将依赖的修订视作父修订 + """ + + script = ScriptDirectory.from_config(config) + if resolve_dependencies: + heads = script.get_revisions("heads") + else: + heads = script.get_revisions(script.get_heads()) + + for rev in cast("tuple[Script]", heads): + config.print_stdout( + rev.cmd_format(verbose, include_branches=True, tree_indicators=False) + ) + + +def branches(config: AlembicConfig, verbose: bool = False) -> None: + """显示所有的分支。 + + 参数: + config: `AlembicConfig` 对象 + verbose: 是否显示详细信息 + """ + script = ScriptDirectory.from_config(config) + for sc in script.walk_revisions(): + if not sc.is_branch_point: + continue + + config.print_stdout( + "%s\n%s\n", + sc.cmd_format(verbose, include_branches=True), + "\n".join( + "%s -> %s" + % ( + " " * len(str(sc.revision)), + cast(Script, script.get_revision(rev)).cmd_format( + False, include_branches=True, include_doc=verbose + ), + ) + for rev in sc.nextrev + ), + ) + + +def current(config: AlembicConfig, verbose: bool = False) -> None: + """显示当前的修订。 + + 参数: + config: `AlembicConfig` 对象 + verbose: 是否显示详细信息 + """ + + script = ScriptDirectory.from_config(config) + + def display_version(rev, context): + if verbose: + config.print_stdout( + "Current revision(s) for %s:", + obfuscate_url_pw(context.connection.engine.url), + ) + for rev in cast("set[Script]", script.get_all_current(rev)): + config.print_stdout(rev.cmd_format(verbose)) + + return () + + with EnvironmentContext(config, script, fn=display_version, dont_mutate=True): + script.run_env() + + +def stamp( + config: AlembicConfig, + revisions: tuple[str, ...] = ("heads",), + sql: bool = False, + tag: str | None = None, + purge: bool = False, +) -> None: + """将数据库标记为特定的修订版本,不运行任何迁移。 + + 参数: + config: `AlembicConfig` 对象 + revisions: 目标修订 + sql: 是否以 SQL 的形式输出修订脚本 + tag: 一个任意的字符串,可在自定义的 `env.py` 中通过 `alembic.EnvironmentContext.get_tag_argument` 获得 + purge: 是否在标记前清空数据库版本表 + """ + + revisions = revisions or ("heads",) + script = ScriptDirectory.from_config(config) + + starting_rev = None + if sql: + destination_revs = [] + for revision in revisions: + if ":" in revision: + srev, revision = revision.split(":", 2) + + if starting_rev != srev: + if starting_rev is None: + starting_rev = srev + else: + raise click.BadParameter( + "--sql 模式下标记操作仅支持一个起始修订", param_hint="REVISIONS" + ) + destination_revs.append(revision) + else: + destination_revs = revisions + + def do_stamp(rev, _): + return script._stamp_revs(destination_revs, rev) + + with EnvironmentContext( + config, + script, + fn=do_stamp, + as_sql=sql, + starting_rev=starting_rev, + destination_rev=destination_revs, + tag=tag, + purge=purge, + ): + script.run_env() + + +def edit(config: AlembicConfig, rev: str = "current") -> None: + """使用 `$EDITOR` 编辑修订文件。 + + 参数: + config: `AlembicConfig` 对象 + rev: 目标修订 + """ + + script = ScriptDirectory.from_config(config) + temp_version_locations = click.get_current_context().meta.get( + f"{__name__}.temp_version_locations" + ) + + if rev == "current": + + def edit_current(rev, _): + if not rev: + raise click.UsageError("当前没有修订") + + for sc in cast("tuple[Script]", script.get_revisions(rev)): + script_path = config.move_script(sc) + open_in_editor(str(script_path)) + + return () + + with EnvironmentContext(config, script, fn=edit_current): + script.run_env() + else: + revs = cast(tuple[Script, ...], script.get_revisions(rev)) + + if not revs: + raise click.BadParameter(f'没有 "{rev}" 指示的修订文件') + + for sc in cast(tuple[Script], revs): + script_path = config.move_script(sc) + open_in_editor(str(script_path)) + + +def ensure_version(config: AlembicConfig, sql: bool = False) -> None: + """创建版本表。 + + 参数: + config: `AlembicConfig` 对象 + sql: 是否以 SQL 的形式输出修订脚本 + """ + + script = ScriptDirectory.from_config(config) + + def do_ensure_version(rev, context): + context._ensure_version_table() + return () + + with EnvironmentContext( + config, + script, + fn=do_ensure_version, + as_sql=sql, + ): + script.run_env() diff --git a/nonebot_plugin_orm/model.py b/nonebot_plugin_orm/model.py new file mode 100644 index 0000000..a9332c4 --- /dev/null +++ b/nonebot_plugin_orm/model.py @@ -0,0 +1,111 @@ +from __future__ import annotations + +from inspect import Parameter, Signature +from typing import TYPE_CHECKING, Any, ClassVar, Annotated, get_args, get_origin + +from sqlalchemy import Table, MetaData +from nonebot import get_plugin_by_module_name +from sqlalchemy.orm import Mapped, DeclarativeBase + +from .utils import DependsInner, get_annotations + +__all__ = ("Model",) + + +NAMING_CONVENTION = { + "ix": "ix_%(column_0_label)s", + "uq": "uq_%(table_name)s_%(column_0_name)s", + "ck": "ck_%(table_name)s_%(constraint_name)s", + "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", + "pk": "pk_%(table_name)s", +} + + +def _setup_di(cls: type[Model]) -> None: + """Get signature for NoneBot's dependency injection, + and set annotations for SQLAlchemy declarative class. + """ + + parameters: list[Parameter] = [] + + annotations: dict[str, Any] = {} + for base in reversed(cls.__mro__): + annotations.update(get_annotations(base, eval_str=True)) + + for name, type_annotation in annotations.items(): + # Check if the attribute is both a dependent and a mapped column + depends_inner = None + if isinstance(get_origin(type_annotation), Annotated): + (type_annotation, *extra_args) = get_args(type_annotation) + depends_inner = next( + (x for x in extra_args if isinstance(x, DependsInner)), None + ) + + if not isinstance(get_origin(type_annotation), Mapped): + continue + + default = getattr(cls, name, Signature.empty) + + depends_inner = default if isinstance(default, DependsInner) else depends_inner + if depends_inner is None: + continue + + # Set parameter for NoneBot dependency injection + parameters.append( + Parameter( + name, + Parameter.KEYWORD_ONLY, + default=depends_inner, + annotation=get_args(type_annotation)[0], + ) + ) + + # Set annotation for SQLAlchemy declarative class + cls.__annotations__[name] = type_annotation + if default is not Signature.empty and not isinstance(default, Mapped): + delattr(cls, name) + + cls.__signature__ = Signature(parameters) + + +def _setup_tablename(cls: type[Model]) -> None: + for attr in ("__abstract__", "__tablename__", "__table__"): + if getattr(cls, attr, None): + return + + cls.__tablename__ = cls.__name__.lower() + + if plugin := get_plugin_by_module_name(cls.__module__): + cls.__tablename__ = f"{plugin.name.replace('-', '_')}_{cls.__tablename__}" + + +def _setup_bind(cls: type[Model]) -> None: + bind_key: str | None = getattr(cls, "__bind_key__", None) + + if bind_key is None: + if plugin := get_plugin_by_module_name(cls.__module__): + bind_key = plugin.name + else: + bind_key = "" + + cls.__table__.info["bind_key"] = bind_key + + +class Model(DeclarativeBase): + metadata = MetaData(naming_convention=NAMING_CONVENTION) + + if TYPE_CHECKING: + __table__: ClassVar[Table] + __bind_key__: ClassVar[str] + __signature__: ClassVar[Signature] + + def __init_subclass__(cls) -> None: + _setup_di(cls) + _setup_tablename(cls) + + super().__init_subclass__() + + if not hasattr(cls, "__table__"): + return + + _setup_bind(cls) diff --git a/nonebot_plugin_orm/sql.py b/nonebot_plugin_orm/sql.py new file mode 100644 index 0000000..7866b44 --- /dev/null +++ b/nonebot_plugin_orm/sql.py @@ -0,0 +1,264 @@ +from __future__ import annotations + +import sys +from functools import wraps +from contextlib import suppress +from abc import ABCMeta, abstractmethod +from typing import Any, Union, Generic, TypeVar +from inspect import Parameter, Signature, signature +from collections.abc import Callable, Sequence, Coroutine + +import sqlalchemy as sa +from nonebot.params import Depends +from sqlalchemy.sql.compiler import SQLCompiler +from sqlalchemy import Row, ColumnExpressionArgument +from sqlalchemy.engine.default import DefaultDialect +from sqlalchemy.sql.elements import SQLCoreOperations +from sqlalchemy.exc import NoResultFound, InvalidRequestError +from sqlalchemy.sql.roles import ExpressionElementRole, TypedColumnsClauseRole +from sqlalchemy.ext.asyncio import ( + AsyncResult, + AsyncSession, + AsyncScalarResult, + async_scoped_session, +) + +from . import get_scoped_session +from .utils import DependsInner, return_eq + +if sys.version_info >= (3, 12): + from typing import Self, Unpack, TypeVarTuple, override +else: + from typing_extensions import Self, Unpack, TypeVarTuple, override + +__all__ = ( + "one", + "all_", + "first", + "select", + "scalars", + "scalar_all", + "scalar_one", + "one_or_none", + "scalar_first", + "one_or_create", + "scalar_one_or_none", +) + +_T = TypeVar("_T") +_Ts = TypeVarTuple("_Ts") +_TypedColumnClauseArgument = Union[ + TypedColumnsClauseRole[_T], + SQLCoreOperations[_T], + ExpressionElementRole[_T], + "type[_T]", +] + +_default_dialect = DefaultDialect() + + +class SelectBase(Generic[_T], metaclass=ABCMeta): + __signature__: Signature + + @abstractmethod + async def __call__( + self, *, session: async_scoped_session[AsyncSession], **kwargs: Any + ) -> Any: + raise NotImplementedError + + def all(self) -> Callable[..., Coroutine[Any, Any, Sequence[_T]]]: + @wraps(self) + async def all(**kwargs: Any) -> Sequence[_T]: + return await (await self(**kwargs)).all() + + return all + + def first(self) -> Callable[..., Coroutine[Any, Any, _T | None]]: + @wraps(self) + async def first(**kwargs: Any) -> _T: + return await (await self(**kwargs)).first() + + return first + + def one_or_none(self) -> Callable[..., Coroutine[Any, Any, _T | None]]: + @wraps(self) + async def one_or_none(**kwargs: Any) -> _T | None: + return await (await self(**kwargs)).one_or_none() + + return one_or_none + + def one(self) -> Callable[..., Coroutine[Any, Any, _T]]: + @wraps(self) + async def one(**kwargs: Any) -> _T: + return await (await self(**kwargs)).one() + + return one + + +class Select(sa.Select["tuple[Unpack[_Ts]]"], SelectBase[Row["tuple[Unpack[_Ts]]"]]): + inherit_cache = True + + _final: Self + + @property + @override + def __signature__(self) -> Signature: + try: + self._final = self.filter_by(__signature__=return_eq) + except InvalidRequestError: + self._final = self + + compiled = SQLCompiler(_default_dialect, self._final) + parameters = [ + Parameter( + "__session__", + Parameter.KEYWORD_ONLY, + default=Depends(get_scoped_session), + ), + *( + Parameter(name, Parameter.KEYWORD_ONLY, default=depends) + for name, depends in compiled.params.items() + if isinstance(depends, DependsInner) + ), + ] + + return Signature(parameters) + + @override + async def __call__( + self, *, __session__: async_scoped_session[AsyncSession], **kwargs: Any + ) -> AsyncResult[tuple[Unpack[_Ts]]]: + return await __session__.stream(self._final, kwargs) + + @override + def where( + self, + sig_or_clause: Signature | ColumnExpressionArgument[bool] | None = None, + *whereclause: ColumnExpressionArgument[bool], + ) -> Self: + if sig_or_clause is None: + return self + + if not isinstance(sig_or_clause, Signature): + return super().where(sig_or_clause, *whereclause) + + if whereclause: + raise ValueError( + "Cannot specify other where clauses when first argument is a Signature" + ) + + return self.filter_by( + **{name: param.default for name, param in sig_or_clause.parameters.items()} + ) + + @override + def as_scalar(self) -> ScalarSelect[Union[Unpack[_Ts]]]: + super().as_scalar() + return ScalarSelect(self) + + +def select(*entities: Any) -> Select[Unpack[tuple[Any, ...]]]: + return Select(*entities) + + +def all_( + *entities: Any, +) -> Callable[..., Coroutine[Any, Any, Sequence[Row[tuple[Any]]]]]: + return select(*entities).all() + + +def first(*entities: Any) -> Callable[..., Coroutine[Any, Any, Row[tuple[Any]] | None]]: + return select(*entities).first() + + +def one_or_none( + *entities: Any, +) -> Callable[..., Coroutine[Any, Any, Row[tuple[Any]] | None]]: + return select(*entities).one_or_none() + + +def one(*entities: Any) -> Callable[..., Coroutine[Any, Any, Row[tuple[Any]]]]: + return select(*entities).one() + + +class ScalarSelect(sa.ScalarSelect[_T], SelectBase[_T]): + element: Select[_T] + + @property + @override + def __signature__(self) -> Signature: + return self.element.__signature__ + + @override + async def __call__( + self, *, __session__: async_scoped_session[AsyncSession], **kwargs: Any + ) -> AsyncScalarResult[_T]: + return (await self.element(__session__=__session__, **kwargs)).scalars() + + +def scalars(entity: _TypedColumnClauseArgument[_T]) -> ScalarSelect[_T]: + return ScalarSelect(Select(entity)) + + +def scalar_all( + entity: _TypedColumnClauseArgument[_T], +) -> Callable[..., Coroutine[Any, Any, Sequence[_T]]]: + return scalars(entity).all() + + +def scalar_first( + entity: _TypedColumnClauseArgument[_T], +) -> Callable[..., Coroutine[Any, Any, _T | None]]: + return scalars(entity).first() + + +def scalar_one( + entity: _TypedColumnClauseArgument[_T], +) -> Callable[..., Coroutine[Any, Any, _T]]: + return scalars(entity).one() + + +def scalar_one_or_none( + entity: _TypedColumnClauseArgument[_T], +) -> Callable[..., Coroutine[Any, Any, _T | None]]: + return scalars(entity).one_or_none() + + +def one_or_create( + entity: type[_T], defaults: dict[str, Any] | None = None, **criterions: Any +) -> Callable[..., Coroutine[Any, Any, _T]]: + defaults = defaults or {} + parameters = dict(signature(entity).parameters.items()) + for name, value in criterions.items(): + if isinstance(value, DependsInner): + parameters[name] = Parameter(name, Parameter.KEYWORD_ONLY, default=value) + del criterions[name] + else: + with suppress(KeyError): + del parameters[name] + + async def _one_or_create( + __session__: async_scoped_session[AsyncSession], **kwargs: Any + ) -> _T: + try: + return await ( + await __session__.stream_scalars( + select(entity).filter_by(**criterions, **kwargs) + ) + ).one() + except NoResultFound: + instance = entity(**defaults, **criterions, **kwargs) + __session__.add(instance) + return instance + + _one_or_create.__signature__ = Signature( + ( + Parameter( + "__session__", + Parameter.KEYWORD_ONLY, + default=Depends(get_scoped_session), + ), + *parameters.values(), + ) + ) + return _one_or_create diff --git a/nonebot_plugin_orm/templates/generic/README b/nonebot_plugin_orm/templates/generic/README new file mode 100644 index 0000000..e129192 --- /dev/null +++ b/nonebot_plugin_orm/templates/generic/README @@ -0,0 +1 @@ +单数据库模板 diff --git a/nonebot_plugin_orm/templates/generic/__init__.py b/nonebot_plugin_orm/templates/generic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nonebot_plugin_orm/templates/generic/env.py b/nonebot_plugin_orm/templates/generic/env.py new file mode 100644 index 0000000..5ba7a0e --- /dev/null +++ b/nonebot_plugin_orm/templates/generic/env.py @@ -0,0 +1,69 @@ +from typing import cast + +from alembic import context +from sqlalchemy import Connection +from sqlalchemy.util import await_fallback +from sqlalchemy.ext.asyncio import AsyncEngine + +from nonebot_plugin_orm import AlembicConfig, plugin_config + +# Alembic Config 对象,它提供正在使用的 .ini 文件中的值。 +config = cast(AlembicConfig, context.config) + +# 默认 AsyncEngine +engine: AsyncEngine = config.attributes["engines"][""] + +# 模型的 MetaData,用于 'autogenerate' 支持。 +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = config.attributes["metadatas"][""] + +# 其他来自 config 的值,可以按 env.py 的需求定义,例如可以获取: +# my_important_option = config.get_main_option("my_important_option") +# ... 等等。 + + +def run_migrations_offline() -> None: + """在“离线”模式下运行迁移。 + + 虽然这里也可以获得 Engine,但我们只需要一个 URL 即可配置 context。 + 通过跳过 Engine 的创建,我们甚至不需要 DBAPI 可用。 + + 在这里调用 context.execute() 会将给定的字符串写入到脚本输出。 + """ + context.configure( + url=engine.url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + **plugin_config.alembic_context, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def do_run_migrations(connection: Connection) -> None: + context.configure( + connection=connection, + target_metadata=target_metadata, + **plugin_config.alembic_context, + ) + + with context.begin_transaction(): + context.run_migrations() + + +async def run_migrations_online() -> None: + """在“在线”模式下运行迁移。 + + 这种情况下,我们需要为 context 创建一个连接。 + """ + async with engine.begin() as connection: + await connection.run_sync(do_run_migrations) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + await_fallback(run_migrations_online()) diff --git a/nonebot_plugin_orm/templates/generic/script.py.mako b/nonebot_plugin_orm/templates/generic/script.py.mako new file mode 100644 index 0000000..48cc036 --- /dev/null +++ b/nonebot_plugin_orm/templates/generic/script.py.mako @@ -0,0 +1,29 @@ +"""${message} + +修订 ID: ${up_revision} +父修订: ${down_revision | comma,n} +创建时间: ${create_date} + +""" +from collections.abc import Sequence + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +revision: str = ${repr(up_revision)} +down_revision: str | Sequence[str] | None = ${repr(down_revision)} +branch_labels: str | Sequence[str] | None = ${repr(branch_labels)} +depends_on: str | Sequence[str] | None = ${repr(depends_on)} + + +def upgrade(name: str = "") -> None: + if name: + return + ${upgrades} + + +def downgrade(name: str = "") -> None: + if name: + return + ${downgrades} diff --git a/nonebot_plugin_orm/templates/generic/versions/__init__.py b/nonebot_plugin_orm/templates/generic/versions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nonebot_plugin_orm/templates/multidb/README b/nonebot_plugin_orm/templates/multidb/README new file mode 100644 index 0000000..6a8f1f9 --- /dev/null +++ b/nonebot_plugin_orm/templates/multidb/README @@ -0,0 +1,11 @@ +多数据库模板 + +多数据库与单数据库没有太大区别。 +最主要的区别是会运行迁移脚本 N 次(取决于你有多少个数据库), +并为每一次运行提供一个 `bind_key` 与对应的 `AsyncEngine` 和 `MetaData` 对象。 + +`bind_key` 允许迁移脚本正确地运行数据库对应的迁移操作,参见 mako 模板文件。 + +在模型定义中,你需要声明 `__bind_key__` 以将模型加入对应的 `MetaData` 中。 +在配置中,你可以提供 `bind_key` 对应的 `AsyncEngine` 或数据库 URL,或者使用默认数据库。 +在插件中,`bind_key` 默认为插件名(将 "-" 替换为 "\_")。 diff --git a/nonebot_plugin_orm/templates/multidb/__init__.py b/nonebot_plugin_orm/templates/multidb/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nonebot_plugin_orm/templates/multidb/env.py b/nonebot_plugin_orm/templates/multidb/env.py new file mode 100644 index 0000000..40e2863 --- /dev/null +++ b/nonebot_plugin_orm/templates/multidb/env.py @@ -0,0 +1,141 @@ +import asyncio +from typing import cast +from operator import methodcaller + +from alembic import context +from sqlalchemy.util import await_fallback +from alembic.migration import MigrationContext +from alembic.operations import MigrateOperation +from alembic.operations.ops import MigrationScript +from sqlalchemy.ext.asyncio import AsyncEngine, AsyncConnection +from sqlalchemy import MetaData, Connection, TwoPhaseTransaction + +from nonebot_plugin_orm import AlembicConfig, plugin_config + +# 是否使用二阶段提交 (Two-Phase Commit), +# 当同时迁移多个数据库时,可以启用以保证迁移的原子性。 +# 注意:只有部分数据库支持(例如 SQLite 就不支持)。 +USE_TWOPHASE = False + +# Alembic Config 对象,它提供正在使用的 .ini 文件中的值。 +config = cast(AlembicConfig, context.config) + +# bind key 到 AsyncEngine 的映射 +engines: dict[str, AsyncEngine] = config.attributes["engines"] + +# bind key 到 MetaData 的映射,用于 'autogenerate' 支持。 +# Metadata 对象必须仅包含对应数据库中的表。 +# table.to_metadata() 在需要“复制”表到 MetaData 中时可能很有用。 +# from myapp import mymodel +# target_metadata = { +# 'engine1':mymodel.metadata1, +# 'engine2':mymodel.metadata2 +# } +target_metadatas: dict[str, MetaData] = config.attributes["metadatas"] + +# 其他来自 config 的值,可以按 env.py 的需求定义,例如可以获取: +# my_important_option = config.get_main_option("my_important_option") +# ... 等等。 + + +def run_migrations_offline() -> None: + """在“离线”模式下运行迁移。 + + 虽然这里也可以获得 Engine,但我们只需要一个 URL 即可配置 context。 + 通过跳过 Engine 的创建,我们甚至不需要 DBAPI 可用。 + + 在这里调用 context.execute() 会将给定的字符串写入到脚本输出。 + + """ + # 使用 --sql 选项的情况下,将每个引擎的迁移写入到单独的 .sql 文件中。 + + for name, engine in engines.items(): + file_ = f"{name}.sql" + with open(file_, "w") as buffer: + context.configure( + url=engine.url, + output_buffer=buffer, + target_metadata=target_metadatas["name"], + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + **plugin_config.alembic_context, + ) + with context.begin_transaction(), config.status( + f"迁移数据库 {name or ''} 中" + ): + context.run_migrations(name=name) + config.print_stdout(f"将输出写入到 {file_}") + + +def process_revision_directives( + context: MigrationContext, + revision: tuple[str, str], + directives: list[MigrateOperation], +) -> None: + # 此回调用于防止在模型没有更改时生成自动迁移。 + # 参见:https://alembic.sqlalchemy.org/en/latest/cookbook.html#don-t-generate-empty-migrations-with-autogenerate + + if getattr(config.cmd_opts, "autogenerate", False) and all( + filter( + methodcaller("is_empty"), + cast("MigrationScript", directives[0]).upgrade_ops_list, + ) + ): + directives[:] = [] + config.print_stdout("未检测到模型更改") + + +def do_run_migrations(conn: Connection, name: str, metadata: MetaData) -> None: + context.configure( + connection=conn, + upgrade_token=f"{name}_upgrades", + downgrade_token=f"{name}_downgrades", + target_metadata=metadata, + process_revision_directives=process_revision_directives, + **plugin_config.alembic_context, + ) + with config.status(f"迁移数据库 {name or ''} 中"): + context.run_migrations(name=name) + + +async def run_migrations_online() -> None: + """在“在线”模式下运行迁移。 + + 这种情况下,我们需要为 context 创建一个连接。 + + """ + # 直接连接到数据库的情况下,对所有引擎启动事务,运行迁移,然后提交。 + + conns: dict[str, AsyncConnection] = {} + txns: dict[str, TwoPhaseTransaction] = {} + + try: + for name, engine in engines.items(): + conn = conns[name] = await engine.connect() + if USE_TWOPHASE: + txns[name] = await conn.run_sync(Connection.begin_twophase) + else: + await conn.begin() + + await conn.run_sync(do_run_migrations, name, target_metadatas[name]) + + if USE_TWOPHASE: + await asyncio.gather( + *( + conn.run_sync(lambda _: txns[name].prepare()) + for name, conn in conns.items() + ) + ) + + await asyncio.gather(*(conn.commit() for conn in conns.values())) + except BaseException: + await asyncio.gather(*(conn.rollback() for conn in conns.values())) + raise + finally: + await asyncio.gather(*(conn.close() for conn in conns.values())) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + await_fallback(run_migrations_online()) diff --git a/nonebot_plugin_orm/templates/multidb/script.py.mako b/nonebot_plugin_orm/templates/multidb/script.py.mako new file mode 100644 index 0000000..fedbccc --- /dev/null +++ b/nonebot_plugin_orm/templates/multidb/script.py.mako @@ -0,0 +1,39 @@ +"""${message} + +修订 ID: ${up_revision} +父修订: ${down_revision | comma,n} +创建时间: ${create_date} + +""" +from collections.abc import Sequence +from contextlib import suppress + +import sqlalchemy as sa +from alembic import op +${imports if imports else ""} + +revision: str = ${repr(up_revision)} +down_revision: str | Sequence[str] | None = ${repr(down_revision)} +branch_labels: str | Sequence[str] | None = ${repr(branch_labels)} +depends_on: str | Sequence[str] | None = ${repr(depends_on)} + + +def upgrade(name: str) -> None: + with suppress(KeyError): + globals()[f"upgrade_{name}"]() + + +def downgrade(name: str) -> None: + with suppress(KeyError): + globals()[f"downgrade_{name}"]() + +% for name in config.attributes["metadatas"]: + +def upgrade_${name}() -> None: + ${context.get(f"{name}_upgrades", "pass")} + + +def downgrade_${name}() -> None: + ${context.get(f"{name}_downgrades", "pass")} + +% endfor diff --git a/nonebot_plugin_orm/templates/multidb/versions/__init__.py b/nonebot_plugin_orm/templates/multidb/versions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nonebot_plugin_orm/utils.py b/nonebot_plugin_orm/utils.py new file mode 100644 index 0000000..975d680 --- /dev/null +++ b/nonebot_plugin_orm/utils.py @@ -0,0 +1,211 @@ +from __future__ import annotations + +import sys +import json +from io import StringIO +from typing import TypeVar +from contextlib import suppress +from functools import lru_cache +from importlib.metadata import Distribution, PackageNotFoundError, distribution + +from nonebot import logger +from nonebot.plugin import Plugin +from nonebot.params import Depends + +if sys.version_info >= (3, 10): + from importlib.metadata import packages_distributions +else: + from importlib_metadata import packages_distributions + +_T = TypeVar("_T") + + +DependsInner = type(Depends()) + + +class _ReturnEq: + def __eq__(self, __o: _T) -> _T: + return __o + + +return_eq = _ReturnEq() + + +class StreamToLogger(StringIO): + """Use for startup migrate only""" + + def __init__(self, level="INFO"): + self._level = level + + def write(self, buffer): + for line in buffer.rstrip().splitlines(): + # depth 0: this function + # depth 1: click.echo() + # depth 2: click.secho() + logger.opt(depth=3).log(self._level, line.rstrip()) + + def flush(self): + pass + + +_packages_distributions = lru_cache(None)(packages_distributions) + + +# https://github.com/pdm-project/pdm/blob/fee1e6bffd7de30315e2134e19f9a6f58e15867c/src/pdm/utils.py#L361-L374 +def is_editable(plugin: Plugin) -> bool: + """Check if the distribution is installed in editable mode""" + + while plugin.parent_plugin: + plugin = plugin.parent_plugin + + dist: Distribution | None = None + + if plugin.metadata: + with suppress(PackageNotFoundError): + dist = distribution(plugin.metadata.name) + + if not dist: + with suppress(KeyError, IndexError): + dist = distribution( + _packages_distributions()[plugin.module_name.split(".")[0]][0] + ) + + if not dist: + return "site-packages" not in plugin.module.__path__[0] + + # https://github.com/pdm-project/pdm/blob/fee1e6bffd7de30315e2134e19f9a6f58e15867c/src/pdm/utils.py#L361-L374 + if getattr(dist, "link_file", None) is not None: + return True + + direct_url = dist.read_text("direct_url.json") + + if not direct_url: + return False + + direct_url_data = json.loads(direct_url) + + return direct_url_data.get("dir_info", {}).get("editable", False) + + +if sys.version_info >= (3, 10): + from inspect import get_annotations as get_annotations # nopycln: import +else: + # https://github.com/python/cpython/blob/63a7f7765c6e9c1c9b93b7692e828ecf7bbd3bb9/Lib/inspect.py#L66-L178 + import types + import functools + + def get_annotations(obj, *, globals=None, locals=None, eval_str=False): + # sourcery skip + """Compute the annotations dict for an object. + + obj may be a callable, class, or module. + Passing in an object of any other type raises TypeError. + + Returns a dict. get_annotations() returns a new dict every time + it's called; calling it twice on the same object will return two + different but equivalent dicts. + + This function handles several details for you: + + * If eval_str is true, values of type str will + be un-stringized using eval(). This is intended + for use with stringized annotations + ("from __future__ import annotations"). + * If obj doesn't have an annotations dict, returns an + empty dict. (Functions and methods always have an + annotations dict; classes, modules, and other types of + callables may not.) + * Ignores inherited annotations on classes. If a class + doesn't have its own annotations dict, returns an empty dict. + * All accesses to object members and dict values are done + using getattr() and dict.get() for safety. + * Always, always, always returns a freshly-created dict. + + eval_str controls whether or not values of type str are replaced + with the result of calling eval() on those values: + + * If eval_str is true, eval() is called on values of type str. + * If eval_str is false (the default), values of type str are unchanged. + + globals and locals are passed in to eval(); see the documentation + for eval() for more information. If either globals or locals is + None, this function may replace that value with a context-specific + default, contingent on type(obj): + + * If obj is a module, globals defaults to obj.__dict__. + * If obj is a class, globals defaults to + sys.modules[obj.__module__].__dict__ and locals + defaults to the obj class namespace. + * If obj is a callable, globals defaults to obj.__globals__, + although if obj is a wrapped function (using + functools.update_wrapper()) it is first unwrapped. + """ + if isinstance(obj, type): + # class + obj_dict = getattr(obj, "__dict__", None) + if obj_dict and hasattr(obj_dict, "get"): + ann = obj_dict.get("__annotations__", None) + if isinstance(ann, types.GetSetDescriptorType): + ann = None + else: + ann = None + + obj_globals = None + module_name = getattr(obj, "__module__", None) + if module_name: + module = sys.modules.get(module_name, None) + if module: + obj_globals = getattr(module, "__dict__", None) + obj_locals = dict(vars(obj)) + unwrap = obj + elif isinstance(obj, types.ModuleType): + # module + ann = getattr(obj, "__annotations__", None) + obj_globals = getattr(obj, "__dict__") + obj_locals = None + unwrap = None + elif callable(obj): + # this includes types.Function, types.BuiltinFunctionType, + # types.BuiltinMethodType, functools.partial, functools.singledispatch, + # "class funclike" from Lib/test/test_inspect... on and on it goes. + ann = getattr(obj, "__annotations__", None) + obj_globals = getattr(obj, "__globals__", None) + obj_locals = None + unwrap = obj + else: + raise TypeError(f"{obj!r} is not a module, class, or callable.") + + if ann is None: + return {} + + if not isinstance(ann, dict): + raise ValueError(f"{obj!r}.__annotations__ is neither a dict nor None") + + if not ann: + return {} + + if not eval_str: + return dict(ann) + + if unwrap is not None: + while True: + if hasattr(unwrap, "__wrapped__"): + unwrap = unwrap.__wrapped__ # type: ignore[attr-defined] + continue + if isinstance(unwrap, functools.partial): + unwrap = unwrap.func + continue + break + if hasattr(unwrap, "__globals__"): + obj_globals = unwrap.__globals__ # type: ignore[attr-defined] + + if globals is None: + globals = obj_globals + if locals is None: + locals = obj_locals + + return_value = { + key: value if not isinstance(value, str) else eval(value, globals, locals) + for key, value in ann.items() + } + return return_value diff --git a/pdm.lock b/pdm.lock new file mode 100644 index 0000000..e639311 --- /dev/null +++ b/pdm.lock @@ -0,0 +1,987 @@ +# This file is @generated by PDM. +# It is not intended for manual editing. + +[metadata] +groups = ["default", "default", "dev", "mysql", "postgresql", "sqlite"] +cross_platform = true +static_urls = false +lock_version = "4.3" +content_hash = "sha256:6a0d818cf1a28cb45b0baeea257ff0fef2c065aee0d083ecf9eb6667f49eb01c" + +[[package]] +name = "aiomysql" +version = "0.2.0" +requires_python = ">=3.7" +summary = "MySQL driver for asyncio." +dependencies = [ + "PyMySQL>=1.0", +] +files = [ + {file = "aiomysql-0.2.0-py3-none-any.whl", hash = "sha256:b7c26da0daf23a5ec5e0b133c03d20657276e4eae9b73e040b72787f6f6ade0a"}, + {file = "aiomysql-0.2.0.tar.gz", hash = "sha256:558b9c26d580d08b8c5fd1be23c5231ce3aeff2dadad989540fee740253deb67"}, +] + +[[package]] +name = "aiosqlite" +version = "0.19.0" +requires_python = ">=3.7" +summary = "asyncio bridge to the standard sqlite3 module" +files = [ + {file = "aiosqlite-0.19.0-py3-none-any.whl", hash = "sha256:edba222e03453e094a3ce605db1b970c4b3376264e56f32e2a4959f948d66a96"}, + {file = "aiosqlite-0.19.0.tar.gz", hash = "sha256:95ee77b91c8d2808bd08a59fbebf66270e9090c3d92ffbf260dc0db0b979577d"}, +] + +[[package]] +name = "alembic" +version = "1.12.0" +requires_python = ">=3.7" +summary = "A database migration tool for SQLAlchemy." +dependencies = [ + "Mako", + "SQLAlchemy>=1.3.0", + "typing-extensions>=4", +] +files = [ + {file = "alembic-1.12.0-py3-none-any.whl", hash = "sha256:03226222f1cf943deee6c85d9464261a6c710cd19b4fe867a3ad1f25afda610f"}, + {file = "alembic-1.12.0.tar.gz", hash = "sha256:8e7645c32e4f200675e69f0745415335eb59a3663f5feb487abfa0b30c45888b"}, +] + +[[package]] +name = "black" +version = "23.9.1" +requires_python = ">=3.8" +summary = "The uncompromising code formatter." +dependencies = [ + "click>=8.0.0", + "mypy-extensions>=0.4.3", + "packaging>=22.0", + "pathspec>=0.9.0", + "platformdirs>=2", + "tomli>=1.1.0; python_version < \"3.11\"", + "typing-extensions>=4.0.1; python_version < \"3.11\"", +] +files = [ + {file = "black-23.9.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301"}, + {file = "black-23.9.1-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100"}, + {file = "black-23.9.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71"}, + {file = "black-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7"}, + {file = "black-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80"}, + {file = "black-23.9.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f"}, + {file = "black-23.9.1-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe"}, + {file = "black-23.9.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186"}, + {file = "black-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f"}, + {file = "black-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300"}, + {file = "black-23.9.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c"}, + {file = "black-23.9.1-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325"}, + {file = "black-23.9.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393"}, + {file = "black-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9"}, + {file = "black-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f"}, + {file = "black-23.9.1-py3-none-any.whl", hash = "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9"}, + {file = "black-23.9.1.tar.gz", hash = "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d"}, +] + +[[package]] +name = "cfgv" +version = "3.4.0" +requires_python = ">=3.8" +summary = "Validate configuration and produce human readable error messages." +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] + +[[package]] +name = "click" +version = "8.1.7" +requires_python = ">=3.7" +summary = "Composable command line interface toolkit" +dependencies = [ + "colorama; platform_system == \"Windows\"", +] +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +summary = "Cross-platform colored terminal text." +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "distlib" +version = "0.3.7" +summary = "Distribution utilities" +files = [ + {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, + {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, +] + +[[package]] +name = "filelock" +version = "3.12.4" +requires_python = ">=3.8" +summary = "A platform independent file lock." +files = [ + {file = "filelock-3.12.4-py3-none-any.whl", hash = "sha256:08c21d87ded6e2b9da6728c3dff51baf1dcecf973b768ef35bcbc3447edb9ad4"}, + {file = "filelock-3.12.4.tar.gz", hash = "sha256:2e6f249f1f3654291606e046b09f1fd5eac39b360664c27f5aad072012f8bcbd"}, +] + +[[package]] +name = "greenlet" +version = "3.0.0" +requires_python = ">=3.7" +summary = "Lightweight in-process concurrent programming" +files = [ + {file = "greenlet-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e09dea87cc91aea5500262993cbd484b41edf8af74f976719dd83fe724644cd6"}, + {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47932c434a3c8d3c86d865443fadc1fbf574e9b11d6650b656e602b1797908a"}, + {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bdfaeecf8cc705d35d8e6de324bf58427d7eafb55f67050d8f28053a3d57118c"}, + {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a68d670c8f89ff65c82b936275369e532772eebc027c3be68c6b87ad05ca695"}, + {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ad562a104cd41e9d4644f46ea37167b93190c6d5e4048fcc4b80d34ecb278f"}, + {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02a807b2a58d5cdebb07050efe3d7deaf915468d112dfcf5e426d0564aa3aa4a"}, + {file = "greenlet-3.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b1660a15a446206c8545edc292ab5c48b91ff732f91b3d3b30d9a915d5ec4779"}, + {file = "greenlet-3.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:813720bd57e193391dfe26f4871186cf460848b83df7e23e6bef698a7624b4c9"}, + {file = "greenlet-3.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:aa15a2ec737cb609ed48902b45c5e4ff6044feb5dcdfcf6fa8482379190330d7"}, + {file = "greenlet-3.0.0-cp310-universal2-macosx_11_0_x86_64.whl", hash = "sha256:7709fd7bb02b31908dc8fd35bfd0a29fc24681d5cc9ac1d64ad07f8d2b7db62f"}, + {file = "greenlet-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:211ef8d174601b80e01436f4e6905aca341b15a566f35a10dd8d1e93f5dbb3b7"}, + {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6512592cc49b2c6d9b19fbaa0312124cd4c4c8a90d28473f86f92685cc5fef8e"}, + {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:871b0a8835f9e9d461b7fdaa1b57e3492dd45398e87324c047469ce2fc9f516c"}, + {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b505fcfc26f4148551826a96f7317e02c400665fa0883fe505d4fcaab1dabfdd"}, + {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123910c58234a8d40eaab595bc56a5ae49bdd90122dde5bdc012c20595a94c14"}, + {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:96d9ea57292f636ec851a9bb961a5cc0f9976900e16e5d5647f19aa36ba6366b"}, + {file = "greenlet-3.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0b72b802496cccbd9b31acea72b6f87e7771ccfd7f7927437d592e5c92ed703c"}, + {file = "greenlet-3.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:527cd90ba3d8d7ae7dceb06fda619895768a46a1b4e423bdb24c1969823b8362"}, + {file = "greenlet-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:37f60b3a42d8b5499be910d1267b24355c495064f271cfe74bf28b17b099133c"}, + {file = "greenlet-3.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1482fba7fbed96ea7842b5a7fc11d61727e8be75a077e603e8ab49d24e234383"}, + {file = "greenlet-3.0.0-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:be557119bf467d37a8099d91fbf11b2de5eb1fd5fc5b91598407574848dc910f"}, + {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73b2f1922a39d5d59cc0e597987300df3396b148a9bd10b76a058a2f2772fc04"}, + {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1e22c22f7826096ad503e9bb681b05b8c1f5a8138469b255eb91f26a76634f2"}, + {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d363666acc21d2c204dd8705c0e0457d7b2ee7a76cb16ffc099d6799744ac99"}, + {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:334ef6ed8337bd0b58bb0ae4f7f2dcc84c9f116e474bb4ec250a8bb9bd797a66"}, + {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6672fdde0fd1a60b44fb1751a7779c6db487e42b0cc65e7caa6aa686874e79fb"}, + {file = "greenlet-3.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:952256c2bc5b4ee8df8dfc54fc4de330970bf5d79253c863fb5e6761f00dda35"}, + {file = "greenlet-3.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:269d06fa0f9624455ce08ae0179430eea61085e3cf6457f05982b37fd2cefe17"}, + {file = "greenlet-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:9adbd8ecf097e34ada8efde9b6fec4dd2a903b1e98037adf72d12993a1c80b51"}, + {file = "greenlet-3.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4cd83fb8d8e17633ad534d9ac93719ef8937568d730ef07ac3a98cb520fd93e4"}, + {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a5b2d4cdaf1c71057ff823a19d850ed5c6c2d3686cb71f73ae4d6382aaa7a06"}, + {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e7dcdfad252f2ca83c685b0fa9fba00e4d8f243b73839229d56ee3d9d219314"}, + {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c94e4e924d09b5a3e37b853fe5924a95eac058cb6f6fb437ebb588b7eda79870"}, + {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad6fb737e46b8bd63156b8f59ba6cdef46fe2b7db0c5804388a2d0519b8ddb99"}, + {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d55db1db455c59b46f794346efce896e754b8942817f46a1bada2d29446e305a"}, + {file = "greenlet-3.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:56867a3b3cf26dc8a0beecdb4459c59f4c47cdd5424618c08515f682e1d46692"}, + {file = "greenlet-3.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a812224a5fb17a538207e8cf8e86f517df2080c8ee0f8c1ed2bdaccd18f38f4"}, + {file = "greenlet-3.0.0-cp39-cp39-win32.whl", hash = "sha256:0d3f83ffb18dc57243e0151331e3c383b05e5b6c5029ac29f754745c800f8ed9"}, + {file = "greenlet-3.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:831d6f35037cf18ca5e80a737a27d822d87cd922521d18ed3dbc8a6967be50ce"}, + {file = "greenlet-3.0.0-cp39-universal2-macosx_11_0_x86_64.whl", hash = "sha256:a048293392d4e058298710a54dfaefcefdf49d287cd33fb1f7d63d55426e4355"}, + {file = "greenlet-3.0.0.tar.gz", hash = "sha256:19834e3f91f485442adc1ee440171ec5d9a4840a1f7bd5ed97833544719ce10b"}, +] + +[[package]] +name = "identify" +version = "2.5.30" +requires_python = ">=3.8" +summary = "File identification library for Python" +files = [ + {file = "identify-2.5.30-py2.py3-none-any.whl", hash = "sha256:afe67f26ae29bab007ec21b03d4114f41316ab9dd15aa8736a167481e108da54"}, + {file = "identify-2.5.30.tar.gz", hash = "sha256:f302a4256a15c849b91cfcdcec052a8ce914634b2f77ae87dad29cd749f2d88d"}, +] + +[[package]] +name = "idna" +version = "3.4" +requires_python = ">=3.5" +summary = "Internationalized Domain Names in Applications (IDNA)" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "importlib-metadata" +version = "6.8.0" +requires_python = ">=3.8" +summary = "Read metadata from Python packages" +dependencies = [ + "zipp>=0.5", +] +files = [ + {file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"}, + {file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"}, +] + +[[package]] +name = "isort" +version = "5.12.0" +requires_python = ">=3.8.0" +summary = "A Python utility / library to sort Python imports." +files = [ + {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, + {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, +] + +[[package]] +name = "libcst" +version = "1.1.0" +requires_python = ">=3.8" +summary = "A concrete syntax tree with AST-like properties for Python 3.5, 3.6, 3.7, 3.8, 3.9, and 3.10 programs." +dependencies = [ + "pyyaml>=5.2", + "typing-extensions>=3.7.4.2", + "typing-inspect>=0.4.0", +] +files = [ + {file = "libcst-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:63f75656fd733dc20354c46253fde3cf155613e37643c3eaf6f8818e95b7a3d1"}, + {file = "libcst-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8ae11eb1ea55a16dc0cdc61b41b29ac347da70fec14cc4381248e141ee2fbe6c"}, + {file = "libcst-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bc745d0c06420fe2644c28d6ddccea9474fb68a2135904043676deb4fa1e6bc"}, + {file = "libcst-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c1f2da45f1c45634090fd8672c15e0159fdc46853336686959b2d093b6e10fa"}, + {file = "libcst-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:003e5e83a12eed23542c4ea20fdc8de830887cc03662432bb36f84f8c4841b81"}, + {file = "libcst-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:3ebbb9732ae3cc4ae7a0e97890bed0a57c11d6df28790c2b9c869f7da653c7c7"}, + {file = "libcst-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d68c34e3038d3d1d6324eb47744cbf13f2c65e1214cf49db6ff2a6603c1cd838"}, + {file = "libcst-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9dffa1795c2804d183efb01c0f1efd20a7831db6a21a0311edf90b4100d67436"}, + {file = "libcst-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc9b6ac36d7ec9db2f053014ea488086ca2ed9c322be104fbe2c71ca759da4bb"}, + {file = "libcst-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b7a38ec4c1c009ac39027d51558b52851fb9234669ba5ba62283185963a31c"}, + {file = "libcst-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5297a16e575be8173185e936b7765c89a3ca69d4ae217a4af161814a0f9745a7"}, + {file = "libcst-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:7ccaf53925f81118aeaadb068a911fac8abaff608817d7343da280616a5ca9c1"}, + {file = "libcst-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:75816647736f7e09c6120bdbf408456f99b248d6272277eed9a58cf50fb8bc7d"}, + {file = "libcst-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c8f26250f87ca849a7303ed7a4fd6b2c7ac4dec16b7d7e68ca6a476d7c9bfcdb"}, + {file = "libcst-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d37326bd6f379c64190a28947a586b949de3a76be00176b0732c8ee87d67ebe"}, + {file = "libcst-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3d8cf974cfa2487b28f23f56c4bff90d550ef16505e58b0dca0493d5293784b"}, + {file = "libcst-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d1271403509b0a4ee6ff7917c2d33b5a015f44d1e208abb1da06ba93b2a378"}, + {file = "libcst-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:bca1841693941fdd18371824bb19a9702d5784cd347cb8231317dbdc7062c5bc"}, + {file = "libcst-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:73c086705ed34dbad16c62c9adca4249a556c1b022993d511da70ea85feaf669"}, + {file = "libcst-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3a07ecfabbbb8b93209f952a365549e65e658831e9231649f4f4e4263cad24b1"}, + {file = "libcst-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c653d9121d6572d8b7f8abf20f88b0a41aab77ff5a6a36e5a0ec0f19af0072e8"}, + {file = "libcst-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f1cd308a4c2f71d5e4eec6ee693819933a03b78edb2e4cc5e3ad1afd5fb3f07"}, + {file = "libcst-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8afb6101b8b3c86c5f9cec6b90ab4da16c3c236fe7396f88e8b93542bb341f7c"}, + {file = "libcst-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:d22d1abfe49aa60fc61fa867e10875a9b3024ba5a801112f4d7ba42d8d53242e"}, + {file = "libcst-1.1.0.tar.gz", hash = "sha256:0acbacb9a170455701845b7e940e2d7b9519db35a86768d86330a0b0deae1086"}, +] + +[[package]] +name = "loguru" +version = "0.7.2" +requires_python = ">=3.5" +summary = "Python logging made (stupidly) simple" +dependencies = [ + "colorama>=0.3.4; sys_platform == \"win32\"", + "win32-setctime>=1.0.0; sys_platform == \"win32\"", +] +files = [ + {file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"}, + {file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"}, +] + +[[package]] +name = "mako" +version = "1.2.4" +requires_python = ">=3.7" +summary = "A super-fast templating language that borrows the best ideas from the existing templating languages." +dependencies = [ + "MarkupSafe>=0.9.2", +] +files = [ + {file = "Mako-1.2.4-py3-none-any.whl", hash = "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818"}, + {file = "Mako-1.2.4.tar.gz", hash = "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34"}, +] + +[[package]] +name = "markupsafe" +version = "2.1.3" +requires_python = ">=3.7" +summary = "Safely add untrusted strings to HTML/XML markup." +files = [ + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, + {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, +] + +[[package]] +name = "multidict" +version = "6.0.4" +requires_python = ">=3.7" +summary = "multidict implementation" +files = [ + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, + {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, + {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, + {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, + {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, + {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, + {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, + {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +requires_python = ">=3.5" +summary = "Type system extensions for programs checked with the mypy type checker." +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "nodeenv" +version = "1.8.0" +requires_python = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +summary = "Node.js virtual environment builder" +dependencies = [ + "setuptools", +] +files = [ + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, +] + +[[package]] +name = "nonebot-plugin-localstore" +version = "0.5.1" +requires_python = ">=3.8,<4.0" +summary = "Local Storage Support for NoneBot2" +dependencies = [ + "nonebot2<3.0.0,>=2.0.0", + "typing-extensions>=4.0.0", +] +files = [ + {file = "nonebot_plugin_localstore-0.5.1-py3-none-any.whl", hash = "sha256:520c4dd42f00495ec8fc458c40c5877bc604556f58147c34c4051860df34575b"}, + {file = "nonebot_plugin_localstore-0.5.1.tar.gz", hash = "sha256:97491d213d419de3f76f941a8d80ab190aa9828950289ec83402028e0f0892eb"}, +] + +[[package]] +name = "nonebot2" +version = "2.1.1" +requires_python = ">=3.8,<4.0" +summary = "An asynchronous python bot framework." +dependencies = [ + "loguru<1.0.0,>=0.6.0", + "pydantic[dotenv]<2.0.0,>=1.10.0", + "pygtrie<3.0.0,>=2.4.1", + "tomli<3.0.0,>=2.0.1; python_version < \"3.11\"", + "typing-extensions<5.0.0,>=4.4.0", + "yarl<2.0.0,>=1.7.2", +] +files = [ + {file = "nonebot2-2.1.1-py3-none-any.whl", hash = "sha256:fe780eee116b2798e1db043b92aefa1b1f2bf1026725388a1dbbb211bbb4ef4b"}, + {file = "nonebot2-2.1.1.tar.gz", hash = "sha256:e3937c28bca26f2f8717fedd8594e0cfc79faa2d99a3209c4a63706727d700f8"}, +] + +[[package]] +name = "nonemoji" +version = "0.1.4" +requires_python = ">=3.7.3,<4.0.0" +summary = "Simple gitmoji cli written in python" +dependencies = [ + "noneprompt<0.2.0,>=0.1.3", +] +files = [ + {file = "nonemoji-0.1.4-py3-none-any.whl", hash = "sha256:6e2b22d315bd936df7d004cf55b13fac5d55abd36aba6b37b405da39b6f78269"}, + {file = "nonemoji-0.1.4.tar.gz", hash = "sha256:f7480e1f2f27f0a149da23f371bab0a47dd2cf46674f61798658b3daa7836fc5"}, +] + +[[package]] +name = "noneprompt" +version = "0.1.9" +requires_python = ">=3.8,<4.0" +summary = "Prompt toolkit for console interaction" +dependencies = [ + "prompt-toolkit<4.0.0,>=3.0.19", +] +files = [ + {file = "noneprompt-0.1.9-py3-none-any.whl", hash = "sha256:a54f1e6a19a3da2dedf7f365f80420e9ae49326a0ffe60a8a9c7afdee6b6eeb3"}, + {file = "noneprompt-0.1.9.tar.gz", hash = "sha256:338b8bb89a8d22ef35f1dedb3aa7c1b228cf139973bdc43c5ffc3eef64457db9"}, +] + +[[package]] +name = "packaging" +version = "23.2" +requires_python = ">=3.7" +summary = "Core utilities for Python packages" +files = [ + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, +] + +[[package]] +name = "pathspec" +version = "0.11.2" +requires_python = ">=3.7" +summary = "Utility library for gitignore style pattern matching of file paths." +files = [ + {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, + {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, +] + +[[package]] +name = "platformdirs" +version = "3.11.0" +requires_python = ">=3.7" +summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +files = [ + {file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"}, + {file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"}, +] + +[[package]] +name = "pre-commit" +version = "3.4.0" +requires_python = ">=3.8" +summary = "A framework for managing and maintaining multi-language pre-commit hooks." +dependencies = [ + "cfgv>=2.0.0", + "identify>=1.0.0", + "nodeenv>=0.11.1", + "pyyaml>=5.1", + "virtualenv>=20.10.0", +] +files = [ + {file = "pre_commit-3.4.0-py2.py3-none-any.whl", hash = "sha256:96d529a951f8b677f730a7212442027e8ba53f9b04d217c4c67dc56c393ad945"}, + {file = "pre_commit-3.4.0.tar.gz", hash = "sha256:6bbd5129a64cad4c0dfaeeb12cd8f7ea7e15b77028d985341478c8af3c759522"}, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.39" +requires_python = ">=3.7.0" +summary = "Library for building powerful interactive command lines in Python" +dependencies = [ + "wcwidth", +] +files = [ + {file = "prompt_toolkit-3.0.39-py3-none-any.whl", hash = "sha256:9dffbe1d8acf91e3de75f3b544e4842382fc06c6babe903ac9acb74dc6e08d88"}, + {file = "prompt_toolkit-3.0.39.tar.gz", hash = "sha256:04505ade687dc26dc4284b1ad19a83be2f2afe83e7a828ace0c72f3a1df72aac"}, +] + +[[package]] +name = "psycopg" +version = "3.1.12" +requires_python = ">=3.7" +summary = "PostgreSQL database adapter for Python" +dependencies = [ + "typing-extensions>=4.1", + "tzdata; sys_platform == \"win32\"", +] +files = [ + {file = "psycopg-3.1.12-py3-none-any.whl", hash = "sha256:8ec5230d6a7eb654b4fb3cf2d3eda8871d68f24807b934790504467f1deee9f8"}, + {file = "psycopg-3.1.12.tar.gz", hash = "sha256:cec7ad2bc6a8510e56c45746c631cf9394148bdc8a9a11fd8cf8554ce129ae78"}, +] + +[[package]] +name = "psycopg-binary" +version = "3.1.12" +requires_python = ">=3.7" +summary = "PostgreSQL database adapter for Python -- C optimisation distribution" +files = [ + {file = "psycopg_binary-3.1.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29a69f62aae8617361376d9ed1e34966ae9c3a74c4ab3aa430a7ce0c11530862"}, + {file = "psycopg_binary-3.1.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7308316fdb6796399041b80db0ab9f356504ed26427e46834ade82ba94b067ce"}, + {file = "psycopg_binary-3.1.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130752b9b2f8d071f179e257b9698cedfe4546be81ad5ecd8ed52cf9d725580d"}, + {file = "psycopg_binary-3.1.12-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:45bcecc96a6e6fe11e06b75f7ba8005d6f717f16fae7ab1cf5a0aec5191f87c3"}, + {file = "psycopg_binary-3.1.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc3f0fcc4fcccffda2450c725bee9fad73bc6c110cfbe3b8a777063845d9c6b9"}, + {file = "psycopg_binary-3.1.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f93749f0fe69cfbfec22af690bb4b241f1a4347c57be26fe2e5b70588f7d602f"}, + {file = "psycopg_binary-3.1.12-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:36147f708cc6a9d74c2b8d880f8dd3a6d53364b5c487536adaa022d435c90733"}, + {file = "psycopg_binary-3.1.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2bbcc6fbabc2b92d18d955d9fa104fd9d8bd2dcb97a279c4e788c6b714ffd1af"}, + {file = "psycopg_binary-3.1.12-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0dee8a1ecc501d9c3db06d08184712459bbb5806a09121c3a25e8cbe91e234d7"}, + {file = "psycopg_binary-3.1.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:49d6acf228edb5bd9000735b89b780b18face776d081b905cf68e149d57dfcc1"}, + {file = "psycopg_binary-3.1.12-cp310-cp310-win_amd64.whl", hash = "sha256:ee65335781a54f29f4abc28060a6188c41bdd42fdc3cbc1dd84695ed8ef18321"}, + {file = "psycopg_binary-3.1.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d401722aa38bda64d1ba8293f6dad99f6f684711e2c016a93f138f2bbcff2a4b"}, + {file = "psycopg_binary-3.1.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46eac158e8e794d9414a8fe7706beeee9b1ecc4accbea914314825ace8137105"}, + {file = "psycopg_binary-3.1.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f017400679aa38f6cb22b888b8ec198a5b100ec2132e6b3bcfa797b14b5b438"}, + {file = "psycopg_binary-3.1.12-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d176c4614f5208ab9938d5426d61627c8fbc7f8dab53fef42c8bf2ab8605aa51"}, + {file = "psycopg_binary-3.1.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c48c4f3fcfd9e75e3fdb18eea320de591e06059a859280ec26ce8d753299353d"}, + {file = "psycopg_binary-3.1.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98fce28d8136bdd883f20d26467bf259b5fb559eb64d8f83695690714cdfdad3"}, + {file = "psycopg_binary-3.1.12-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4a0f44bc29fc1b56ee1c865796cbe354078ee1e985f898e4915db185055bf7d"}, + {file = "psycopg_binary-3.1.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6def4f238ca02d6b42336b405d02729c081c978cda9b6ba7549a9c63a91ba823"}, + {file = "psycopg_binary-3.1.12-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:000838cb5ab7851116b462e58893a96b0f1e35864135a6283f3242a730ec45d3"}, + {file = "psycopg_binary-3.1.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7949e1aefe339f04dbecac6aa036c9cd137a58f966c4b96ab933823c340ee12"}, + {file = "psycopg_binary-3.1.12-cp311-cp311-win_amd64.whl", hash = "sha256:b32922872460575083487de41e17e8cf308c3550da02c704efe42960bc6c19de"}, + {file = "psycopg_binary-3.1.12-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:70054ada2f890d004dc3d5ff908e34aecb085fd599d40db2975c09a39c50dfc3"}, + {file = "psycopg_binary-3.1.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7544d6d74f5b5f9daafe8a4ed7d266787d62a2bf16f5120c45d42d1f4a856bc8"}, + {file = "psycopg_binary-3.1.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43197161099cb4e36a9ca44c10657908b619d7263ffcff30932ad4627430dc3c"}, + {file = "psycopg_binary-3.1.12-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68398cdf3aedd4042b1126b9aba34615f1ab592831483282f19f0159fce5ca75"}, + {file = "psycopg_binary-3.1.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77ae6cda3ffee2425aca9ea7af57296d0c701e2ac5897b48b95dfee050234592"}, + {file = "psycopg_binary-3.1.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:278e8888e90fb6ebd7eae8ccb85199eafd712b734e641e0d40f2a903e946102d"}, + {file = "psycopg_binary-3.1.12-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:047c4ba8d3089465b0a69c4c669128df43403867858d78da6b40b33788bfa89f"}, + {file = "psycopg_binary-3.1.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8248b11ac490bb74de80457ab0e9cef31c08164ff7b867031927a17e5c9e19ed"}, + {file = "psycopg_binary-3.1.12-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6979c02acb9783c6134ee516751b8f891a2d4db7f73ebecc9e92750283d6fb99"}, + {file = "psycopg_binary-3.1.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:eaf2375b724ad61ee82a5c2a849e57b12b3cb510ec8845084132bbb907cb3335"}, + {file = "psycopg_binary-3.1.12-cp312-cp312-win_amd64.whl", hash = "sha256:6177cfa6f872a9cc84dbfc7dc163af6ef01639c50acc9a441673f29c2305c37a"}, + {file = "psycopg_binary-3.1.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5112245daf98e22046316e72690689a8952a9b078908206a6b16cd28d84cde7c"}, + {file = "psycopg_binary-3.1.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2eb94bf0bd653c940517cd92dc4f98c85d505f69013b247dda747413bcf0a8b"}, + {file = "psycopg_binary-3.1.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d41b03ce52a109858735ac19fe0295e3f77bef0388d6a3e105074ad68f4a9645"}, + {file = "psycopg_binary-3.1.12-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4fddc3c9beaf745de3da10230f0144a4c667b21c3f7a94a3bb1fb004954c9810"}, + {file = "psycopg_binary-3.1.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5987616698c895ae079fb5e26811b72948cb3b75c2c690446379298e96c1568"}, + {file = "psycopg_binary-3.1.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4ae45d58bd79795a2d23d05be5496b226b09ac2688b9ed9808e13c345e2d542"}, + {file = "psycopg_binary-3.1.12-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bb98252ac8ba41a121f88979e4232ffc1d6722c953531cbdae2b328322308581"}, + {file = "psycopg_binary-3.1.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ca09e4937c9db24a58951ee9aea7aae7bca11a954b30c59f3b271e9bdebd80d7"}, + {file = "psycopg_binary-3.1.12-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:03e321e149d051daa20892ed1bb3beabf0aae98a8c37da30ec80fa12306f9ba9"}, + {file = "psycopg_binary-3.1.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d819cb43cccc10ba501b9d462409fcaaeb19f77b8379b2e7ca0ced4a49446d4a"}, + {file = "psycopg_binary-3.1.12-cp39-cp39-win_amd64.whl", hash = "sha256:c9eb2ba27760bc1303f0708ba95b9e4f3f3b77a081ef4f7f53375c71da3a1bee"}, +] + +[[package]] +name = "psycopg" +version = "3.1.12" +extras = ["binary"] +requires_python = ">=3.7" +summary = "PostgreSQL database adapter for Python" +dependencies = [ + "psycopg-binary==3.1.12", + "psycopg==3.1.12", +] +files = [ + {file = "psycopg-3.1.12-py3-none-any.whl", hash = "sha256:8ec5230d6a7eb654b4fb3cf2d3eda8871d68f24807b934790504467f1deee9f8"}, + {file = "psycopg-3.1.12.tar.gz", hash = "sha256:cec7ad2bc6a8510e56c45746c631cf9394148bdc8a9a11fd8cf8554ce129ae78"}, +] + +[[package]] +name = "pycln" +version = "2.2.2" +requires_python = ">=3.6.2,<4" +summary = "A formatter for finding and removing unused import statements." +dependencies = [ + "libcst>=0.3.10; python_version >= \"3.7\"", + "pathspec>=0.9.0", + "pyyaml>=5.3.1", + "tomlkit>=0.11.1", + "typer>=0.4.1", +] +files = [ + {file = "pycln-2.2.2-py3-none-any.whl", hash = "sha256:73ed5e997dac02cb6dc84bf87c1d834191d026cfabade94e913973610abd65f7"}, + {file = "pycln-2.2.2.tar.gz", hash = "sha256:a8a1bd0ac0f5d2fb5fcfb409e027667c2a5ac5341bb7fa23d4a7bd1c1cfc3fec"}, +] + +[[package]] +name = "pydantic" +version = "1.10.13" +requires_python = ">=3.7" +summary = "Data validation and settings management using python type hints" +dependencies = [ + "typing-extensions>=4.2.0", +] +files = [ + {file = "pydantic-1.10.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:efff03cc7a4f29d9009d1c96ceb1e7a70a65cfe86e89d34e4a5f2ab1e5693737"}, + {file = "pydantic-1.10.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ecea2b9d80e5333303eeb77e180b90e95eea8f765d08c3d278cd56b00345d01"}, + {file = "pydantic-1.10.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1740068fd8e2ef6eb27a20e5651df000978edce6da6803c2bef0bc74540f9548"}, + {file = "pydantic-1.10.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84bafe2e60b5e78bc64a2941b4c071a4b7404c5c907f5f5a99b0139781e69ed8"}, + {file = "pydantic-1.10.13-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bc0898c12f8e9c97f6cd44c0ed70d55749eaf783716896960b4ecce2edfd2d69"}, + {file = "pydantic-1.10.13-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:654db58ae399fe6434e55325a2c3e959836bd17a6f6a0b6ca8107ea0571d2e17"}, + {file = "pydantic-1.10.13-cp310-cp310-win_amd64.whl", hash = "sha256:75ac15385a3534d887a99c713aa3da88a30fbd6204a5cd0dc4dab3d770b9bd2f"}, + {file = "pydantic-1.10.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c553f6a156deb868ba38a23cf0df886c63492e9257f60a79c0fd8e7173537653"}, + {file = "pydantic-1.10.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5e08865bc6464df8c7d61439ef4439829e3ab62ab1669cddea8dd00cd74b9ffe"}, + {file = "pydantic-1.10.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e31647d85a2013d926ce60b84f9dd5300d44535a9941fe825dc349ae1f760df9"}, + {file = "pydantic-1.10.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:210ce042e8f6f7c01168b2d84d4c9eb2b009fe7bf572c2266e235edf14bacd80"}, + {file = "pydantic-1.10.13-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8ae5dd6b721459bfa30805f4c25880e0dd78fc5b5879f9f7a692196ddcb5a580"}, + {file = "pydantic-1.10.13-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f8e81fc5fb17dae698f52bdd1c4f18b6ca674d7068242b2aff075f588301bbb0"}, + {file = "pydantic-1.10.13-cp311-cp311-win_amd64.whl", hash = "sha256:61d9dce220447fb74f45e73d7ff3b530e25db30192ad8d425166d43c5deb6df0"}, + {file = "pydantic-1.10.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8ef467901d7a41fa0ca6db9ae3ec0021e3f657ce2c208e98cd511f3161c762c6"}, + {file = "pydantic-1.10.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:968ac42970f57b8344ee08837b62f6ee6f53c33f603547a55571c954a4225691"}, + {file = "pydantic-1.10.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9849f031cf8a2f0a928fe885e5a04b08006d6d41876b8bbd2fc68a18f9f2e3fd"}, + {file = "pydantic-1.10.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56e3ff861c3b9c6857579de282ce8baabf443f42ffba355bf070770ed63e11e1"}, + {file = "pydantic-1.10.13-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f00790179497767aae6bcdc36355792c79e7bbb20b145ff449700eb076c5f96"}, + {file = "pydantic-1.10.13-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:75b297827b59bc229cac1a23a2f7a4ac0031068e5be0ce385be1462e7e17a35d"}, + {file = "pydantic-1.10.13-cp39-cp39-win_amd64.whl", hash = "sha256:e70ca129d2053fb8b728ee7d1af8e553a928d7e301a311094b8a0501adc8763d"}, + {file = "pydantic-1.10.13-py3-none-any.whl", hash = "sha256:b87326822e71bd5f313e7d3bfdc77ac3247035ac10b0c0618bd99dcf95b1e687"}, + {file = "pydantic-1.10.13.tar.gz", hash = "sha256:32c8b48dcd3b2ac4e78b0ba4af3a2c2eb6048cb75202f0ea7b34feb740efc340"}, +] + +[[package]] +name = "pydantic" +version = "1.10.13" +extras = ["dotenv"] +requires_python = ">=3.7" +summary = "Data validation and settings management using python type hints" +dependencies = [ + "pydantic==1.10.13", + "python-dotenv>=0.10.4", +] +files = [ + {file = "pydantic-1.10.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:efff03cc7a4f29d9009d1c96ceb1e7a70a65cfe86e89d34e4a5f2ab1e5693737"}, + {file = "pydantic-1.10.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ecea2b9d80e5333303eeb77e180b90e95eea8f765d08c3d278cd56b00345d01"}, + {file = "pydantic-1.10.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1740068fd8e2ef6eb27a20e5651df000978edce6da6803c2bef0bc74540f9548"}, + {file = "pydantic-1.10.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84bafe2e60b5e78bc64a2941b4c071a4b7404c5c907f5f5a99b0139781e69ed8"}, + {file = "pydantic-1.10.13-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bc0898c12f8e9c97f6cd44c0ed70d55749eaf783716896960b4ecce2edfd2d69"}, + {file = "pydantic-1.10.13-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:654db58ae399fe6434e55325a2c3e959836bd17a6f6a0b6ca8107ea0571d2e17"}, + {file = "pydantic-1.10.13-cp310-cp310-win_amd64.whl", hash = "sha256:75ac15385a3534d887a99c713aa3da88a30fbd6204a5cd0dc4dab3d770b9bd2f"}, + {file = "pydantic-1.10.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c553f6a156deb868ba38a23cf0df886c63492e9257f60a79c0fd8e7173537653"}, + {file = "pydantic-1.10.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5e08865bc6464df8c7d61439ef4439829e3ab62ab1669cddea8dd00cd74b9ffe"}, + {file = "pydantic-1.10.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e31647d85a2013d926ce60b84f9dd5300d44535a9941fe825dc349ae1f760df9"}, + {file = "pydantic-1.10.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:210ce042e8f6f7c01168b2d84d4c9eb2b009fe7bf572c2266e235edf14bacd80"}, + {file = "pydantic-1.10.13-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8ae5dd6b721459bfa30805f4c25880e0dd78fc5b5879f9f7a692196ddcb5a580"}, + {file = "pydantic-1.10.13-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f8e81fc5fb17dae698f52bdd1c4f18b6ca674d7068242b2aff075f588301bbb0"}, + {file = "pydantic-1.10.13-cp311-cp311-win_amd64.whl", hash = "sha256:61d9dce220447fb74f45e73d7ff3b530e25db30192ad8d425166d43c5deb6df0"}, + {file = "pydantic-1.10.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8ef467901d7a41fa0ca6db9ae3ec0021e3f657ce2c208e98cd511f3161c762c6"}, + {file = "pydantic-1.10.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:968ac42970f57b8344ee08837b62f6ee6f53c33f603547a55571c954a4225691"}, + {file = "pydantic-1.10.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9849f031cf8a2f0a928fe885e5a04b08006d6d41876b8bbd2fc68a18f9f2e3fd"}, + {file = "pydantic-1.10.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56e3ff861c3b9c6857579de282ce8baabf443f42ffba355bf070770ed63e11e1"}, + {file = "pydantic-1.10.13-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f00790179497767aae6bcdc36355792c79e7bbb20b145ff449700eb076c5f96"}, + {file = "pydantic-1.10.13-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:75b297827b59bc229cac1a23a2f7a4ac0031068e5be0ce385be1462e7e17a35d"}, + {file = "pydantic-1.10.13-cp39-cp39-win_amd64.whl", hash = "sha256:e70ca129d2053fb8b728ee7d1af8e553a928d7e301a311094b8a0501adc8763d"}, + {file = "pydantic-1.10.13-py3-none-any.whl", hash = "sha256:b87326822e71bd5f313e7d3bfdc77ac3247035ac10b0c0618bd99dcf95b1e687"}, + {file = "pydantic-1.10.13.tar.gz", hash = "sha256:32c8b48dcd3b2ac4e78b0ba4af3a2c2eb6048cb75202f0ea7b34feb740efc340"}, +] + +[[package]] +name = "pygtrie" +version = "2.5.0" +summary = "A pure Python trie data structure implementation." +files = [ + {file = "pygtrie-2.5.0-py3-none-any.whl", hash = "sha256:8795cda8105493d5ae159a5bef313ff13156c5d4d72feddefacaad59f8c8ce16"}, + {file = "pygtrie-2.5.0.tar.gz", hash = "sha256:203514ad826eb403dab1d2e2ddd034e0d1534bbe4dbe0213bb0593f66beba4e2"}, +] + +[[package]] +name = "pymysql" +version = "1.1.0" +requires_python = ">=3.7" +summary = "Pure Python MySQL Driver" +files = [ + {file = "PyMySQL-1.1.0-py3-none-any.whl", hash = "sha256:8969ec6d763c856f7073c4c64662882675702efcb114b4bcbb955aea3a069fa7"}, + {file = "PyMySQL-1.1.0.tar.gz", hash = "sha256:4f13a7df8bf36a51e81dd9f3605fede45a4878fe02f9236349fd82a3f0612f96"}, +] + +[[package]] +name = "python-dotenv" +version = "1.0.0" +requires_python = ">=3.8" +summary = "Read key-value pairs from a .env file and set them as environment variables" +files = [ + {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, + {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.1" +requires_python = ">=3.6" +summary = "YAML parser and emitter for Python" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "setuptools" +version = "68.2.2" +requires_python = ">=3.8" +summary = "Easily download, build, install, upgrade, and uninstall Python packages" +files = [ + {file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"}, + {file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"}, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.21" +requires_python = ">=3.7" +summary = "Database Abstraction Library" +dependencies = [ + "greenlet!=0.4.17; platform_machine == \"aarch64\" or (platform_machine == \"ppc64le\" or (platform_machine == \"x86_64\" or (platform_machine == \"amd64\" or (platform_machine == \"AMD64\" or (platform_machine == \"win32\" or platform_machine == \"WIN32\")))))", + "typing-extensions>=4.2.0", +] +files = [ + {file = "SQLAlchemy-2.0.21-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1e7dc99b23e33c71d720c4ae37ebb095bebebbd31a24b7d99dfc4753d2803ede"}, + {file = "SQLAlchemy-2.0.21-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7f0c4ee579acfe6c994637527c386d1c22eb60bc1c1d36d940d8477e482095d4"}, + {file = "SQLAlchemy-2.0.21-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f7d57a7e140efe69ce2d7b057c3f9a595f98d0bbdfc23fd055efdfbaa46e3a5"}, + {file = "SQLAlchemy-2.0.21-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca38746eac23dd7c20bec9278d2058c7ad662b2f1576e4c3dbfcd7c00cc48fa"}, + {file = "SQLAlchemy-2.0.21-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3cf229704074bce31f7f47d12883afee3b0a02bb233a0ba45ddbfe542939cca4"}, + {file = "SQLAlchemy-2.0.21-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fb87f763b5d04a82ae84ccff25554ffd903baafba6698e18ebaf32561f2fe4aa"}, + {file = "SQLAlchemy-2.0.21-cp310-cp310-win32.whl", hash = "sha256:89e274604abb1a7fd5c14867a412c9d49c08ccf6ce3e1e04fffc068b5b6499d4"}, + {file = "SQLAlchemy-2.0.21-cp310-cp310-win_amd64.whl", hash = "sha256:e36339a68126ffb708dc6d1948161cea2a9e85d7d7b0c54f6999853d70d44430"}, + {file = "SQLAlchemy-2.0.21-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bf8eebccc66829010f06fbd2b80095d7872991bfe8415098b9fe47deaaa58063"}, + {file = "SQLAlchemy-2.0.21-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b977bfce15afa53d9cf6a632482d7968477625f030d86a109f7bdfe8ce3c064a"}, + {file = "SQLAlchemy-2.0.21-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ff3dc2f60dbf82c9e599c2915db1526d65415be323464f84de8db3e361ba5b9"}, + {file = "SQLAlchemy-2.0.21-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44ac5c89b6896f4740e7091f4a0ff2e62881da80c239dd9408f84f75a293dae9"}, + {file = "SQLAlchemy-2.0.21-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:87bf91ebf15258c4701d71dcdd9c4ba39521fb6a37379ea68088ce8cd869b446"}, + {file = "SQLAlchemy-2.0.21-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b69f1f754d92eb1cc6b50938359dead36b96a1dcf11a8670bff65fd9b21a4b09"}, + {file = "SQLAlchemy-2.0.21-cp311-cp311-win32.whl", hash = "sha256:af520a730d523eab77d754f5cf44cc7dd7ad2d54907adeb3233177eeb22f271b"}, + {file = "SQLAlchemy-2.0.21-cp311-cp311-win_amd64.whl", hash = "sha256:141675dae56522126986fa4ca713739d00ed3a6f08f3c2eb92c39c6dfec463ce"}, + {file = "SQLAlchemy-2.0.21-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:785e2f2c1cb50d0a44e2cdeea5fd36b5bf2d79c481c10f3a88a8be4cfa2c4615"}, + {file = "SQLAlchemy-2.0.21-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c111cd40910ffcb615b33605fc8f8e22146aeb7933d06569ac90f219818345ef"}, + {file = "SQLAlchemy-2.0.21-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9cba4e7369de663611ce7460a34be48e999e0bbb1feb9130070f0685e9a6b66"}, + {file = "SQLAlchemy-2.0.21-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50a69067af86ec7f11a8e50ba85544657b1477aabf64fa447fd3736b5a0a4f67"}, + {file = "SQLAlchemy-2.0.21-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ccb99c3138c9bde118b51a289d90096a3791658da9aea1754667302ed6564f6e"}, + {file = "SQLAlchemy-2.0.21-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:513fd5b6513d37e985eb5b7ed89da5fd9e72354e3523980ef00d439bc549c9e9"}, + {file = "SQLAlchemy-2.0.21-cp39-cp39-win32.whl", hash = "sha256:f9fefd6298433b6e9188252f3bff53b9ff0443c8fde27298b8a2b19f6617eeb9"}, + {file = "SQLAlchemy-2.0.21-cp39-cp39-win_amd64.whl", hash = "sha256:2e617727fe4091cedb3e4409b39368f424934c7faa78171749f704b49b4bb4ce"}, + {file = "SQLAlchemy-2.0.21-py3-none-any.whl", hash = "sha256:ea7da25ee458d8f404b93eb073116156fd7d8c2a776d8311534851f28277b4ce"}, + {file = "SQLAlchemy-2.0.21.tar.gz", hash = "sha256:05b971ab1ac2994a14c56b35eaaa91f86ba080e9ad481b20d99d77f381bb6258"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +requires_python = ">=3.7" +summary = "A lil' TOML parser" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tomlkit" +version = "0.12.1" +requires_python = ">=3.7" +summary = "Style preserving TOML library" +files = [ + {file = "tomlkit-0.12.1-py3-none-any.whl", hash = "sha256:712cbd236609acc6a3e2e97253dfc52d4c2082982a88f61b640ecf0817eab899"}, + {file = "tomlkit-0.12.1.tar.gz", hash = "sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86"}, +] + +[[package]] +name = "typer" +version = "0.9.0" +requires_python = ">=3.6" +summary = "Typer, build great CLIs. Easy to code. Based on Python type hints." +dependencies = [ + "click<9.0.0,>=7.1.1", + "typing-extensions>=3.7.4.3", +] +files = [ + {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, + {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, +] + +[[package]] +name = "typing-extensions" +version = "4.8.0" +requires_python = ">=3.8" +summary = "Backported and Experimental Type Hints for Python 3.8+" +files = [ + {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, + {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, +] + +[[package]] +name = "typing-inspect" +version = "0.9.0" +summary = "Runtime inspection utilities for typing module." +dependencies = [ + "mypy-extensions>=0.3.0", + "typing-extensions>=3.7.4", +] +files = [ + {file = "typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f"}, + {file = "typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"}, +] + +[[package]] +name = "tzdata" +version = "2023.3" +requires_python = ">=2" +summary = "Provider of IANA time zone data" +files = [ + {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, + {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, +] + +[[package]] +name = "virtualenv" +version = "20.24.5" +requires_python = ">=3.7" +summary = "Virtual Python Environment builder" +dependencies = [ + "distlib<1,>=0.3.7", + "filelock<4,>=3.12.2", + "platformdirs<4,>=3.9.1", +] +files = [ + {file = "virtualenv-20.24.5-py3-none-any.whl", hash = "sha256:b80039f280f4919c77b30f1c23294ae357c4c8701042086e3fc005963e4e537b"}, + {file = "virtualenv-20.24.5.tar.gz", hash = "sha256:e8361967f6da6fbdf1426483bfe9fca8287c242ac0bc30429905721cefbff752"}, +] + +[[package]] +name = "wcwidth" +version = "0.2.8" +summary = "Measures the displayed width of unicode strings in a terminal" +files = [ + {file = "wcwidth-0.2.8-py2.py3-none-any.whl", hash = "sha256:77f719e01648ed600dfa5402c347481c0992263b81a027344f3e1ba25493a704"}, + {file = "wcwidth-0.2.8.tar.gz", hash = "sha256:8705c569999ffbb4f6a87c6d1b80f324bd6db952f5eb0b95bc07517f4c1813d4"}, +] + +[[package]] +name = "win32-setctime" +version = "1.1.0" +requires_python = ">=3.5" +summary = "A small Python utility to set file creation time on Windows" +files = [ + {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"}, + {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"}, +] + +[[package]] +name = "yarl" +version = "1.9.2" +requires_python = ">=3.7" +summary = "Yet another URL library" +dependencies = [ + "idna>=2.0", + "multidict>=4.0", +] +files = [ + {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82"}, + {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8"}, + {file = "yarl-1.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528"}, + {file = "yarl-1.9.2-cp310-cp310-win32.whl", hash = "sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3"}, + {file = "yarl-1.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde"}, + {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6"}, + {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb"}, + {file = "yarl-1.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a"}, + {file = "yarl-1.9.2-cp311-cp311-win32.whl", hash = "sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8"}, + {file = "yarl-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051"}, + {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9"}, + {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8"}, + {file = "yarl-1.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80"}, + {file = "yarl-1.9.2-cp39-cp39-win32.whl", hash = "sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623"}, + {file = "yarl-1.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18"}, + {file = "yarl-1.9.2.tar.gz", hash = "sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571"}, +] + +[[package]] +name = "zipp" +version = "3.17.0" +requires_python = ">=3.8" +summary = "Backport of pathlib-compatible object wrapper for zip files" +files = [ + {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, + {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, +] diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index 05c1093..0000000 --- a/poetry.lock +++ /dev/null @@ -1,411 +0,0 @@ -# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. - -[[package]] -name = "black" -version = "23.3.0" -description = "The uncompromising code formatter." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"}, - {file = "black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"}, - {file = "black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"}, - {file = "black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"}, - {file = "black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"}, - {file = "black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"}, - {file = "black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"}, - {file = "black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"}, - {file = "black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"}, - {file = "black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"}, - {file = "black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"}, - {file = "black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"}, - {file = "black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"}, - {file = "black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"}, - {file = "black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"}, - {file = "black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "cfgv" -version = "3.3.1" -description = "Validate configuration and produce human readable error messages." -category = "dev" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, - {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, -] - -[[package]] -name = "click" -version = "8.1.3" -description = "Composable command line interface toolkit" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "distlib" -version = "0.3.6" -description = "Distribution utilities" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, - {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, -] - -[[package]] -name = "filelock" -version = "3.11.0" -description = "A platform independent file lock." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "filelock-3.11.0-py3-none-any.whl", hash = "sha256:f08a52314748335c6460fc8fe40cd5638b85001225db78c2aa01c8c0db83b318"}, - {file = "filelock-3.11.0.tar.gz", hash = "sha256:3618c0da67adcc0506b015fd11ef7faf1b493f0b40d87728e19986b536890c37"}, -] - -[package.extras] -docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.2.2)", "diff-cover (>=7.5)", "pytest (>=7.2.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] - -[[package]] -name = "identify" -version = "2.5.22" -description = "File identification library for Python" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "identify-2.5.22-py2.py3-none-any.whl", hash = "sha256:f0faad595a4687053669c112004178149f6c326db71ee999ae4636685753ad2f"}, - {file = "identify-2.5.22.tar.gz", hash = "sha256:f7a93d6cf98e29bd07663c60728e7a4057615068d7a639d132dc883b2d54d31e"}, -] - -[package.extras] -license = ["ukkonen"] - -[[package]] -name = "isort" -version = "5.12.0" -description = "A Python utility / library to sort Python imports." -category = "dev" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, - {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, -] - -[package.extras] -colors = ["colorama (>=0.4.3)"] -pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -category = "dev" -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "nodeenv" -version = "1.7.0" -description = "Node.js virtual environment builder" -category = "dev" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" -files = [ - {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, - {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, -] - -[package.dependencies] -setuptools = "*" - -[[package]] -name = "nonemoji" -version = "0.1.4" -description = "Simple gitmoji cli written in python" -category = "dev" -optional = false -python-versions = ">=3.7.3,<4.0.0" -files = [ - {file = "nonemoji-0.1.4-py3-none-any.whl", hash = "sha256:6e2b22d315bd936df7d004cf55b13fac5d55abd36aba6b37b405da39b6f78269"}, - {file = "nonemoji-0.1.4.tar.gz", hash = "sha256:f7480e1f2f27f0a149da23f371bab0a47dd2cf46674f61798658b3daa7836fc5"}, -] - -[package.dependencies] -noneprompt = ">=0.1.3,<0.2.0" - -[[package]] -name = "noneprompt" -version = "0.1.9" -description = "Prompt toolkit for console interaction" -category = "dev" -optional = false -python-versions = ">=3.8,<4.0" -files = [ - {file = "noneprompt-0.1.9-py3-none-any.whl", hash = "sha256:a54f1e6a19a3da2dedf7f365f80420e9ae49326a0ffe60a8a9c7afdee6b6eeb3"}, - {file = "noneprompt-0.1.9.tar.gz", hash = "sha256:338b8bb89a8d22ef35f1dedb3aa7c1b228cf139973bdc43c5ffc3eef64457db9"}, -] - -[package.dependencies] -prompt-toolkit = ">=3.0.19,<4.0.0" - -[[package]] -name = "packaging" -version = "23.0" -description = "Core utilities for Python packages" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, - {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, -] - -[[package]] -name = "pathspec" -version = "0.11.1" -description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, - {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, -] - -[[package]] -name = "platformdirs" -version = "3.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "platformdirs-3.2.0-py3-none-any.whl", hash = "sha256:ebe11c0d7a805086e99506aa331612429a72ca7cd52a1f0d277dc4adc20cb10e"}, - {file = "platformdirs-3.2.0.tar.gz", hash = "sha256:d5b638ca397f25f979350ff789db335903d7ea010ab28903f57b27e1b16c2b08"}, -] - -[package.extras] -docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.2.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] - -[[package]] -name = "pre-commit" -version = "3.2.2" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pre_commit-3.2.2-py2.py3-none-any.whl", hash = "sha256:0b4210aea813fe81144e87c5a291f09ea66f199f367fa1df41b55e1d26e1e2b4"}, - {file = "pre_commit-3.2.2.tar.gz", hash = "sha256:5b808fcbda4afbccf6d6633a56663fed35b6c2bc08096fd3d47ce197ac351d9d"}, -] - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -virtualenv = ">=20.10.0" - -[[package]] -name = "prompt-toolkit" -version = "3.0.38" -description = "Library for building powerful interactive command lines in Python" -category = "dev" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "prompt_toolkit-3.0.38-py3-none-any.whl", hash = "sha256:45ea77a2f7c60418850331366c81cf6b5b9cf4c7fd34616f733c5427e6abbb1f"}, - {file = "prompt_toolkit-3.0.38.tar.gz", hash = "sha256:23ac5d50538a9a38c8bde05fecb47d0b403ecd0662857a86f886f798563d5b9b"}, -] - -[package.dependencies] -wcwidth = "*" - -[[package]] -name = "pyyaml" -version = "6.0" -description = "YAML parser and emitter for Python" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {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"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, - {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, - {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {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"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {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"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, -] - -[[package]] -name = "setuptools" -version = "67.6.1" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "setuptools-67.6.1-py3-none-any.whl", hash = "sha256:e728ca814a823bf7bf60162daf9db95b93d532948c4c0bea762ce62f60189078"}, - {file = "setuptools-67.6.1.tar.gz", hash = "sha256:257de92a9d50a60b8e22abfcbb771571fde0dbf3ec234463212027a4eeecbe9a"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "typing-extensions" -version = "4.5.0" -description = "Backported and Experimental Type Hints for Python 3.7+" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, - {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, -] - -[[package]] -name = "virtualenv" -version = "20.21.0" -description = "Virtual Python Environment builder" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "virtualenv-20.21.0-py3-none-any.whl", hash = "sha256:31712f8f2a17bd06234fa97fdf19609e789dd4e3e4bf108c3da71d710651adbc"}, - {file = "virtualenv-20.21.0.tar.gz", hash = "sha256:f50e3e60f990a0757c9b68333c9fdaa72d7188caa417f96af9e52407831a3b68"}, -] - -[package.dependencies] -distlib = ">=0.3.6,<1" -filelock = ">=3.4.1,<4" -platformdirs = ">=2.4,<4" - -[package.extras] -docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] -test = ["covdefaults (>=2.2.2)", "coverage (>=7.1)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23)", "pytest (>=7.2.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)"] - -[[package]] -name = "wcwidth" -version = "0.2.6" -description = "Measures the displayed width of unicode strings in a terminal" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, - {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, -] - -[metadata] -lock-version = "2.0" -python-versions = "^3.8" -content-hash = "d973d76670e2d7251ece10e5fd2236182fb60a4583ea05fb9728486e12f9d3bd" diff --git a/pyproject.toml b/pyproject.toml index fe09437..07f8589 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,23 +1,56 @@ -[tool.poetry] +[project] name = "nonebot-plugin-orm" version = "0.1.0" description = "SQLAlchemy ORM support for nonebot" -authors = ["yanyongyu "] -license = "MIT" +authors = [ + { name = "yanyongyu", email = "yyy@nonebot.dev" }, + { name = "ProgramRipper", email = "programripper@foxmail.com" }, +] +dependencies = [ + "alembic~=1.12", + "click~=8.1", + "importlib-metadata~=6.8; python_version < '3.10'", + "nonebot2~=2.1", + "sqlalchemy~=2.0", + "typing-extensions~=4.8; python_version < '3.12'", + "psycopg[binary]~=3.1", +] +requires-python = ">=3.9,<4.0" readme = "README.md" +license = { text = "MIT" } +keywords = ["nonebot", "orm", "sqlalchemy"] + +[project.urls] homepage = "https://github.com/nonebot/plugin-orm" repository = "https://github.com/nonebot/plugin-orm" documentation = "https://github.com/nonebot/plugin-orm" -keywords = ["nonebot", "orm", "sqlalchemy"] -[tool.poetry.dependencies] -python = "^3.8" +[project.optional-dependencies] +default = ["aiosqlite~=0.19", "nonebot-plugin-localstore~=0.5"] +mysql = ["aiomysql~=0.2"] +postgresql = ["psycopg[binary]~=3.1"] +sqlite = ["aiosqlite~=0.19"] -[tool.poetry.group.dev.dependencies] -isort = "^5.10.1" -black = "^23.1.0" -nonemoji = "^0.1.2" -pre-commit = "^3.1.0" +[project.entry-points.nb_scripts] +orm = "nonebot_plugin_orm.__main__:main" + +[build-system] +requires = ["pdm-backend"] +build-backend = "pdm.backend" + +[tool.pdm] +[tool.pdm.dev-dependencies] +dev = [ + "aiomysql~=0.2", + "aiosqlite~=0.19", + "black~=23.7", + "isort~=5.12", + "nonebot-plugin-localstore~=0.5", + "nonemoji~=0.1", + "pre-commit~=3.4", + "psycopg[binary]~=3.1", + "pycln~=2.2", +] [tool.black] line-length = 88 @@ -36,6 +69,5 @@ extra_standard_library = ["typing_extensions"] [tool.pycln] path = "." -[build-system] -requires = ["poetry_core>=1.0.0"] -build-backend = "poetry.core.masonry.api" +[tool.pyright] +pythonVersion = "3.9"