diff --git a/.gitignore b/.gitignore
index 57c7c40..d3b3a6e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -284,3 +284,4 @@ dmypy.json
cython_debug/
credentials.env
+volumes/
diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
index 4a94487..286b5ab 100644
--- a/.idea/dataSources.xml
+++ b/.idea/dataSources.xml
@@ -1,35 +1,18 @@
-
- sqlite.xerial
+
+ mariadb
true
- org.sqlite.JDBC
- jdbc:sqlite:$PROJECT_DIR$/src/sqlite.db
+ org.mariadb.jdbc.Driver
+ jdbc:mariadb://localhost:3306/backend
+
+
+
$ProjectFileDir$
-
-
- file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.39.2/sqlite-jdbc-3.39.2.jar
-
-
-
-
- sqlite.xerial
- true
- org.sqlite.JDBC
- jdbc:sqlite:$PROJECT_DIR$/auth_volumes/kratos/db.sqlite
-
-
-
- $ProjectFileDir$
-
-
- file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.39.2/sqlite-jdbc-3.39.2.jar
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/Dev_Dependencies.xml b/.idea/runConfigurations/Dev_Dependencies.xml
new file mode 100644
index 0000000..70d88f3
--- /dev/null
+++ b/.idea/runConfigurations/Dev_Dependencies.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/FastAPI_app.xml b/.idea/runConfigurations/FastAPI_app.xml
index 48f3cf2..87d93e6 100644
--- a/.idea/runConfigurations/FastAPI_app.xml
+++ b/.idea/runConfigurations/FastAPI_app.xml
@@ -1,11 +1,18 @@
-
+
+
+
+
+
+
+
+
@@ -21,6 +28,8 @@
-
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Socket_io_app.xml b/.idea/runConfigurations/Socket_io_app.xml
index 7262f6e..4c41dbd 100644
--- a/.idea/runConfigurations/Socket_io_app.xml
+++ b/.idea/runConfigurations/Socket_io_app.xml
@@ -5,7 +5,14 @@
+
+
+
+
+
+
+
@@ -21,6 +28,8 @@
-
+
+
+
\ No newline at end of file
diff --git a/Makefile b/Makefile
index e75a7be..0853601 100644
--- a/Makefile
+++ b/Makefile
@@ -4,13 +4,16 @@ containers:
docker compose build --build-arg UID=`id -u`
dev-http:
- uv run ./src/http_app/dev_server.py
+ docker compose up dev-http
dev-socketio:
- uv run ./src/socketio_app/dev_server.py
+ docker compose up dev-socketio
-run:
- uv run uvicorn http_app:create_app --host 0.0.0.0 --port 8000 --factory
+migrate:
+ docker compose run --rm migrate
+
+autogenerate-migration:
+ docker compose run --rm autogenerate-migration
test:
uv run pytest -n auto --cov
@@ -34,9 +37,6 @@ update-dependencies:
uv lock --upgrade
uv sync --all-groups --frozen
-migrate:
- uv run alembic upgrade heads
-
format:
uv run ruff format --check .
@@ -53,8 +53,5 @@ check: lint format typing test
docs:
uv run mkdocs serve
-adr:
- adr-viewer --serve --adr-path docs/adr
-
docs-build:
uv run mkdocs build
diff --git a/README.md b/README.md
index b713f97..1b21d5f 100644
--- a/README.md
+++ b/README.md
@@ -44,29 +44,31 @@ Create your GitHub repository using this template (The big green `Use this templ
Optionally tweak name and authors in the `pyproject.toml` file, however the metadata
are not used when building the application, nor are referenced anywhere in the code.
-Before running any commands, install `uv`:
+Before running any commands, install `uv` and `Docker`:
-- On Mac (using `brew`): `brew install uv`
+- You can install `uv` on Mac using `brew`: `brew install uv`
+- Download and install Docker: https://www.docker.com/products/docker-desktop/
Using Docker:
* `make containers`: Build containers
-* `docker compose run --rm dev make migrate`: Run database migrations
* `docker compose up dev-http`: Run HTTP application with hot reload
* `docker compose up dev-socketio`: Run HTTP application with hot reload
* `docker compose up dramatiq-worker`: Run the dramatiq worker
* `docker compose run --rm test`: Run test suite
+* `docker compose run --rm migrate`: Run database migrations
+* `docker compose run --rm autogenerate-migration`: Generate a new migration file
-Locally:
+Using Make (you still need Docker for most of them):
-* `make migrate`: Run database migrations
* `make install-dependencies`: Install requirements
* `make dev-dependencies`: Install dev requirements
* `make update-dependencies`: Updates requirements
-* `make migrate`: Run database migrations
* `make dev-http`: Run HTTP application with hot reload
* `make dev-socketio`: Run HTTP application with hot reload
* `make test`: Run test suite
+* `make migrate`: Run database migrations
+* `make autogenerate-migration`: Generate a new migration file
## Other commands for development
diff --git a/alembic.ini b/alembic.ini
index b85e09a..d043558 100644
--- a/alembic.ini
+++ b/alembic.ini
@@ -67,3 +67,7 @@ version_path_separator = os # Use os.pathsep. Default configuration used for ne
# black.type = console_scripts
# black.entrypoint = black
# black.options = -l 79 REVISION_SCRIPT_FILENAME
+hooks = ruff
+ruff.type = exec
+ruff.executable = %(here)s/.venv/bin/ruff
+ruff.options = format REVISION_SCRIPT_FILENAME
diff --git a/credentials.env.template b/credentials.env.template
new file mode 100644
index 0000000..400f844
--- /dev/null
+++ b/credentials.env.template
@@ -0,0 +1,3 @@
+GC_USERNAME:
+GC_PASSWORD:
+GC_ENDPOINT:
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 80b354e..22f5e74 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -4,17 +4,21 @@ services:
dockerfile: Dockerfile
context: .
target: dev
- env_file: local.env
+ env_file: docker.env
environment:
- APP_NAME: "bootstrap-fastapi"
+ APP_NAME: "backend-fastapi"
ports:
- '8000:8000'
working_dir: "/app/src"
volumes:
- '.:/app'
depends_on:
- - redis
- - otel-collector
+ redis:
+ condition: service_healthy
+ otel-collector:
+ condition: service_started
+ mariadb:
+ condition: service_healthy
command:
- python
- ./http_app/dev_server.py
@@ -22,7 +26,7 @@ services:
dev-socketio:
<<: *dev
environment:
- APP_NAME: "bootstrap-socketio"
+ APP_NAME: "backend-socketio"
ports:
- '8001:8001'
command:
@@ -52,6 +56,7 @@ services:
- jaeger
ports:
- "12345:12345"
+ - "4317:4317"
volumes:
- ./config.alloy:/etc/alloy/config.alloy
command:
@@ -63,11 +68,34 @@ services:
redis:
image: redis
+ healthcheck:
+ test: ["CMD", "redis-cli", "ping"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+
+ mariadb:
+ image: mariadb:11.4
+ environment:
+ MYSQL_ROOT_PASSWORD: "stanis"
+ MYSQL_DATABASE: "backend"
+ MYSQL_USER: "corinna"
+ MYSQL_PASSWORD: "gioieiiere"
+ volumes:
+ - ./volumes/mariadb:/var/lib/mysql
+ ports:
+ - "3306:3306"
+ healthcheck:
+ test: [ "CMD", "healthcheck.sh", "--su-mysql", "--connect", "--innodb_initialized" ]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ start_period: 30s
dramatiq-worker:
<<: *dev
environment:
- APP_NAME: "bootstrap-dramatiq-worker"
+ APP_NAME: "backend-dramatiq-worker"
ports: []
command:
- dramatiq
@@ -151,12 +179,32 @@ services:
#### One-off commands ####
##########################
test:
- build:
- dockerfile: Dockerfile
- context: .
- target: dev
- volumes:
- - '.:/app'
+ <<: *dev
+ environment:
+ APP_NAME: "backend-test"
+ ports: []
command:
- "make"
- "test"
+
+ migrate:
+ <<: *dev
+ environment:
+ APP_NAME: "backend-migrations"
+ ports: []
+ command:
+ - "alembic"
+ - "upgrade"
+ - "heads"
+
+ autogenerate-migration:
+ <<: *dev
+ environment:
+ APP_NAME: "backend-migration-generator"
+ ports: []
+ command:
+ - "alembic"
+ - "revision"
+ - "--autogenerate"
+ - "-m"
+ - "Description message"
diff --git a/local.env b/docker.env
similarity index 55%
rename from local.env
rename to docker.env
index 6aad3d6..e75576a 100644
--- a/local.env
+++ b/docker.env
@@ -4,3 +4,9 @@ AUTH__JWKS_URL: "http://oathkeeper:4456/.well-known/jwks.json"
OTEL_EXPORTER_OTLP_ENDPOINT: "http://otel-collector:4317"
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: ".*"
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: ".*"
+CORS_ORIGINS: ["*"]
+GC_USERNAME: FAKE_USERNAME
+GC_PASSWORD: FAKE_PASS
+GC_ENDPOINT: FAKE_ENDPOINT
+SQLALCHEMY_CONFIG__default__engine_url: mysql+asyncmy://corinna:gioieiiere@mariadb/backend?charset=utf8mb4
+SQLALCHEMY_CONFIG__default__async_engine: true
diff --git a/pyproject.toml b/pyproject.toml
index 70272ae..e801ea9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -10,6 +10,7 @@ readme = "README.md"
dependencies = [
"aiosqlite>=0.18.0",
"asgiref<4.0.0,>=3.7.2",
+ "asyncmy>=0.2.10",
"cloudevents-pydantic<1.0.0,>=0.0.3",
"dependency-injector[pydantic]<5.0.0,>=4.41.0",
"dramatiq[redis,watch]<2.0.0,>=1.17.1",
@@ -24,7 +25,6 @@ dependencies = [
"pydantic<3.0.0,>=2.2.1",
"pydantic-asyncapi>=0.2.1",
"pydantic-settings<3.0.0,>=2.0.3",
- "rich<14.0.0,>=13.2.0",
"SQLAlchemy[asyncio,mypy]<3.0.0,>=2.0.0",
"sqlalchemy-bind-manager",
"structlog<25.1.1,>=25.1.0",
diff --git a/src/alembic.ini b/src/alembic.ini
index 223f247..dd1c4a4 100644
--- a/src/alembic.ini
+++ b/src/alembic.ini
@@ -5,3 +5,9 @@ script_location = migrations
prepend_sys_path = .
file_template = %%(year)d-%%(month).2d-%%(day).2d-%%(hour).2d%%(minute).2d%%(second).2d-%%(rev)s_%%(slug)s
version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
+
+[post_write_hooks]
+hooks = ruff
+ruff.type = exec
+ruff.executable = /venv/bin/ruff
+ruff.options = format REVISION_SCRIPT_FILENAME
diff --git a/src/common/bootstrap.py b/src/common/bootstrap.py
index fa0766c..100be87 100644
--- a/src/common/bootstrap.py
+++ b/src/common/bootstrap.py
@@ -1,6 +1,4 @@
-from typing import cast
-
-from dependency_injector.containers import DynamicContainer
+from dependency_injector.containers import Container as DI_Container
from dependency_injector.providers import Object
from pydantic import BaseModel, ConfigDict
@@ -14,17 +12,17 @@
class InitReference(BaseModel):
- di_container: DynamicContainer
+ di_container: DI_Container
model_config = ConfigDict(arbitrary_types_allowed=True)
-def application_init(app_config: AppConfig) -> InitReference:
- container = cast(
- DynamicContainer, # Make mypy happy
- Container(
- config=Object(app_config),
- ),
+def application_init(
+ app_config: AppConfig,
+ external_di_container: Container | None = None,
+) -> InitReference:
+ container = external_di_container or Container(
+ config=Object(app_config),
)
init_logger(app_config)
init_storage()
diff --git a/src/common/config.py b/src/common/config.py
index 8b6489a..86d15e7 100644
--- a/src/common/config.py
+++ b/src/common/config.py
@@ -1,7 +1,6 @@
-from pathlib import Path
from typing import Dict, Literal, Optional
-from pydantic import BaseModel
+from pydantic import BaseModel, Field
from pydantic_settings import BaseSettings, SettingsConfigDict
from sqlalchemy_bind_manager import SQLAlchemyConfig
@@ -21,20 +20,16 @@ class AppConfig(BaseSettings):
model_config = SettingsConfigDict(env_nested_delimiter="__")
APP_NAME: str = "bootstrap"
+ CORS_ORIGINS: list[str] = Field(default_factory=list)
+ CORS_METHODS: list[str] = ["*"]
+ CORS_HEADERS: list[str] = ["*"]
AUTH: AuthConfig = AuthConfig()
DRAMATIQ: DramatiqConfig = DramatiqConfig()
DEBUG: bool = False
ENVIRONMENT: TYPE_ENVIRONMENT = "local"
SQLALCHEMY_CONFIG: Dict[str, SQLAlchemyConfig] = dict(
default=SQLAlchemyConfig(
- engine_url=f"sqlite+aiosqlite:///{Path(__file__).parent.parent.joinpath('sqlite.db')}",
- engine_options=dict(
- connect_args={
- "check_same_thread": False,
- },
- echo=False,
- future=True,
- ),
+ engine_url="mysql+asyncmy://corinna:gioieiiere@127.0.0.1/backend?charset=utf8mb4",
async_engine=True,
),
)
diff --git a/src/http_app/__init__.py b/src/http_app/__init__.py
index effa63d..6782c91 100644
--- a/src/http_app/__init__.py
+++ b/src/http_app/__init__.py
@@ -1,11 +1,12 @@
import logging
-from typing import Union
from fastapi import FastAPI, Request
+from fastapi.middleware.cors import CORSMiddleware
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from starlette.responses import JSONResponse
from common import AppConfig, application_init
+from common.di_container import Container
from common.telemetry import instrument_third_party
from http_app import context
from http_app.routes import init_routes
@@ -16,7 +17,8 @@
def create_app(
- test_config: Union[AppConfig, None] = None,
+ test_config: AppConfig | None = None,
+ test_di_container: Container | None = None,
) -> FastAPI:
app_config = test_config or AppConfig()
@@ -29,13 +31,24 @@ def create_app(
"""
context.app_config.set(app_config)
- application_init(app_config)
+ ref = application_init(app_config, test_di_container)
+ ref.di_container.wire(packages=["http_app"])
+
app = FastAPI(
debug=app_config.DEBUG,
title=app_config.APP_NAME,
)
init_exception_handlers(app)
+ if app_config.CORS_ORIGINS:
+ app.add_middleware(
+ CORSMiddleware,
+ allow_origins=app_config.CORS_ORIGINS,
+ allow_credentials=True,
+ allow_methods=app_config.CORS_METHODS,
+ allow_headers=app_config.CORS_HEADERS,
+ )
+
init_routes(app)
FastAPIInstrumentor.instrument_app(app)
diff --git a/src/migrations/env.py b/src/migrations/env.py
index a6f8737..cd38a16 100644
--- a/src/migrations/env.py
+++ b/src/migrations/env.py
@@ -47,11 +47,11 @@ def generate_fixture_migration_model(declarative_base: type):
class FixtureMigration(declarative_base):
__tablename__ = "alembic_fixtures"
- bind: Mapped[str] = mapped_column(String(), primary_key=True)
- module_name: Mapped[str] = mapped_column(String(), primary_key=True)
- signature: Mapped[str] = mapped_column(String(), nullable=False)
+ bind: Mapped[str] = mapped_column(String(255), primary_key=True)
+ module_name: Mapped[str] = mapped_column(String(255), primary_key=True)
+ signature: Mapped[str] = mapped_column(String(255), nullable=False)
alembic_head_revisions: Mapped[str] = mapped_column(
- String(), nullable=True, default=str(context.get_head_revision())
+ String(255), nullable=False, default=str(context.get_head_revision())
)
processed_at: Mapped[datetime] = mapped_column(DateTime(), nullable=False, default=datetime.now)
@@ -327,6 +327,7 @@ def run_migrations_offline() -> None:
target_metadata=target_metadata.get(name),
literal_binds=True,
dialect_opts={"paramstyle": "named"},
+ render_as_batch=True,
)
with context.begin_transaction():
context.run_migrations(engine_name=name)
@@ -338,6 +339,7 @@ def do_run_migration(conn, name):
upgrade_token=f"{name}_upgrades",
downgrade_token=f"{name}_downgrades",
target_metadata=target_metadata.get(name),
+ render_as_batch=True,
)
context.run_migrations(engine_name=name)
diff --git a/src/migrations/versions/2025-01-26-212326-52b1246eda46_initialize_fixture_tables.py b/src/migrations/versions/2025-01-26-212326-52b1246eda46_initialize_fixture_tables.py
index 1a83c84..4701900 100644
--- a/src/migrations/versions/2025-01-26-212326-52b1246eda46_initialize_fixture_tables.py
+++ b/src/migrations/versions/2025-01-26-212326-52b1246eda46_initialize_fixture_tables.py
@@ -27,10 +27,10 @@ def downgrade(engine_name: str) -> None:
def upgrade_default() -> None:
op.create_table(
"alembic_fixtures",
- sa.Column("bind", sa.String(), nullable=False),
- sa.Column("module_name", sa.String(), nullable=False),
- sa.Column("signature", sa.String(), nullable=False),
- sa.Column("alembic_head_revisions", sa.String(), nullable=False),
+ sa.Column("bind", sa.String(length=255), nullable=False),
+ sa.Column("module_name", sa.String(length=255), nullable=False),
+ sa.Column("signature", sa.String(length=255), nullable=False),
+ sa.Column("alembic_head_revisions", sa.String(length=255), nullable=False),
sa.Column("processed_at", sa.DateTime(timezone=True), nullable=False),
sa.PrimaryKeyConstraint("bind", "module_name"),
)
diff --git a/src/socketio_app/__init__.py b/src/socketio_app/__init__.py
index 01e8756..16f394d 100644
--- a/src/socketio_app/__init__.py
+++ b/src/socketio_app/__init__.py
@@ -4,6 +4,7 @@
from starlette.routing import Mount, Route, Router
from common import AppConfig, application_init
+from common.di_container import Container
from common.telemetry import instrument_third_party
from socketio_app.namespaces.chat import ChatNamespace
from socketio_app.web_routes import docs
@@ -15,9 +16,11 @@
def create_app(
test_config: Union[AppConfig, None] = None,
+ test_di_container: Union[Container, None] = None,
) -> Router:
_config = test_config or AppConfig()
- application_init(_config)
+ ref = application_init(_config, test_di_container)
+ ref.di_container.wire(packages=["socketio_app"])
# SocketIO App
sio = socketio.AsyncServer(async_mode="asgi")
diff --git a/tests/conftest.py b/tests/conftest.py
index d3c3e28..6917d0c 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -14,7 +14,4 @@ def anyio_backend():
@pytest.fixture(scope="session")
def test_config() -> AppConfig:
- return AppConfig(
- SQLALCHEMY_CONFIG={},
- ENVIRONMENT="test",
- )
+ return AppConfig(SQLALCHEMY_CONFIG={}, ENVIRONMENT="test", CORS_ORIGINS=["*"])
diff --git a/tests/http_app/conftest.py b/tests/http_app/conftest.py
index 19c220a..5ff86e3 100644
--- a/tests/http_app/conftest.py
+++ b/tests/http_app/conftest.py
@@ -2,14 +2,23 @@
from unittest.mock import patch
import pytest
+from dependency_injector.providers import Object
from fastapi import FastAPI
+from common.di_container import Container
from http_app import create_app
@pytest.fixture(scope="session")
-def testapp(test_config) -> Iterator[FastAPI]:
+def test_di_container(test_config) -> Container:
+ return Container(
+ config=Object(test_config),
+ )
+
+
+@pytest.fixture(scope="session")
+def testapp(test_config, test_di_container) -> Iterator[FastAPI]:
# We don't need the storage to test the HTTP app
with patch("common.bootstrap.init_storage", return_value=None):
- app = create_app(test_config=test_config)
+ app = create_app(test_config=test_config, test_di_container=test_di_container)
yield app
diff --git a/tests/socketio_app/conftest.py b/tests/socketio_app/conftest.py
new file mode 100644
index 0000000..36ed2f1
--- /dev/null
+++ b/tests/socketio_app/conftest.py
@@ -0,0 +1,24 @@
+from collections.abc import Iterator
+from unittest.mock import patch
+
+import pytest
+from dependency_injector.providers import Object
+from starlette.routing import Router
+
+from common.di_container import Container
+from socketio_app import create_app
+
+
+@pytest.fixture(scope="session")
+def test_di_container(test_config) -> Container:
+ return Container(
+ config=Object(test_config),
+ )
+
+
+@pytest.fixture(scope="session")
+def testapp(test_config, test_di_container) -> Iterator[Router]:
+ # We don't need the storage to test the HTTP app
+ with patch("common.bootstrap.init_storage", return_value=None):
+ app = create_app(test_config=test_config, test_di_container=test_di_container)
+ yield app
diff --git a/uv.lock b/uv.lock
index aee229e..2cd9b69 100644
--- a/uv.lock
+++ b/uv.lock
@@ -73,6 +73,43 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233 },
]
+[[package]]
+name = "asyncmy"
+version = "0.2.10"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b5/76/55cc0577f9e838c5a5213bf33159b9e484c9d9820a2bafd4d6bfa631bf86/asyncmy-0.2.10.tar.gz", hash = "sha256:f4b67edadf7caa56bdaf1c2e6cf451150c0a86f5353744deabe4426fe27aff4e", size = 63889 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/78/c9/412b137c52f6c6437faba27412ccb32721571c42e59bc4f799796316866b/asyncmy-0.2.10-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:c2237c8756b8f374099bd320c53b16f7ec0cee8258f00d72eed5a2cd3d251066", size = 1803880 },
+ { url = "https://files.pythonhosted.org/packages/74/f3/c9520f489dc42a594c8ad3cbe2088ec511245a3c55c3333e6fa949838420/asyncmy-0.2.10-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:6e98d4fbf7ea0d99dfecb24968c9c350b019397ba1af9f181d51bb0f6f81919b", size = 1736363 },
+ { url = "https://files.pythonhosted.org/packages/52/9c/3c531a414290cbde9313cad54bb525caf6b1055ffa56bb271bf70512b533/asyncmy-0.2.10-cp310-cp310-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:b1b1ee03556c7eda6422afc3aca132982a84706f8abf30f880d642f50670c7ed", size = 4970043 },
+ { url = "https://files.pythonhosted.org/packages/03/64/176ed8a79d3a24b2e8ba7a11b429553f29fea20276537651526f3a87660b/asyncmy-0.2.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e2b97672ea3f0b335c0ffd3da1a5727b530f82f5032cd87e86c3aa3ac6df7f3", size = 5168645 },
+ { url = "https://files.pythonhosted.org/packages/81/3f/46f126663649784ab6586bc9b482bca432a35588714170621db8d33d76e4/asyncmy-0.2.10-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c6471ce1f9ae1e6f0d55adfb57c49d0bcf5753a253cccbd33799ddb402fe7da2", size = 4988493 },
+ { url = "https://files.pythonhosted.org/packages/5f/c6/acce7ea4b74e092582d65744418940b2b8c661102a22a638f58e7b651c6f/asyncmy-0.2.10-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:10e2a10fe44a2b216a1ae58fbdafa3fed661a625ec3c030c560c26f6ab618522", size = 5158496 },
+ { url = "https://files.pythonhosted.org/packages/d5/01/d8fa0291083e9a0d899addda1f7608da37d28fff9bb4df1bd6f7f37354db/asyncmy-0.2.10-cp310-cp310-win32.whl", hash = "sha256:a791ab117787eb075bc37ed02caa7f3e30cca10f1b09ec7eeb51d733df1d49fc", size = 1624372 },
+ { url = "https://files.pythonhosted.org/packages/cf/a0/ad6669fd2870492749c189a72c881716a3727b7f0bc972fc8cea7a40879c/asyncmy-0.2.10-cp310-cp310-win_amd64.whl", hash = "sha256:bd16fdc0964a4a1a19aec9797ca631c3ff2530013fdcd27225fc2e48af592804", size = 1694174 },
+ { url = "https://files.pythonhosted.org/packages/72/1a/21b4af0d19862cc991f1095f006981a4f898599060dfa59f136e292b3e9a/asyncmy-0.2.10-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:7af0f1f31f800a8789620c195e92f36cce4def68ee70d625534544d43044ed2a", size = 1806974 },
+ { url = "https://files.pythonhosted.org/packages/1d/ce/3579a88123ead38e60e0b6e744224907e3d7a668518f9a46ed584df4f788/asyncmy-0.2.10-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:800116ab85dc53b24f484fb644fefffac56db7367a31e7d62f4097d495105a2c", size = 1738218 },
+ { url = "https://files.pythonhosted.org/packages/e2/39/10646bbafce22025be25aa709e83f0cdd3fb9089304cf9d3169a80540850/asyncmy-0.2.10-cp311-cp311-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:39525e9d7e557b83db268ed14b149a13530e0d09a536943dba561a8a1c94cc07", size = 5346417 },
+ { url = "https://files.pythonhosted.org/packages/8f/f8/3fb0d0481def3a0900778f7d04f50028a4a2d987087a2f1e718e6c236e01/asyncmy-0.2.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76e199d6b57918999efc702d2dbb182cb7ba8c604cdfc912517955219b16eaea", size = 5553197 },
+ { url = "https://files.pythonhosted.org/packages/82/a5/8281e8c0999fc6303b5b522ee82d1e338157a74f8bbbaa020e392b69156a/asyncmy-0.2.10-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9ca8fdd7dbbf2d9b4c2d3a5fac42b058707d6a483b71fded29051b8ae198a250", size = 5337915 },
+ { url = "https://files.pythonhosted.org/packages/fe/f4/425108f5c6976ceb67b8f95bc73480fe777a95e7a89a29299664f5cb380f/asyncmy-0.2.10-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0df23db54e38602c803dacf1bbc1dcc4237a87223e659681f00d1a319a4f3826", size = 5524662 },
+ { url = "https://files.pythonhosted.org/packages/ff/32/17291b12dce380abbbec888ea9d4e863fd2116530bf2c87c1ab40b39f9d1/asyncmy-0.2.10-cp311-cp311-win32.whl", hash = "sha256:a16633032be020b931acfd7cd1862c7dad42a96ea0b9b28786f2ec48e0a86757", size = 1622375 },
+ { url = "https://files.pythonhosted.org/packages/e2/a3/76e65877de5e6fc853373908079adb711f80ed09aab4e152a533e0322375/asyncmy-0.2.10-cp311-cp311-win_amd64.whl", hash = "sha256:cca06212575922216b89218abd86a75f8f7375fc9c28159ea469f860785cdbc7", size = 1696693 },
+ { url = "https://files.pythonhosted.org/packages/b8/82/5a4b1aedae9b35f7885f10568437d80507d7a6704b51da2fc960a20c4948/asyncmy-0.2.10-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:42295530c5f36784031f7fa42235ef8dd93a75d9b66904de087e68ff704b4f03", size = 1783558 },
+ { url = "https://files.pythonhosted.org/packages/39/24/0fce480680531a29b51e1d2680a540c597e1a113aa1dc58cb7483c123a6b/asyncmy-0.2.10-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:641a853ffcec762905cbeceeb623839c9149b854d5c3716eb9a22c2b505802af", size = 1729268 },
+ { url = "https://files.pythonhosted.org/packages/c8/96/74dc1aaf1ab0bde88d3c6b3a70bd25f18796adb4e91b77ad580efe232df5/asyncmy-0.2.10-cp312-cp312-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:c554874223dd36b1cfc15e2cd0090792ea3832798e8fe9e9d167557e9cf31b4d", size = 5343513 },
+ { url = "https://files.pythonhosted.org/packages/9a/04/14662ff5b9cfab5cc11dcf91f2316e2f80d88fbd2156e458deef3e72512a/asyncmy-0.2.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd16e84391dde8edb40c57d7db634706cbbafb75e6a01dc8b68a63f8dd9e44ca", size = 5592344 },
+ { url = "https://files.pythonhosted.org/packages/7c/ac/3cf0abb3acd4f469bd012a1b4a01968bac07a142fca510da946b6ab1bf4f/asyncmy-0.2.10-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9f6b44c4bf4bb69a2a1d9d26dee302473099105ba95283b479458c448943ed3c", size = 5300819 },
+ { url = "https://files.pythonhosted.org/packages/5c/23/6d05254d1c89ad15e7f32eb3df277afc7bbb2220faa83a76bea0b7bc6407/asyncmy-0.2.10-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:16d398b1aad0550c6fe1655b6758455e3554125af8aaf1f5abdc1546078c7257", size = 5548799 },
+ { url = "https://files.pythonhosted.org/packages/fe/32/b7ce9782c741b6a821a0d11772f180f431a5c3ba6eaf2e6dfa1c3cbcf4df/asyncmy-0.2.10-cp312-cp312-win32.whl", hash = "sha256:59d2639dcc23939ae82b93b40a683c15a091460a3f77fa6aef1854c0a0af99cc", size = 1597544 },
+ { url = "https://files.pythonhosted.org/packages/94/08/7de4f4a17196c355e4706ceba0ab60627541c78011881a7c69f41c6414c5/asyncmy-0.2.10-cp312-cp312-win_amd64.whl", hash = "sha256:4c6674073be97ffb7ac7f909e803008b23e50281131fef4e30b7b2162141a574", size = 1679064 },
+ { url = "https://files.pythonhosted.org/packages/83/32/3317d5290737a3c4685343fe37e02567518357c46ed87c51f47139d31ded/asyncmy-0.2.10-pp310-pypy310_pp73-macosx_13_0_x86_64.whl", hash = "sha256:f10c977c60a95bd6ec6b8654e20c8f53bad566911562a7ad7117ca94618f05d3", size = 1627680 },
+ { url = "https://files.pythonhosted.org/packages/e9/e1/afeb50deb0554006c48b9f4f7b6b726e0aa42fa96d7cfbd3fdd0800765e2/asyncmy-0.2.10-pp310-pypy310_pp73-macosx_14_0_arm64.whl", hash = "sha256:aab07fbdb9466beaffef136ffabe388f0d295d8d2adb8f62c272f1d4076515b9", size = 1593957 },
+ { url = "https://files.pythonhosted.org/packages/be/c1/56d3721e2b2eab84320058c3458da168d143446031eca3799aed481c33d2/asyncmy-0.2.10-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:63144322ade68262201baae73ad0c8a06b98a3c6ae39d1f3f21c41cc5287066a", size = 1756531 },
+ { url = "https://files.pythonhosted.org/packages/ac/1a/295f06eb8e5926749265e08da9e2dc0dc14e0244bf36843997a1c8e18a50/asyncmy-0.2.10-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9659d95c6f2a611aec15bdd928950df937bf68bc4bbb68b809ee8924b6756067", size = 1752746 },
+ { url = "https://files.pythonhosted.org/packages/ab/09/3a5351acc6273c28333cad8193184de0070c617fd8385fd8ba23d789e08d/asyncmy-0.2.10-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8ced4bd938e95ede0fb9fa54755773df47bdb9f29f142512501e613dd95cf4a4", size = 1614903 },
+]
+
[[package]]
name = "asynctest"
version = "0.13.0"
@@ -120,6 +157,7 @@ source = { editable = "." }
dependencies = [
{ name = "aiosqlite" },
{ name = "asgiref" },
+ { name = "asyncmy" },
{ name = "cloudevents-pydantic" },
{ name = "dependency-injector", extra = ["pydantic"] },
{ name = "dramatiq", extra = ["redis", "watch"] },
@@ -134,7 +172,6 @@ dependencies = [
{ name = "pydantic" },
{ name = "pydantic-asyncapi" },
{ name = "pydantic-settings" },
- { name = "rich" },
{ name = "sqlalchemy", extra = ["asyncio", "mypy"] },
{ name = "sqlalchemy-bind-manager" },
{ name = "structlog" },
@@ -182,6 +219,7 @@ socketio = [
requires-dist = [
{ name = "aiosqlite", specifier = ">=0.18.0" },
{ name = "asgiref", specifier = ">=3.7.2,<4.0.0" },
+ { name = "asyncmy", specifier = ">=0.2.10" },
{ name = "cloudevents-pydantic", specifier = ">=0.0.3,<1.0.0" },
{ name = "dependency-injector", extras = ["pydantic"], specifier = ">=4.41.0,<5.0.0" },
{ name = "dramatiq", extras = ["redis", "watch"], specifier = ">=1.17.1,<2.0.0" },
@@ -196,7 +234,6 @@ requires-dist = [
{ name = "pydantic", specifier = ">=2.2.1,<3.0.0" },
{ name = "pydantic-asyncapi", specifier = ">=0.2.1" },
{ name = "pydantic-settings", specifier = ">=2.0.3,<3.0.0" },
- { name = "rich", specifier = ">=13.2.0,<14.0.0" },
{ name = "sqlalchemy", extras = ["asyncio", "mypy"], specifier = ">=2.0.0,<3.0.0" },
{ name = "sqlalchemy-bind-manager" },
{ name = "structlog", specifier = ">=25.1.0,<25.1.1" },
@@ -754,14 +791,14 @@ wheels = [
[[package]]
name = "googleapis-common-protos"
-version = "1.67.0"
+version = "1.68.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "protobuf" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/31/e1/fbffb85a624f1404133b5bb624834e77e0f549e2b8548146fe18c56e1411/googleapis_common_protos-1.67.0.tar.gz", hash = "sha256:21398025365f138be356d5923e9168737d94d46a72aefee4a6110a1f23463c86", size = 57344 }
+sdist = { url = "https://files.pythonhosted.org/packages/54/d2/c08f0d9f94b45faca68e355771329cba2411c777c8713924dd1baee0e09c/googleapis_common_protos-1.68.0.tar.gz", hash = "sha256:95d38161f4f9af0d9423eed8fb7b64ffd2568c3464eb542ff02c5bfa1953ab3c", size = 57367 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/89/30/2bd0eb03a7dee7727cd2ec643d1e992979e62d5e7443507381cce0455132/googleapis_common_protos-1.67.0-py2.py3-none-any.whl", hash = "sha256:579de760800d13616f51cf8be00c876f00a9f146d3e6510e19d1f4111758b741", size = 164985 },
+ { url = "https://files.pythonhosted.org/packages/3f/85/c99a157ee99d67cc6c9ad123abb8b1bfb476fab32d2f3511c59314548e4f/googleapis_common_protos-1.68.0-py2.py3-none-any.whl", hash = "sha256:aaf179b2f81df26dfadac95def3b16a95064c76a5f45f07e4c68a21bb371c4ac", size = 164985 },
]
[[package]]
@@ -1238,14 +1275,11 @@ wheels = [
[[package]]
name = "mistune"
-version = "3.1.1"
+version = "3.0.2"
source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "typing-extensions", marker = "python_full_version < '3.11'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/c6/1d/6b2b634e43bacc3239006e61800676aa6c41ac1836b2c57497ed27a7310b/mistune-3.1.1.tar.gz", hash = "sha256:e0740d635f515119f7d1feb6f9b192ee60f0cc649f80a8f944f905706a21654c", size = 94645 }
+sdist = { url = "https://files.pythonhosted.org/packages/ef/c8/f0173fe3bf85fd891aee2e7bcd8207dfe26c2c683d727c5a6cc3aec7b628/mistune-3.0.2.tar.gz", hash = "sha256:fc7f93ded930c92394ef2cb6f04a8aabab4117a91449e72dcc8dfa646a508be8", size = 90840 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c6/02/c66bdfdadbb021adb642ca4e8a5ed32ada0b4a3e4b39c5d076d19543452f/mistune-3.1.1-py3-none-any.whl", hash = "sha256:02106ac2aa4f66e769debbfa028509a275069dcffce0dfa578edd7b991ee700a", size = 53696 },
+ { url = "https://files.pythonhosted.org/packages/f0/74/c95adcdf032956d9ef6c89a9b8a5152bf73915f8c633f3e3d88d06bd699c/mistune-3.0.2-py3-none-any.whl", hash = "sha256:71481854c30fdbc938963d3605b72501f5c10a9320ecd412c121c163a1c7d205", size = 47958 },
]
[[package]]
@@ -1302,7 +1336,7 @@ wheels = [
[[package]]
name = "mkdocs-macros-adr-summary"
-version = "1.1.0"
+version = "1.1.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mistune" },
@@ -1310,9 +1344,9 @@ dependencies = [
{ name = "mkdocs-macros-plugin" },
{ name = "pyyaml" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/24/5d/d99b3653962ab5e0680faf5e739343e9eb83eb06b40922533ecbe0b57074/mkdocs_macros_adr_summary-1.1.0.tar.gz", hash = "sha256:6f2fb2c6f5085aa03d1b34206f35935641ff680fa3c5ede269477c5cd1602396", size = 10638 }
+sdist = { url = "https://files.pythonhosted.org/packages/d5/2d/e8bcf15323ab2d2189b15b9d8eef21a58209ad55d40860874b06e09af928/mkdocs_macros_adr_summary-1.1.1.tar.gz", hash = "sha256:411edda6084318abf1c784dad7efb268647bd1cad53b82999064b992d601e2d1", size = 10250 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/60/c5/40511f5749ac97bfaa0188375f1b751008cb854bca6ddcd97dd9348ef4c7/mkdocs_macros_adr_summary-1.1.0-py3-none-any.whl", hash = "sha256:4d2eb25f7e199222370d9bb9809be1feab2ab5379eb15d66d3772a257fd33ae8", size = 20234 },
+ { url = "https://files.pythonhosted.org/packages/e8/df/4c0f7a7baa3aa7ba2c6aa2fcc1bb6c356d0a46825d3f84d81795721c28e2/mkdocs_macros_adr_summary-1.1.1-py3-none-any.whl", hash = "sha256:3061e998c1e1fa10e4086e68fe3e9ae6f69e07f8ac57337b6df1e0454bbf0b23", size = 20232 },
]
[[package]]
@@ -1337,7 +1371,7 @@ wheels = [
[[package]]
name = "mkdocs-material"
-version = "9.6.4"
+version = "9.6.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "babel" },
@@ -1352,9 +1386,9 @@ dependencies = [
{ name = "regex" },
{ name = "requests" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/9b/80/4efbd3df76c6c1ec27130b43662612f9033adc5a4166f1df2acb8dd6fb1b/mkdocs_material-9.6.4.tar.gz", hash = "sha256:4d1d35e1c1d3e15294cb7fa5d02e0abaee70d408f75027dc7be6e30fb32e6867", size = 3942628 }
+sdist = { url = "https://files.pythonhosted.org/packages/38/4d/0a9f6f604f01eaa43df3b3b30b5218548efd7341913b302815585f48abb2/mkdocs_material-9.6.5.tar.gz", hash = "sha256:b714679a8c91b0ffe2188e11ed58c44d2523e9c2ae26a29cc652fa7478faa21f", size = 3946479 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/5b/a5/f3c0e86c1d28fe04f1b724700ff3dd8b3647c89df03a8e10c4bc6b4db1b8/mkdocs_material-9.6.4-py3-none-any.whl", hash = "sha256:414e8376551def6d644b8e6f77226022868532a792eb2c9accf52199009f568f", size = 8688727 },
+ { url = "https://files.pythonhosted.org/packages/3d/05/7d440b23454c0fc8cdba21f73ce23369eb16e7f7ee475fac3a4ad15ad5e0/mkdocs_material-9.6.5-py3-none-any.whl", hash = "sha256:aad3e6fb860c20870f75fb2a69ef901f1be727891e41adb60b753efcae19453b", size = 8695060 },
]
[[package]]
@@ -1908,15 +1942,15 @@ wheels = [
[[package]]
name = "pydantic-settings"
-version = "2.7.1"
+version = "2.8.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pydantic" },
{ name = "python-dotenv" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/73/7b/c58a586cd7d9ac66d2ee4ba60ca2d241fa837c02bca9bea80a9a8c3d22a9/pydantic_settings-2.7.1.tar.gz", hash = "sha256:10c9caad35e64bfb3c2fbf70a078c0e25cc92499782e5200747f942a065dec93", size = 79920 }
+sdist = { url = "https://files.pythonhosted.org/packages/ca/a2/ad2511ede77bb424f3939e5148a56d968cdc6b1462620d24b2a1f4ab65b4/pydantic_settings-2.8.0.tar.gz", hash = "sha256:88e2ca28f6e68ea102c99c3c401d6c9078e68a5df600e97b43891c34e089500a", size = 83347 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/b4/46/93416fdae86d40879714f72956ac14df9c7b76f7d41a4d68aa9f71a0028b/pydantic_settings-2.7.1-py3-none-any.whl", hash = "sha256:590be9e6e24d06db33a4262829edef682500ef008565a969c73d39d5f8bfb3fd", size = 29718 },
+ { url = "https://files.pythonhosted.org/packages/c1/a9/3b9642025174bbe67e900785fb99c9bfe91ea584b0b7126ff99945c24a0e/pydantic_settings-2.8.0-py3-none-any.whl", hash = "sha256:c782c7dc3fb40e97b238e713c25d26f64314aece2e91abcff592fcac15f71820", size = 30746 },
]
[[package]]
@@ -2241,27 +2275,27 @@ wheels = [
[[package]]
name = "ruff"
-version = "0.9.6"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/2a/e1/e265aba384343dd8ddd3083f5e33536cd17e1566c41453a5517b5dd443be/ruff-0.9.6.tar.gz", hash = "sha256:81761592f72b620ec8fa1068a6fd00e98a5ebee342a3642efd84454f3031dca9", size = 3639454 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/76/e3/3d2c022e687e18cf5d93d6bfa2722d46afc64eaa438c7fbbdd603b3597be/ruff-0.9.6-py3-none-linux_armv6l.whl", hash = "sha256:2f218f356dd2d995839f1941322ff021c72a492c470f0b26a34f844c29cdf5ba", size = 11714128 },
- { url = "https://files.pythonhosted.org/packages/e1/22/aff073b70f95c052e5c58153cba735748c9e70107a77d03420d7850710a0/ruff-0.9.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b908ff4df65dad7b251c9968a2e4560836d8f5487c2f0cc238321ed951ea0504", size = 11682539 },
- { url = "https://files.pythonhosted.org/packages/75/a7/f5b7390afd98a7918582a3d256cd3e78ba0a26165a467c1820084587cbf9/ruff-0.9.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b109c0ad2ececf42e75fa99dc4043ff72a357436bb171900714a9ea581ddef83", size = 11132512 },
- { url = "https://files.pythonhosted.org/packages/a6/e3/45de13ef65047fea2e33f7e573d848206e15c715e5cd56095589a7733d04/ruff-0.9.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1de4367cca3dac99bcbd15c161404e849bb0bfd543664db39232648dc00112dc", size = 11929275 },
- { url = "https://files.pythonhosted.org/packages/7d/f2/23d04cd6c43b2e641ab961ade8d0b5edb212ecebd112506188c91f2a6e6c/ruff-0.9.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac3ee4d7c2c92ddfdaedf0bf31b2b176fa7aa8950efc454628d477394d35638b", size = 11466502 },
- { url = "https://files.pythonhosted.org/packages/b5/6f/3a8cf166f2d7f1627dd2201e6cbc4cb81f8b7d58099348f0c1ff7b733792/ruff-0.9.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dc1edd1775270e6aa2386119aea692039781429f0be1e0949ea5884e011aa8e", size = 12676364 },
- { url = "https://files.pythonhosted.org/packages/f5/c4/db52e2189983c70114ff2b7e3997e48c8318af44fe83e1ce9517570a50c6/ruff-0.9.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4a091729086dffa4bd070aa5dab7e39cc6b9d62eb2bef8f3d91172d30d599666", size = 13335518 },
- { url = "https://files.pythonhosted.org/packages/66/44/545f8a4d136830f08f4d24324e7db957c5374bf3a3f7a6c0bc7be4623a37/ruff-0.9.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1bbc6808bf7b15796cef0815e1dfb796fbd383e7dbd4334709642649625e7c5", size = 12823287 },
- { url = "https://files.pythonhosted.org/packages/c5/26/8208ef9ee7431032c143649a9967c3ae1aae4257d95e6f8519f07309aa66/ruff-0.9.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:589d1d9f25b5754ff230dce914a174a7c951a85a4e9270613a2b74231fdac2f5", size = 14592374 },
- { url = "https://files.pythonhosted.org/packages/31/70/e917781e55ff39c5b5208bda384fd397ffd76605e68544d71a7e40944945/ruff-0.9.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc61dd5131742e21103fbbdcad683a8813be0e3c204472d520d9a5021ca8b217", size = 12500173 },
- { url = "https://files.pythonhosted.org/packages/84/f5/e4ddee07660f5a9622a9c2b639afd8f3104988dc4f6ba0b73ffacffa9a8c/ruff-0.9.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5e2d9126161d0357e5c8f30b0bd6168d2c3872372f14481136d13de9937f79b6", size = 11906555 },
- { url = "https://files.pythonhosted.org/packages/f1/2b/6ff2fe383667075eef8656b9892e73dd9b119b5e3add51298628b87f6429/ruff-0.9.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:68660eab1a8e65babb5229a1f97b46e3120923757a68b5413d8561f8a85d4897", size = 11538958 },
- { url = "https://files.pythonhosted.org/packages/3c/db/98e59e90de45d1eb46649151c10a062d5707b5b7f76f64eb1e29edf6ebb1/ruff-0.9.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c4cae6c4cc7b9b4017c71114115db0445b00a16de3bcde0946273e8392856f08", size = 12117247 },
- { url = "https://files.pythonhosted.org/packages/ec/bc/54e38f6d219013a9204a5a2015c09e7a8c36cedcd50a4b01ac69a550b9d9/ruff-0.9.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:19f505b643228b417c1111a2a536424ddde0db4ef9023b9e04a46ed8a1cb4656", size = 12554647 },
- { url = "https://files.pythonhosted.org/packages/a5/7d/7b461ab0e2404293c0627125bb70ac642c2e8d55bf590f6fce85f508f1b2/ruff-0.9.6-py3-none-win32.whl", hash = "sha256:194d8402bceef1b31164909540a597e0d913c0e4952015a5b40e28c146121b5d", size = 9949214 },
- { url = "https://files.pythonhosted.org/packages/ee/30/c3cee10f915ed75a5c29c1e57311282d1a15855551a64795c1b2bbe5cf37/ruff-0.9.6-py3-none-win_amd64.whl", hash = "sha256:03482d5c09d90d4ee3f40d97578423698ad895c87314c4de39ed2af945633caa", size = 10999914 },
- { url = "https://files.pythonhosted.org/packages/e8/a8/d71f44b93e3aa86ae232af1f2126ca7b95c0f515ec135462b3e1f351441c/ruff-0.9.6-py3-none-win_arm64.whl", hash = "sha256:0e2bb706a2be7ddfea4a4af918562fdc1bcb16df255e5fa595bbd800ce322a5a", size = 10177499 },
+version = "0.9.7"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/39/8b/a86c300359861b186f18359adf4437ac8e4c52e42daa9eedc731ef9d5b53/ruff-0.9.7.tar.gz", hash = "sha256:643757633417907510157b206e490c3aa11cab0c087c912f60e07fbafa87a4c6", size = 3669813 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b1/f3/3a1d22973291226df4b4e2ff70196b926b6f910c488479adb0eeb42a0d7f/ruff-0.9.7-py3-none-linux_armv6l.whl", hash = "sha256:99d50def47305fe6f233eb8dabfd60047578ca87c9dcb235c9723ab1175180f4", size = 11774588 },
+ { url = "https://files.pythonhosted.org/packages/8e/c9/b881f4157b9b884f2994fd08ee92ae3663fb24e34b0372ac3af999aa7fc6/ruff-0.9.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d59105ae9c44152c3d40a9c40d6331a7acd1cdf5ef404fbe31178a77b174ea66", size = 11746848 },
+ { url = "https://files.pythonhosted.org/packages/14/89/2f546c133f73886ed50a3d449e6bf4af27d92d2f960a43a93d89353f0945/ruff-0.9.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f313b5800483770bd540cddac7c90fc46f895f427b7820f18fe1822697f1fec9", size = 11177525 },
+ { url = "https://files.pythonhosted.org/packages/d7/93/6b98f2c12bf28ab9def59c50c9c49508519c5b5cfecca6de871cf01237f6/ruff-0.9.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:042ae32b41343888f59c0a4148f103208bf6b21c90118d51dc93a68366f4e903", size = 11996580 },
+ { url = "https://files.pythonhosted.org/packages/8e/3f/b3fcaf4f6d875e679ac2b71a72f6691a8128ea3cb7be07cbb249f477c061/ruff-0.9.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87862589373b33cc484b10831004e5e5ec47dc10d2b41ba770e837d4f429d721", size = 11525674 },
+ { url = "https://files.pythonhosted.org/packages/f0/48/33fbf18defb74d624535d5d22adcb09a64c9bbabfa755bc666189a6b2210/ruff-0.9.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a17e1e01bee0926d351a1ee9bc15c445beae888f90069a6192a07a84af544b6b", size = 12739151 },
+ { url = "https://files.pythonhosted.org/packages/63/b5/7e161080c5e19fa69495cbab7c00975ef8a90f3679caa6164921d7f52f4a/ruff-0.9.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7c1f880ac5b2cbebd58b8ebde57069a374865c73f3bf41f05fe7a179c1c8ef22", size = 13416128 },
+ { url = "https://files.pythonhosted.org/packages/4e/c8/b5e7d61fb1c1b26f271ac301ff6d9de5e4d9a9a63f67d732fa8f200f0c88/ruff-0.9.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e63fc20143c291cab2841dbb8260e96bafbe1ba13fd3d60d28be2c71e312da49", size = 12870858 },
+ { url = "https://files.pythonhosted.org/packages/da/cb/2a1a8e4e291a54d28259f8fc6a674cd5b8833e93852c7ef5de436d6ed729/ruff-0.9.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91ff963baed3e9a6a4eba2a02f4ca8eaa6eba1cc0521aec0987da8d62f53cbef", size = 14786046 },
+ { url = "https://files.pythonhosted.org/packages/ca/6c/c8f8a313be1943f333f376d79724260da5701426c0905762e3ddb389e3f4/ruff-0.9.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88362e3227c82f63eaebf0b2eff5b88990280fb1ecf7105523883ba8c3aaf6fb", size = 12550834 },
+ { url = "https://files.pythonhosted.org/packages/9d/ad/f70cf5e8e7c52a25e166bdc84c082163c9c6f82a073f654c321b4dff9660/ruff-0.9.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0372c5a90349f00212270421fe91874b866fd3626eb3b397ede06cd385f6f7e0", size = 11961307 },
+ { url = "https://files.pythonhosted.org/packages/52/d5/4f303ea94a5f4f454daf4d02671b1fbfe2a318b5fcd009f957466f936c50/ruff-0.9.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d76b8ab60e99e6424cd9d3d923274a1324aefce04f8ea537136b8398bbae0a62", size = 11612039 },
+ { url = "https://files.pythonhosted.org/packages/eb/c8/bd12a23a75603c704ce86723be0648ba3d4ecc2af07eecd2e9fa112f7e19/ruff-0.9.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0c439bdfc8983e1336577f00e09a4e7a78944fe01e4ea7fe616d00c3ec69a3d0", size = 12168177 },
+ { url = "https://files.pythonhosted.org/packages/cc/57/d648d4f73400fef047d62d464d1a14591f2e6b3d4a15e93e23a53c20705d/ruff-0.9.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:115d1f15e8fdd445a7b4dc9a30abae22de3f6bcabeb503964904471691ef7606", size = 12610122 },
+ { url = "https://files.pythonhosted.org/packages/49/79/acbc1edd03ac0e2a04ae2593555dbc9990b34090a9729a0c4c0cf20fb595/ruff-0.9.7-py3-none-win32.whl", hash = "sha256:e9ece95b7de5923cbf38893f066ed2872be2f2f477ba94f826c8defdd6ec6b7d", size = 9988751 },
+ { url = "https://files.pythonhosted.org/packages/6d/95/67153a838c6b6ba7a2401241fd8a00cd8c627a8e4a0491b8d853dedeffe0/ruff-0.9.7-py3-none-win_amd64.whl", hash = "sha256:3770fe52b9d691a15f0b87ada29c45324b2ace8f01200fb0c14845e499eb0c2c", size = 11002987 },
+ { url = "https://files.pythonhosted.org/packages/63/6a/aca01554949f3a401991dc32fe22837baeaccb8a0d868256cbb26a029778/ruff-0.9.7-py3-none-win_arm64.whl", hash = "sha256:b075a700b2533feb7a01130ff656a4ec0d5f340bb540ad98759b8401c32c2037", size = 10177763 },
]
[[package]]