From 6fece6d096a4fa5923145f28d45bb1ccd20cbd19 Mon Sep 17 00:00:00 2001 From: VinokurovVE Date: Wed, 23 Oct 2024 16:53:57 +0900 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=B1=D0=B0=D0=B7=D1=8B=20=D0=B8=20=D1=80=D0=BE?= =?UTF-8?q?=D1=83=D1=82=D1=8B=20=D0=B4=D0=BB=D1=8F=20=D0=A1=D0=B5=D1=80?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D0=BE=D0=B2=20=D0=B8=20=D0=91=D0=B0=D0=B7=20?= =?UTF-8?q?=D0=94=D0=B0=D0=BD=D0=BD=D1=8B=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- alembic.ini | 114 ++++++++++ alembic/README | 1 + alembic/env.py | 86 ++++++++ alembic/script.py.mako | 26 +++ ...2ad5b9d_create_tables_servers_databases.py | 55 +++++ core/__init__.py | 9 + core/base.py | 6 + core/crud/__init__.py | 0 core/crud/databases.py | 53 +++++ core/crud/servers.py | 53 +++++ core/db_helper.py | 46 ++++ core/models.py | 20 ++ core/router.py | 10 + core/routers/__init__.py | 0 core/routers/servers.py | 40 ++++ core/schemas.py | 19 ++ core/settings.py | 36 ++++ main.py | 13 +- poetry.lock | 203 +++++++++++++++++- pyproject.toml | 2 + settings.py | 20 -- test.db | Bin 0 -> 28672 bytes 22 files changed, 783 insertions(+), 29 deletions(-) create mode 100644 alembic.ini create mode 100644 alembic/README create mode 100644 alembic/env.py create mode 100644 alembic/script.py.mako create mode 100644 alembic/versions/2024_10_23_1651-71fbc2ad5b9d_create_tables_servers_databases.py create mode 100644 core/__init__.py create mode 100644 core/base.py create mode 100644 core/crud/__init__.py create mode 100644 core/crud/databases.py create mode 100644 core/crud/servers.py create mode 100644 core/db_helper.py create mode 100644 core/models.py create mode 100644 core/router.py create mode 100644 core/routers/__init__.py create mode 100644 core/routers/servers.py create mode 100644 core/schemas.py create mode 100644 core/settings.py delete mode 100644 settings.py create mode 100644 test.db diff --git a/alembic.ini b/alembic.ini new file mode 100644 index 0000000..3d7d610 --- /dev/null +++ b/alembic.ini @@ -0,0 +1,114 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts. +# Use forward slashes (/) also on windows to provide an os agnostic path +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python>=3.9 or backports.zoneinfo library. +# Any required deps can installed by adding `alembic[tz]` to the pip requirements +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +version_path_separator = os # Use os.pathsep. Default configuration used for new projects. + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = driver://user:pass@localhost/dbname + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +hooks = black +black.type = console_scripts +black.entrypoint = black +black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = --fix REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/alembic/README b/alembic/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/alembic/env.py b/alembic/env.py new file mode 100644 index 0000000..613e8ee --- /dev/null +++ b/alembic/env.py @@ -0,0 +1,86 @@ +import asyncio +from logging.config import fileConfig + +from sqlalchemy import pool +from sqlalchemy.engine import Connection +from sqlalchemy.ext.asyncio import async_engine_from_config +from core.settings import settings +from core.base import Base +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata +config.set_main_option("sqlalchemy.url", settings.database.url) +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + Calls to context.execute() here emit the given string to the + script output. + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def do_run_migrations(connection: Connection) -> None: + context.configure(connection=connection, target_metadata=target_metadata) + + with context.begin_transaction(): + context.run_migrations() + + +async def run_async_migrations() -> None: + """In this scenario we need to create an Engine + and associate a connection with the context. + """ + + connectable = async_engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + + await connectable.dispose() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + + asyncio.run(run_async_migrations()) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() \ No newline at end of file diff --git a/alembic/script.py.mako b/alembic/script.py.mako new file mode 100644 index 0000000..fbc4b07 --- /dev/null +++ b/alembic/script.py.mako @@ -0,0 +1,26 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + ${downgrades if downgrades else "pass"} diff --git a/alembic/versions/2024_10_23_1651-71fbc2ad5b9d_create_tables_servers_databases.py b/alembic/versions/2024_10_23_1651-71fbc2ad5b9d_create_tables_servers_databases.py new file mode 100644 index 0000000..6081734 --- /dev/null +++ b/alembic/versions/2024_10_23_1651-71fbc2ad5b9d_create_tables_servers_databases.py @@ -0,0 +1,55 @@ +"""create tables servers,databases + +Revision ID: 71fbc2ad5b9d +Revises: +Create Date: 2024-10-23 16:51:42.705117 + +""" + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = "71fbc2ad5b9d" +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "easy_servers", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("name", sa.String(length=255), nullable=False), + sa.Column("description", sa.String(length=1000), nullable=True), + sa.Column("url", sa.String(length=1000), nullable=False), + sa.PrimaryKeyConstraint("id", name=op.f("pk_easy_servers")), + sa.UniqueConstraint("name", name=op.f("uq_easy_servers_name")), + ) + op.create_table( + "easy_databases", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("name", sa.String(length=255), nullable=False), + sa.Column("description", sa.String(length=1000), nullable=True), + sa.Column("date_created", sa.DateTime(), nullable=True), + sa.Column("server_id", sa.Integer(), nullable=False), + sa.ForeignKeyConstraint( + ["server_id"], + ["easy_servers.id"], + name=op.f("fk_easy_databases_server_id_easy_servers"), + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk_easy_databases")), + sa.UniqueConstraint("name", name=op.f("uq_easy_databases_name")), + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("easy_databases") + op.drop_table("easy_servers") + # ### end Alembic commands ### diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 0000000..70ca88b --- /dev/null +++ b/core/__init__.py @@ -0,0 +1,9 @@ +__all__ = ( + "settings", + "db_helper", + "Base" +) +from .settings import settings +from .db_helper import db_helper +from .base import Base +from .models import * \ No newline at end of file diff --git a/core/base.py b/core/base.py new file mode 100644 index 0000000..cd3cbe0 --- /dev/null +++ b/core/base.py @@ -0,0 +1,6 @@ +from sqlalchemy.orm import DeclarativeBase +from sqlalchemy import MetaData +from core.settings import settings +class Base(DeclarativeBase): + __abstract__ = True + metadata = MetaData(naming_convention=settings.database.convention) \ No newline at end of file diff --git a/core/crud/__init__.py b/core/crud/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/crud/databases.py b/core/crud/databases.py new file mode 100644 index 0000000..37cb034 --- /dev/null +++ b/core/crud/databases.py @@ -0,0 +1,53 @@ +from ..models import Database +from ..schemas import DatabaseBase, DatabaseSchema +from sqlalchemy import select, delete +from sqlalchemy.ext.asyncio import AsyncSession +from typing import Sequence, Optional, List +async def get_all( + session: AsyncSession +) -> Sequence[DatabaseSchema]: + stmt = select(Database).order_by(Database.id) + result = await session.scalars(stmt) + return result.all() + +async def get( + session: AsyncSession, + id: int +) -> DatabaseSchema: + db_login = await session.get(Database,id) + if db_login is None: + return None + return db_login + +async def create( + session:AsyncSession, + model: DatabaseBase +) -> DatabaseSchema: + login = Database(**model.model_dump()) + session.add(login) + await session.commit() + return login + +async def delete( + session: AsyncSession, + id: int +): + db_login = await session.get(Database,id) + if db_login is None: + return None + stmt = delete(Database).filter(Database.id == id) + await session.execute(stmt) + await session.commit() + +async def update( + session:AsyncSession, + id: int, + model: DatabaseBase +) -> DatabaseSchema: + db_login = await session.get(Database,id) + if db_login is None: + return None + for var, value in vars(model).items(): + setattr(db_login, var, value) if value else None + await session.commit() + return db_login \ No newline at end of file diff --git a/core/crud/servers.py b/core/crud/servers.py new file mode 100644 index 0000000..30b9954 --- /dev/null +++ b/core/crud/servers.py @@ -0,0 +1,53 @@ +from ..models import Server +from ..schemas import ServerBase, ServerSchema +from sqlalchemy import select, delete +from sqlalchemy.ext.asyncio import AsyncSession +from typing import Sequence, Optional, List +async def get_all( + session: AsyncSession +) -> Sequence[ServerSchema]: + stmt = select(Server).order_by(Server.id) + result = await session.scalars(stmt) + return result.all() + +async def get( + session: AsyncSession, + id: int +) -> ServerSchema: + db_login = await session.get(Server,id) + if db_login is None: + return None + return db_login + +async def create( + session:AsyncSession, + model: ServerBase +) -> ServerSchema: + login = Server(**model.model_dump()) + session.add(login) + await session.commit() + return login + +async def delete( + session: AsyncSession, + id: int +): + db_login = await session.get(Server,id) + if db_login is None: + return None + stmt = delete(Server).filter(Server.id == id) + await session.execute(stmt) + await session.commit() + +async def update( + session:AsyncSession, + id: int, + model: ServerBase +) -> ServerSchema: + db_login = await session.get(Server,id) + if db_login is None: + return None + for var, value in vars(model).items(): + setattr(db_login, var, value) if value else None + await session.commit() + return db_login \ No newline at end of file diff --git a/core/db_helper.py b/core/db_helper.py new file mode 100644 index 0000000..a14b59d --- /dev/null +++ b/core/db_helper.py @@ -0,0 +1,46 @@ +from sqlalchemy.ext.asyncio import create_async_engine, AsyncEngine, async_sessionmaker, AsyncSession +from typing import AsyncGenerator +from core.settings import settings +class DatabaseHelper: + def __init__( + self, + url: str, + echo:bool=False, + echo_pool:bool=False, + pool_size: int = 5, + max_overflow: int =10, + ) -> None: + try: + self.engine: AsyncEngine = create_async_engine( + url=url, + echo=echo, + echo_pool=echo_pool, + pool_size=pool_size, + max_overflow = max_overflow + ) + except TypeError: + # The pool_size argument won't work for the default SQLite setup in SQLAlchemy 0.7, try without + self.engine: AsyncEngine = create_async_engine(url=url) + + self.session_factory: async_sessionmaker[AsyncSession] = async_sessionmaker( + bind= self.engine, + autoflush=False, + autocommit=False, + expire_on_commit=False + ) + + async def dispose(self) -> None: + await self.engine.dispose() + + async def session_getter(self) -> AsyncGenerator[AsyncSession,None]: + async with self.session_factory() as session: + yield session + +db_helper = DatabaseHelper( + url = settings.database.url, + echo = settings.database.echo, + echo_pool = settings.database.echo_pool, + pool_size = settings.database.pool_size, + max_overflow = settings.database.max_overflow, + +) \ No newline at end of file diff --git a/core/models.py b/core/models.py new file mode 100644 index 0000000..b9c02d9 --- /dev/null +++ b/core/models.py @@ -0,0 +1,20 @@ +from .base import Base +from sqlalchemy.orm import Mapped, mapped_column +from sqlalchemy import func +from sqlalchemy import String, Integer, Boolean, ForeignKey, DateTime +from datetime import date, datetime +class Server(Base): + __tablename__ = "easy_servers" + id: Mapped[int] = mapped_column(Integer, primary_key=True) + name:Mapped[str] = mapped_column(String(255), nullable=False, unique=True) + description: Mapped[str | None] = mapped_column(String(1000), nullable=True) + url: Mapped[str] = mapped_column(String(1000), nullable=False, unique=False) + + +class Database(Base): + __tablename__ = "easy_databases" + id: Mapped[int] = mapped_column(Integer, primary_key=True) + name:Mapped[str] = mapped_column(String(255), nullable=False, unique=True) + description: Mapped[str | None] = mapped_column(String(1000), nullable=True) + date_created: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default= func.now()) + server_id: Mapped[int] = mapped_column(Integer, ForeignKey("easy_servers.id")) \ No newline at end of file diff --git a/core/router.py b/core/router.py new file mode 100644 index 0000000..9d20801 --- /dev/null +++ b/core/router.py @@ -0,0 +1,10 @@ +from fastapi import APIRouter +from .routers.servers import router as server_router +router = APIRouter( + tags=["Core"], + prefix="/core", +) + +router.include_router( + router=server_router, +) \ No newline at end of file diff --git a/core/routers/__init__.py b/core/routers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/routers/servers.py b/core/routers/servers.py new file mode 100644 index 0000000..518a2ea --- /dev/null +++ b/core/routers/servers.py @@ -0,0 +1,40 @@ +from fastapi import APIRouter, Depends +from ..crud.servers import get, get_all,delete,update, create +from typing import Annotated, Sequence +from sqlalchemy.ext.asyncio import AsyncSession +from ..db_helper import db_helper +from ..schemas import ServerBase, ServerSchema +router = APIRouter( tags=["Server"], prefix="/server") + +@router.get("/", response_model=Sequence[ServerSchema]) +async def get_all_server(session: Annotated[AsyncSession, Depends(db_helper.session_getter)]): + return await get_all(session) + + +@router.get("/{id}", response_model=ServerSchema) +async def get_server(session: Annotated[AsyncSession, Depends(db_helper.session_getter)],id:int): + return await get(session,id) + +@router.post("/", response_model=ServerSchema) +async def create_server( + session: Annotated[AsyncSession, Depends(db_helper.session_getter)], + model: ServerBase + ): + login = await create( + session=session, + model=model + ) + return login + +@router.patch("/{id}", response_model=ServerSchema) +async def update_server( + session: Annotated[AsyncSession, Depends(db_helper.session_getter)], + id: int, + model: ServerBase + ): + login = await update( + session=session, + id=id, + model=model + ) + return login \ No newline at end of file diff --git a/core/schemas.py b/core/schemas.py new file mode 100644 index 0000000..4ff5ce0 --- /dev/null +++ b/core/schemas.py @@ -0,0 +1,19 @@ +from pydantic import BaseModel +from datetime import datetime + +class ServerBase(BaseModel): + name:str + description: str | None + url: str + +class ServerSchema(ServerBase): + id: int + +class DatabaseBase(BaseModel): + name:str + description: str | None + date_created: datetime | None + server_id: int + +class DatabaseSchema(DatabaseBase): + id: int \ No newline at end of file diff --git a/core/settings.py b/core/settings.py new file mode 100644 index 0000000..e2fd370 --- /dev/null +++ b/core/settings.py @@ -0,0 +1,36 @@ +from pydantic_settings import BaseSettings, SettingsConfigDict +from pydantic import BaseModel + +class UvicornSettings(BaseModel): + host: str = "0.0.0.0" + port: int = 8000 + reload: bool = True + +class DatabaseSettings(BaseModel): + url: str + echo:bool=False + echo_pool:bool=False + pool_size: int = 50 + max_overflow: int =10 + convention:dict = { + "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", + } + +class Settings(BaseSettings): + model_config = SettingsConfigDict( + env_file=".env", + case_sensitive=False, + env_nested_delimiter="__", + env_prefix="APP_CONFIG__" + ) + database: DatabaseSettings + uvicorn: UvicornSettings = UvicornSettings() + + + +settings = Settings() +print(settings.database.url) \ No newline at end of file diff --git a/main.py b/main.py index 665ae3d..9b1ca1b 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,7 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -from settings import Settings -import os -settings = Settings(_env_file=os.getenv("ENV", ".env")) - +from core.settings import settings +from core.router import router app = FastAPI() app.add_middleware( CORSMiddleware, @@ -13,10 +11,9 @@ app.add_middleware( allow_headers=["*"], ) -@app.get("/") -async def get_greeting(): - return "hello" - +app.include_router( + router=router, +) if __name__ == "__main__": import uvicorn diff --git a/poetry.lock b/poetry.lock index 1f07aa7..d80b92b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -32,6 +32,25 @@ typing_extensions = ">=4.0" dev = ["attribution (==1.7.0)", "black (==24.2.0)", "coverage[toml] (==7.4.1)", "flake8 (==7.0.0)", "flake8-bugbear (==24.2.6)", "flit (==3.9.0)", "mypy (==1.8.0)", "ufmt (==2.3.0)", "usort (==1.0.8.post1)"] docs = ["sphinx (==7.2.6)", "sphinx-mdinclude (==0.5.3)"] +[[package]] +name = "alembic" +version = "1.13.3" +description = "A database migration tool for SQLAlchemy." +optional = false +python-versions = ">=3.8" +files = [ + {file = "alembic-1.13.3-py3-none-any.whl", hash = "sha256:908e905976d15235fae59c9ac42c4c5b75cfcefe3d27c0fbf7ae15a37715d80e"}, + {file = "alembic-1.13.3.tar.gz", hash = "sha256:203503117415561e203aa14541740643a611f641517f0209fcae63e9fa09f1a2"}, +] + +[package.dependencies] +Mako = "*" +SQLAlchemy = ">=1.3.0" +typing-extensions = ">=4" + +[package.extras] +tz = ["backports.zoneinfo"] + [[package]] name = "annotated-types" version = "0.7.0" @@ -126,6 +145,50 @@ docs = ["Sphinx (>=8.1.3,<8.2.0)", "sphinx-rtd-theme (>=1.2.2)"] gssauth = ["gssapi", "sspilib"] test = ["distro (>=1.9.0,<1.10.0)", "flake8 (>=6.1,<7.0)", "flake8-pyi (>=24.1.0,<24.2.0)", "gssapi", "k5test", "mypy (>=1.8.0,<1.9.0)", "sspilib", "uvloop (>=0.15.3)"] +[[package]] +name = "black" +version = "24.10.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.9" +files = [ + {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, + {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, + {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, + {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, + {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, + {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, + {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, + {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, + {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, + {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, + {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, + {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, + {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, + {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, + {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, + {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, + {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, + {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, + {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, + {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, + {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, + {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.10)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + [[package]] name = "click" version = "8.1.7" @@ -282,6 +345,144 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +[[package]] +name = "mako" +version = "1.3.6" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Mako-1.3.6-py3-none-any.whl", hash = "sha256:a91198468092a2f1a0de86ca92690fb0cfc43ca90ee17e15d93662b4c04b241a"}, + {file = "mako-1.3.6.tar.gz", hash = "sha256:9ec3a1583713479fae654f83ed9fa8c9a4c16b7bb0daba0e6bbebff50c0d983d"}, +] + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + +[[package]] +name = "markupsafe" +version = "3.0.2" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.9" +files = [ + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +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 = "packaging" +version = "24.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] + [[package]] name = "pydantic" version = "2.9.2" @@ -641,4 +842,4 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "bc713f4c723ad1089646cc7dea7cfd7106ae641e50f5af008dcfd9b05dab8529" +content-hash = "61c128defdd4c6b5c86e51cf8e7e7cba55e52aaf3367448ab9e53140d04b04b1" diff --git a/pyproject.toml b/pyproject.toml index 1813f9b..a45eb91 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,8 @@ aioodbc = "^0.5.0" aiosqlite = "^0.20.0" asyncpg = "^0.30.0" uvicorn = "^0.32.0" +alembic = "^1.13.3" +black = "^24.10.0" [build-system] diff --git a/settings.py b/settings.py deleted file mode 100644 index f5d3d0a..0000000 --- a/settings.py +++ /dev/null @@ -1,20 +0,0 @@ -from pydantic_settings import BaseSettings -from pydantic import BaseModel - - - -class UvicornSettings(BaseModel): - host: str = "0.0.0.0" - port: int = 8000 - reload: bool = True - -class DatabaseSettings(BaseModel): - url: str = "aiosqlite:///easy_db.db" - -class Settings(BaseSettings): - database: DatabaseSettings = DatabaseSettings() - uvicorn: UvicornSettings = UvicornSettings() - class Config: - env_nested_delimiter = '__' - - diff --git a/test.db b/test.db new file mode 100644 index 0000000000000000000000000000000000000000..cec9ab8ac992dacbf8b039b658986e5c7cd4ab9b GIT binary patch literal 28672 zcmeI&-)h=W9Ki9UwK^@<-g!69jbP{)wOuJ?4CY+gNo`zH9h6;!Xi^AOYmM&D<=X9` zcDZ-h8}tQsxyzlXaVBd!80<#A!H^u!|IhD4IK-3Vnm2UC*~Or155=~!uBfW=TnI%` z67o#RGdz~%P4HcKsNu~$*M#!;?UQWor;=Q~QF7Vb*X*~o@2fZ2&-c_J3jqWWKmY** z5I_I{1pZy%`r%3{uW9O+_d~nYab4U0XgRjLw|&=-rjiv?FE@12D8H!bBAO?PnY8DK zs?pF7bW<4hhA^77+9Q!k_w24K-jvPCtFl?#+S$<}xsL0%2VQ^ZUG!#!N}HRTT2NIE z#I@Q3S6&?PQuf`b9_c}b?+)I%1M7|+D|O?fVV31!ME~514fM5HJt~{0;!r;oMb8=c zGgsj(Ucs96HEC&*^u>PN)T;+Z&`xnyL=&dIubaA2(N88qVewnK8ePW5W{s(uM)kO< zi(-(_GTPH*GGEtJ#p^ll2mi7oH)NJQ8eWXk@dirK$)>K>ms9ydLA`GOj_!0sJ!arYUPurrt%vb z>Q#DdX?NUi%WGS59r|+d#*#}B(O72iuuQ92y-{~YvAq?2Hs&B=Em-~YwuoTNwI>(N zv!!JIc;l`~#v3cez6kd64}zbRtUT4+4@G`B5I_I{1Q0*~0R#|0009ILKwv=x*3_lK zZt1Mm-m;yY)}E7*^J4$MAi-rC2q1s}0tg_000IagfB*srOa*ewh4lRXKY0Hy|Mbs+ z00IagfB*srAbfB*srAb1!5_-H5q literal 0 HcmV?d00001