From bf692cd26a8d62b000e9c43c596f1410f5c9b72f Mon Sep 17 00:00:00 2001 From: Atticuszz <1831768457@qq.com> Date: Thu, 7 Nov 2024 21:16:11 +0800 Subject: [PATCH] chore: store --- .vscode/PythonImportHelper-v2-Completion.json | 180 ++++++------------ README.md | 130 ++----------- pyproject.toml | 1 + src/app/core/config.py | 43 +++-- src/app/core/db.py | 0 tests/api/api_v1/test_items.py | 174 ++++++++--------- tests/conftest.py | 4 +- tests/crud/test_item.py | 100 +++++----- uv.lock | 111 +++++++++++ 9 files changed, 357 insertions(+), 386 deletions(-) create mode 100644 src/app/core/db.py diff --git a/.vscode/PythonImportHelper-v2-Completion.json b/.vscode/PythonImportHelper-v2-Completion.json index ec2c26f..6366f3b 100644 --- a/.vscode/PythonImportHelper-v2-Completion.json +++ b/.vscode/PythonImportHelper-v2-Completion.json @@ -179,22 +179,6 @@ "detail": "app.schemas", "documentation": {} }, - { - "label": "Token", - "importPath": "app.schemas", - "description": "app.schemas", - "isExtraImport": true, - "detail": "app.schemas", - "documentation": {} - }, - { - "label": "Token", - "importPath": "app.schemas", - "description": "app.schemas", - "isExtraImport": true, - "detail": "app.schemas", - "documentation": {} - }, { "label": "items", "importPath": "app.api.api_v1.endpoints", @@ -388,22 +372,6 @@ "detail": "supabase._async.client", "documentation": {} }, - { - "label": "AsyncClient", - "importPath": "supabase._async.client", - "description": "supabase._async.client", - "isExtraImport": true, - "detail": "supabase._async.client", - "documentation": {} - }, - { - "label": "AsyncClient", - "importPath": "supabase._async.client", - "description": "supabase._async.client", - "isExtraImport": true, - "detail": "supabase._async.client", - "documentation": {} - }, { "label": "create_client", "importPath": "supabase._async.client", @@ -460,6 +428,30 @@ "detail": "pydantic", "documentation": {} }, + { + "label": "HttpUrl", + "importPath": "pydantic", + "description": "pydantic", + "isExtraImport": true, + "detail": "pydantic", + "documentation": {} + }, + { + "label": "PostgresDsn", + "importPath": "pydantic", + "description": "pydantic", + "isExtraImport": true, + "detail": "pydantic", + "documentation": {} + }, + { + "label": "computed_field", + "importPath": "pydantic", + "description": "pydantic", + "isExtraImport": true, + "detail": "pydantic", + "documentation": {} + }, { "label": "BaseModel", "importPath": "pydantic", @@ -500,6 +492,14 @@ "detail": "pydantic", "documentation": {} }, + { + "label": "MultiHostUrl", + "importPath": "pydantic_core", + "description": "pydantic_core", + "isExtraImport": true, + "detail": "pydantic_core", + "documentation": {} + }, { "label": "BaseSettings", "importPath": "pydantic_settings", @@ -629,6 +629,22 @@ "detail": "app.core.events", "documentation": {} }, + { + "label": "RotatingFileHandler", + "importPath": "logging.handlers", + "description": "logging.handlers", + "isExtraImport": true, + "detail": "logging.handlers", + "documentation": {} + }, + { + "label": "Path", + "importPath": "pathlib", + "description": "pathlib", + "isExtraImport": true, + "detail": "pathlib", + "documentation": {} + }, { "label": "pytest", "kind": 6, @@ -639,19 +655,19 @@ "documentation": {} }, { - "label": "Faker", - "importPath": "faker", - "description": "faker", + "label": "app", + "importPath": "src.app.main", + "description": "src.app.main", "isExtraImport": true, - "detail": "faker", + "detail": "src.app.main", "documentation": {} }, { - "label": "Faker", - "importPath": "faker", - "description": "faker", + "label": "Token", + "importPath": "src.app.schemas", + "description": "src.app.schemas", "isExtraImport": true, - "detail": "faker", + "detail": "src.app.schemas", "documentation": {} }, { @@ -664,18 +680,10 @@ }, { "label": "TestClient", - "importPath": "starlette.testclient", - "description": "starlette.testclient", - "isExtraImport": true, - "detail": "starlette.testclient", - "documentation": {} - }, - { - "label": "get_auth_header", - "importPath": "tests.utils", - "description": "tests.utils", + "importPath": "fastapi.testclient", + "description": "fastapi.testclient", "isExtraImport": true, - "detail": "tests.utils", + "detail": "fastapi.testclient", "documentation": {} }, { @@ -686,70 +694,6 @@ "detail": "tests.utils", "documentation": {} }, - { - "label": "crud", - "importPath": "app", - "description": "app", - "isExtraImport": true, - "detail": "app", - "documentation": {} - }, - { - "label": "Item", - "importPath": "app.schemas.item", - "description": "app.schemas.item", - "isExtraImport": true, - "detail": "app.schemas.item", - "documentation": {} - }, - { - "label": "ItemCreate", - "importPath": "app.schemas.item", - "description": "app.schemas.item", - "isExtraImport": true, - "detail": "app.schemas.item", - "documentation": {} - }, - { - "label": "ItemUpdate", - "importPath": "app.schemas.item", - "description": "app.schemas.item", - "isExtraImport": true, - "detail": "app.schemas.item", - "documentation": {} - }, - { - "label": "RotatingFileHandler", - "importPath": "logging.handlers", - "description": "logging.handlers", - "isExtraImport": true, - "detail": "logging.handlers", - "documentation": {} - }, - { - "label": "Path", - "importPath": "pathlib", - "description": "pathlib", - "isExtraImport": true, - "detail": "pathlib", - "documentation": {} - }, - { - "label": "app", - "importPath": "app.main", - "description": "app.main", - "isExtraImport": true, - "detail": "app.main", - "documentation": {} - }, - { - "label": "TestClient", - "importPath": "fastapi.testclient", - "description": "fastapi.testclient", - "isExtraImport": true, - "detail": "fastapi.testclient", - "documentation": {} - }, { "label": "bin_dir", "kind": 5, @@ -890,7 +834,7 @@ "kind": 6, "importPath": "src.app.core.config", "description": "src.app.core.config", - "peekOfCode": "class Settings(BaseSettings):\n API_V1_STR: str = \"/api/v1\"\n SUPABASE_URL: str = Field(default_factory=lambda: os.getenv(\"SUPABASE_URL\"))\n SUPABASE_KEY: str = Field(default_factory=lambda: os.getenv(\"SUPABASE_KEY\"))\n SUPERUSER_EMAIL: str = Field(default_factory=lambda: os.getenv(\"SUPERUSER_EMAIL\"))\n SUPERUSER_PASSWORD: str = Field(default=lambda: os.getenv(\"SUPERUSER_PASSWORD\"))\n # SERVER_NAME: str\n SERVER_HOST: AnyHttpUrl = AnyHttpUrl(\"https://localhost\")\n SERVER_PORT: int = 8000\n # # TODO: the following need to follow the newest version of fastapi", + "peekOfCode": "class Settings(BaseSettings):\n API_V1_STR: str = \"/api/v1\"\n SUPABASE_URL: str = Field(default_factory=lambda: os.getenv(\"SUPABASE_URL\"))\n SUPABASE_KEY: str = Field(default_factory=lambda: os.getenv(\"SUPABASE_KEY\"))\n # SUPERUSER_EMAIL: str = Field(default_factory=lambda: os.getenv(\"SUPERUSER_EMAIL\"))\n # SUPERUSER_PASSWORD: str = Field(default=lambda: os.getenv(\"SUPERUSER_PASSWORD\"))\n # SERVER_NAME: str\n SERVER_HOST: AnyHttpUrl = AnyHttpUrl(\"https://localhost\")\n SERVER_PORT: int = 8000\n # # TODO: the following need to follow the newest version of fastapi", "detail": "src.app.core.config", "documentation": {} }, @@ -917,7 +861,7 @@ "kind": 5, "importPath": "src.app.core.config", "description": "src.app.core.config", - "peekOfCode": "stream_handler = logging.StreamHandler()\nstream_handler.setFormatter(log_format)\nroot_logger.addHandler(stream_handler)\nlogger = logging.getLogger(__name__)\nload_dotenv()\nclass Settings(BaseSettings):\n API_V1_STR: str = \"/api/v1\"\n SUPABASE_URL: str = Field(default_factory=lambda: os.getenv(\"SUPABASE_URL\"))\n SUPABASE_KEY: str = Field(default_factory=lambda: os.getenv(\"SUPABASE_KEY\"))\n SUPERUSER_EMAIL: str = Field(default_factory=lambda: os.getenv(\"SUPERUSER_EMAIL\"))", + "peekOfCode": "stream_handler = logging.StreamHandler()\nstream_handler.setFormatter(log_format)\nroot_logger.addHandler(stream_handler)\nlogger = logging.getLogger(__name__)\nload_dotenv()\nclass Settings(BaseSettings):\n API_V1_STR: str = \"/api/v1\"\n SUPABASE_URL: str = Field(default_factory=lambda: os.getenv(\"SUPABASE_URL\"))\n SUPABASE_KEY: str = Field(default_factory=lambda: os.getenv(\"SUPABASE_KEY\"))\n # SUPERUSER_EMAIL: str = Field(default_factory=lambda: os.getenv(\"SUPERUSER_EMAIL\"))", "detail": "src.app.core.config", "documentation": {} }, @@ -926,7 +870,7 @@ "kind": 5, "importPath": "src.app.core.config", "description": "src.app.core.config", - "peekOfCode": "logger = logging.getLogger(__name__)\nload_dotenv()\nclass Settings(BaseSettings):\n API_V1_STR: str = \"/api/v1\"\n SUPABASE_URL: str = Field(default_factory=lambda: os.getenv(\"SUPABASE_URL\"))\n SUPABASE_KEY: str = Field(default_factory=lambda: os.getenv(\"SUPABASE_KEY\"))\n SUPERUSER_EMAIL: str = Field(default_factory=lambda: os.getenv(\"SUPERUSER_EMAIL\"))\n SUPERUSER_PASSWORD: str = Field(default=lambda: os.getenv(\"SUPERUSER_PASSWORD\"))\n # SERVER_NAME: str\n SERVER_HOST: AnyHttpUrl = AnyHttpUrl(\"https://localhost\")", + "peekOfCode": "logger = logging.getLogger(__name__)\nload_dotenv()\nclass Settings(BaseSettings):\n API_V1_STR: str = \"/api/v1\"\n SUPABASE_URL: str = Field(default_factory=lambda: os.getenv(\"SUPABASE_URL\"))\n SUPABASE_KEY: str = Field(default_factory=lambda: os.getenv(\"SUPABASE_KEY\"))\n # SUPERUSER_EMAIL: str = Field(default_factory=lambda: os.getenv(\"SUPERUSER_EMAIL\"))\n # SUPERUSER_PASSWORD: str = Field(default=lambda: os.getenv(\"SUPERUSER_PASSWORD\"))\n # SERVER_NAME: str\n SERVER_HOST: AnyHttpUrl = AnyHttpUrl(\"https://localhost\")", "detail": "src.app.core.config", "documentation": {} }, diff --git a/README.md b/README.md index aaf418a..862e4be 100644 --- a/README.md +++ b/README.md @@ -32,51 +32,21 @@ ___ ___ -### FastAPI&supabase - -1. works of authorization all handled by supabase-py and fastapi **dependency** without any extra code -2. supabase-py crud integration with **pydantic** model validation - -### Pytest - -1. pytest integration with **pytest-cov** -2. pytest **fixtures** for fastapi client and supabase client -3. pytest **fixtures** for access_token and refresh_token -4. test for **CRUD** operations -5. test for **api** operations - -### CI/CD - -1. **codecov** for coverage report -2. **poetry** for dependency management and pytest integration -3. **pre-commit** for code quality -4. **latest_changes.yml** for auto update README.md -5. **Semantic Release** for auto release and changelog -6. **docker** for deployment - ## How to use it -___ -![](docs/assets/usage.gif) -1. create your github repo and config it - 1. allow ci to access your repo - ![img.png](docs/assets/img.png) - 2. config ci_tokens - 1. `CODECOV_TOKEN` for codecov in `.github/workflows/ci.yml` ,`semantic-release` is optional for auto release - 2. `ATTICUS_PAT`should replace with your GitHub token for latest_changes.yml in `.github/workflows/latest_changes.yml` - 3. `DOCKER_USERNAME` and `DOCKER_PASSWORD` for docker-image.yml in `.github/workflows/docker-image.yml` - 4. replace `tags: atticuszhou/supafast:latest` with your docker repo in `.github/workflows/docker-image.yml` - 3. config fastapi setting in `your_project\src\app\core\config.py` - 4. config `pyproject.toml` with your project name and description,etc -2. cd your repo and install dependencies with [uv](https://github.com/astral-sh/uv), which is an extremely fast Python package and project manager, written in Rust. +### install python dependencies + +cd your repo and install dependencies with [uv](https://github.com/astral-sh/uv), which is an extremely fast Python package and project manager, written in Rust. ```shell uv sync ``` -3. [start your supabase locally](https://supabase.com/docs/guides/local-development/cli/getting-started?queryGroups=platform&platform=linux&queryGroups=access-method&access-method=postgres) +### install supabase + +1. [start your supabase locally](https://supabase.com/docs/guides/local-development/cli/getting-started?queryGroups=platform&platform=linux&queryGroups=access-method&access-method=postgres) ```bash # brew in linux https://brew.sh/ @@ -85,34 +55,17 @@ supabase init supabase start ``` -4. set your supabase env +2. set your supabase env ```shell export SUPABASE_URL=your_supabase_url export SUPABASE_KEY=your_supabase_key -export SUPERUSER_EMAIL=your_superuser_email -export SUPERUSER_PASSWORD=your_superuser_password ``` -5. config fastapi settings - -```python -# src/app/core/config.py -class Settings(BaseSettings): - API_V1_STR: str = "/api/v1" - SUPABASE_URL: str = Field(default_factory=lambda: os.getenv("SUPABASE_URL")) - SUPABASE_KEY: str = Field(default_factory=lambda: os.getenv("SUPABASE_KEY")) - SUPERUSER_EMAIL: str = Field(default_factory=lambda: os.getenv("SUPERUSER_EMAIL")) - SUPERUSER_PASSWORD: str = Field(default=lambda: os.getenv("SUPERUSER_PASSWORD")) - # SERVER_NAME: str - SERVER_HOST: AnyHttpUrl = "https://localhost" - SERVER_PORT: int = 8000 - BACKEND_CORS_ORIGINS: list[AnyHttpUrl] = [] - PROJECT_NAME: str = "fastapi supabase template" - Config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) -``` -6. run server +### run + +1. run server ```shell uv run uvicorn app.main:app --reload @@ -123,63 +76,18 @@ uv run uvicorn app.main:app --reload ___ - [x] FastAPI backend - - [x] **standard** structure + - [ ] Layered Architecture for **FastAPI** project + - [ ] sqlmodel ORM supabase + - [ ] config for fastapi and postgresql connection for supabase - ```text - ── src - │ └── app - │ ├── api - │ │ ├── api_v1 - │ │ │ ├── endpoints - │ │ │ │ ├── __init__.py - │ │ │ │ └── items.py - │ │ │ ├── __init__.py - │ │ │ └── api.py - │ │ ├── __init__.py - │ │ └── deps.py - │ ├── core - │ │ ├── __init__.py - │ │ ├── config.py - │ │ └── events.py - │ ├── crud - │ │ ├── __init__.py - │ │ ├── base.py - │ │ └── crud_item.py - │ ├── schemas - │ │ ├── __init__.py - │ │ ├── auth.py - │ │ ├── base.py - │ │ ├── item.py - │ │ └── msg.py - │ ├── services - │ │ └── __init__.py - │ ├── utils - │ │ └── __init__.py - │ ├── __init__.py - │ └── main.py - ... - ``` - - - [x] **auto-auth** by fastapi dependency with supabase-auth - - [x] **CRUD** operations pytest - - [x] **api** requests pytest + - [ ] **auto-auth** by fastapi dependency with supabase-auth + - [ ] **CRUD** operations pytest + - [ ] **api** requests pytest - [ ] Supabase integration - - [x] crud supabase-postgresql + - [ ] crud supabase-postgresql - [ ] websocket with supabase-realtime - [ ] curd supabase-storage - [ ] supafunc integration -- [x] deployment - - [x] Full **Docker** integration (Docker based). -- [ ] clone - - [ ] cookiecutter - -## Release Notes 🥸 - -___ - -### Latest Changes - -## License - -This project is licensed under the terms of the MIT license. +- [ ] deployment + - [ ] Full **Docker** integration (Docker based). diff --git a/pyproject.toml b/pyproject.toml index a1258ad..c4e1dc4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,7 @@ dependencies = [ "python-multipart>=0.0.9", "supabase>=2.7.4", "fastapi>=0.112.2", + "sqlmodel>=0.0.22", ] [dependency-groups] diff --git a/src/app/core/config.py b/src/app/core/config.py index ba5d4e6..231cda0 100644 --- a/src/app/core/config.py +++ b/src/app/core/config.py @@ -3,7 +3,8 @@ from typing import ClassVar from dotenv import load_dotenv -from pydantic import AnyHttpUrl, ConfigDict, Field +from pydantic import AnyHttpUrl, ConfigDict, Field, HttpUrl, PostgresDsn, computed_field +from pydantic_core import MultiHostUrl from pydantic_settings import BaseSettings log_format = logging.Formatter("%(asctime)s : %(levelname)s - %(message)s") @@ -25,8 +26,8 @@ class Settings(BaseSettings): API_V1_STR: str = "/api/v1" SUPABASE_URL: str = Field(default_factory=lambda: os.getenv("SUPABASE_URL")) SUPABASE_KEY: str = Field(default_factory=lambda: os.getenv("SUPABASE_KEY")) - SUPERUSER_EMAIL: str = Field(default_factory=lambda: os.getenv("SUPERUSER_EMAIL")) - SUPERUSER_PASSWORD: str = Field(default=lambda: os.getenv("SUPERUSER_PASSWORD")) + # SUPERUSER_EMAIL: str = Field(default_factory=lambda: os.getenv("SUPERUSER_EMAIL")) + # SUPERUSER_PASSWORD: str = Field(default=lambda: os.getenv("SUPERUSER_PASSWORD")) # SERVER_NAME: str SERVER_HOST: AnyHttpUrl = AnyHttpUrl("https://localhost") SERVER_PORT: int = 8000 @@ -35,21 +36,27 @@ class Settings(BaseSettings): # # e.g: '["http://localhost", "http://localhost:4200", "http://localhost:3000", \ # # "http://localhost:8080", "http://local.dockertoolbox.tiangolo.com"]' BACKEND_CORS_ORIGINS: list[AnyHttpUrl] = [] - # - # @validator("BACKEND_CORS_ORIGINS", pre=True) - # def assemble_cors_origins(cls, v: Union[str, List[str]]) -> Union[List[str], str]: - # if isinstance(v, str) and not v.startswith("["): - # return [i.strip() for i in v.split(",")] - # elif isinstance(v, (list, str)): - # return v - # raise ValueError(v) - # - PROJECT_NAME: str = "fastapi supabase template" - - # class Config(ConfigDict): - # """sensitive to lowercase""" - # - # case_sensitive = True + + PROJECT_NAME: str + SENTRY_DSN: HttpUrl | None = None + POSTGRES_SERVER: str + POSTGRES_PORT: int = 5432 + POSTGRES_USER: str + POSTGRES_PASSWORD: str = "" + POSTGRES_DB: str = "" + + @computed_field # type: ignore[prop-decorator] + @property + def SQLALCHEMY_DATABASE_URI(self) -> PostgresDsn: + return MultiHostUrl.build( + scheme="postgresql+psycopg", + username=self.POSTGRES_USER, + password=self.POSTGRES_PASSWORD, + host=self.POSTGRES_SERVER, + port=self.POSTGRES_PORT, + path=self.POSTGRES_DB, + ) + Config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) diff --git a/src/app/core/db.py b/src/app/core/db.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/api/api_v1/test_items.py b/tests/api/api_v1/test_items.py index 774d8e0..5d6b391 100644 --- a/tests/api/api_v1/test_items.py +++ b/tests/api/api_v1/test_items.py @@ -1,115 +1,115 @@ -# Additional assertions based on your application's logic -import pytest -from app.schemas import Token -from faker import Faker -from starlette.testclient import TestClient +# # Additional assertions based on your application's logic +# import pytest +# from app.schemas import Token +# from faker import Faker +# from starlette.testclient import TestClient -from supabase._async.client import AsyncClient -from tests.utils import get_auth_header +# from supabase._async.client import AsyncClient +# from tests.utils import get_auth_header -@pytest.mark.anyio -async def test_create_item(client: TestClient, token: Token) -> None: +# @pytest.mark.anyio +# async def test_create_item(client: TestClient, token: Token) -> None: - headers = get_auth_header(token.access_token) +# headers = get_auth_header(token.access_token) - test_data = Faker().sentence() +# test_data = Faker().sentence() - response = client.post( - "/api/v1/items/create-item", headers=headers, json={"test_data": test_data} - ) - assert response.status_code == 200 - assert response.json()["test_data"] == test_data +# response = client.post( +# "/api/v1/items/create-item", headers=headers, json={"test_data": test_data} +# ) +# assert response.status_code == 200 +# assert response.json()["test_data"] == test_data -@pytest.mark.anyio -async def test_read_all_items(client: TestClient, token: Token) -> None: - headers = get_auth_header(token.access_token) +# @pytest.mark.anyio +# async def test_read_all_items(client: TestClient, token: Token) -> None: +# headers = get_auth_header(token.access_token) - response = client.get("/api/v1/items/read-all-item", headers=headers) - assert response.status_code == 200 - assert isinstance(response.json(), list) +# response = client.get("/api/v1/items/read-all-item", headers=headers) +# assert response.status_code == 200 +# assert isinstance(response.json(), list) -@pytest.mark.anyio -async def test_read_item_by_id(client: TestClient, token: Token) -> None: - headers = get_auth_header(token.access_token) - test_data = Faker().sentence() +# @pytest.mark.anyio +# async def test_read_item_by_id(client: TestClient, token: Token) -> None: +# headers = get_auth_header(token.access_token) +# test_data = Faker().sentence() - create_response = client.post( - "/api/v1/items/create-item", headers=headers, json={"test_data": test_data} - ) - assert create_response.status_code == 200 - created_item_id = create_response.json()["id"] +# create_response = client.post( +# "/api/v1/items/create-item", headers=headers, json={"test_data": test_data} +# ) +# assert create_response.status_code == 200 +# created_item_id = create_response.json()["id"] - read_response = client.get( - f"/api/v1/items/get-by-id/{created_item_id}", headers=headers - ) - assert read_response.status_code == 200 - assert read_response.json()["id"] == created_item_id - assert read_response.json()["test_data"] == test_data +# read_response = client.get( +# f"/api/v1/items/get-by-id/{created_item_id}", headers=headers +# ) +# assert read_response.status_code == 200 +# assert read_response.json()["id"] == created_item_id +# assert read_response.json()["test_data"] == test_data -@pytest.mark.anyio -async def test_read_item_by_owner( - client: TestClient, token: Token, db: AsyncClient -) -> None: - headers = get_auth_header(token.access_token) - test_data = Faker().sentence() +# @pytest.mark.anyio +# async def test_read_item_by_owner( +# client: TestClient, token: Token, db: AsyncClient +# ) -> None: +# headers = get_auth_header(token.access_token) +# test_data = Faker().sentence() - client.post( - "/api/v1/items/create-item", headers=headers, json={"test_data": test_data} - ) +# client.post( +# "/api/v1/items/create-item", headers=headers, json={"test_data": test_data} +# ) - user = await db.auth.get_user(jwt=token.access_token) - user_id = user.user.id +# user = await db.auth.get_user(jwt=token.access_token) +# user_id = user.user.id - read_response = client.get("/api/v1/items/get-by-owner", headers=headers) - assert read_response.status_code == 200 - items = read_response.json() - assert isinstance(items, list) - assert all(item["user_id"] == user_id for item in items) +# read_response = client.get("/api/v1/items/get-by-owner", headers=headers) +# assert read_response.status_code == 200 +# items = read_response.json() +# assert isinstance(items, list) +# assert all(item["user_id"] == user_id for item in items) -@pytest.mark.anyio -async def test_update_item(client: TestClient, token: Token) -> None: - headers = get_auth_header(token.access_token) - test_data = Faker().sentence() - create_response = client.post( - "/api/v1/items/create-item", headers=headers, json={"test_data": test_data} - ) - assert create_response.status_code == 200 - created_item = create_response.json() - created_item_id = created_item["id"] - updated_data = {"test_data": Faker().sentence(), "id": created_item_id} +# @pytest.mark.anyio +# async def test_update_item(client: TestClient, token: Token) -> None: +# headers = get_auth_header(token.access_token) +# test_data = Faker().sentence() +# create_response = client.post( +# "/api/v1/items/create-item", headers=headers, json={"test_data": test_data} +# ) +# assert create_response.status_code == 200 +# created_item = create_response.json() +# created_item_id = created_item["id"] +# updated_data = {"test_data": Faker().sentence(), "id": created_item_id} - update_response = client.put( - "/api/v1/items/update-item", headers=headers, json=updated_data - ) - assert update_response.status_code == 200 - assert update_response.json()["test_data"] == updated_data["test_data"] +# update_response = client.put( +# "/api/v1/items/update-item", headers=headers, json=updated_data +# ) +# assert update_response.status_code == 200 +# assert update_response.json()["test_data"] == updated_data["test_data"] -@pytest.mark.anyio -async def test_delete_item(client: TestClient, token: Token) -> None: - headers = get_auth_header(token.access_token) - test_data = Faker().sentence() - create_response = client.post( - "/api/v1/items/create-item", headers=headers, json={"test_data": test_data} - ) - assert create_response.status_code == 200 - created_item_id = create_response.json()["id"] +# @pytest.mark.anyio +# async def test_delete_item(client: TestClient, token: Token) -> None: +# headers = get_auth_header(token.access_token) +# test_data = Faker().sentence() +# create_response = client.post( +# "/api/v1/items/create-item", headers=headers, json={"test_data": test_data} +# ) +# assert create_response.status_code == 200 +# created_item_id = create_response.json()["id"] - delete_response = client.delete( - f"/api/v1/items/delete/{created_item_id}", headers=headers - ) - assert delete_response.status_code == 200 +# delete_response = client.delete( +# f"/api/v1/items/delete/{created_item_id}", headers=headers +# ) +# assert delete_response.status_code == 200 - get_response = client.get( - f"/api/v1/items/get-by-id/{created_item_id}", headers=headers - ) - assert get_response.status_code == 200 - assert get_response.json() is None +# get_response = client.get( +# f"/api/v1/items/get-by-id/{created_item_id}", headers=headers +# ) +# assert get_response.status_code == 200 +# assert get_response.json() is None diff --git a/tests/conftest.py b/tests/conftest.py index 7c01e94..7a031b5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,8 +5,8 @@ from pathlib import Path import pytest -from app.main import app -from app.schemas import Token +from src.app.main import app +from src.app.schemas import Token from dotenv import load_dotenv from faker import Faker from fastapi.testclient import TestClient diff --git a/tests/crud/test_item.py b/tests/crud/test_item.py index 0f1ef0d..45173d3 100644 --- a/tests/crud/test_item.py +++ b/tests/crud/test_item.py @@ -1,50 +1,50 @@ -import pytest -from app import crud -from app.schemas.item import Item, ItemCreate, ItemUpdate -from faker import Faker -from supabase._async.client import AsyncClient - - -@pytest.mark.anyio -async def test_create_item(db: AsyncClient) -> None: - test_data = Faker().text() - item_in = ItemCreate(test_data=test_data) - item: Item = await crud.item.create(db=db, obj_in=item_in) - assert item.test_data == test_data - - -@pytest.mark.anyio -async def test_get_item(db: AsyncClient) -> None: - test_data = Faker().text() - item_in = ItemCreate(table_name="test_table", test_data=test_data) - item: Item = await crud.item.create(db=db, obj_in=item_in) - stored_item = await crud.item.get(db, id=item.id) - assert stored_item - assert item.id == stored_item.id - assert item.test_data == stored_item.test_data - - -@pytest.mark.anyio -async def test_update_item(db: AsyncClient) -> None: - test_data = Faker().text() - item_in = ItemCreate(table_name="test_table", test_data=test_data) - item: Item = await crud.item.create(db=db, obj_in=item_in) - test_data2 = Faker().text() - item_update = ItemUpdate(table_name="test_table", id=item.id, test_data=test_data2) - item2 = await crud.item.update(db=db, obj_in=item_update) - assert item.id == item2.id - assert item.test_data != item2.test_data - assert item2.test_data == test_data2 - - -@pytest.mark.anyio -async def test_delete_item(db: AsyncClient) -> None: - test_data = Faker().text() - item_in = ItemCreate(table_name="test_table", test_data=test_data) - item: Item = await crud.item.create(db=db, obj_in=item_in) - item2 = await crud.item.delete(db=db, id=item.id) - item3 = await crud.item.get(db, id=item.id) - assert item3 is None - assert item.id == item2.id - assert item.test_data == item2.test_data - assert item2.test_data == test_data +# import pytest +# from app import crud +# from app.schemas.item import Item, ItemCreate, ItemUpdate +# from faker import Faker +# from supabase._async.client import AsyncClient + + +# @pytest.mark.anyio +# async def test_create_item(db: AsyncClient) -> None: +# test_data = Faker().text() +# item_in = ItemCreate(test_data=test_data) +# item: Item = await crud.item.create(db=db, obj_in=item_in) +# assert item.test_data == test_data + + +# @pytest.mark.anyio +# async def test_get_item(db: AsyncClient) -> None: +# test_data = Faker().text() +# item_in = ItemCreate(table_name="test_table", test_data=test_data) +# item: Item = await crud.item.create(db=db, obj_in=item_in) +# stored_item = await crud.item.get(db, id=item.id) +# assert stored_item +# assert item.id == stored_item.id +# assert item.test_data == stored_item.test_data + + +# @pytest.mark.anyio +# async def test_update_item(db: AsyncClient) -> None: +# test_data = Faker().text() +# item_in = ItemCreate(table_name="test_table", test_data=test_data) +# item: Item = await crud.item.create(db=db, obj_in=item_in) +# test_data2 = Faker().text() +# item_update = ItemUpdate(table_name="test_table", id=item.id, test_data=test_data2) +# item2 = await crud.item.update(db=db, obj_in=item_update) +# assert item.id == item2.id +# assert item.test_data != item2.test_data +# assert item2.test_data == test_data2 + + +# @pytest.mark.anyio +# async def test_delete_item(db: AsyncClient) -> None: +# test_data = Faker().text() +# item_in = ItemCreate(table_name="test_table", test_data=test_data) +# item: Item = await crud.item.create(db=db, obj_in=item_in) +# item2 = await crud.item.delete(db=db, id=item.id) +# item3 = await crud.item.get(db, id=item.id) +# assert item3 is None +# assert item.id == item2.id +# assert item.test_data == item2.test_data +# assert item2.test_data == test_data diff --git a/uv.lock b/uv.lock index b504765..d99f64b 100644 --- a/uv.lock +++ b/uv.lock @@ -421,6 +421,7 @@ dependencies = [ { name = "pydantic-settings" }, { name = "python-dotenv" }, { name = "python-multipart" }, + { name = "sqlmodel" }, { name = "supabase" }, { name = "uvicorn" }, ] @@ -453,6 +454,7 @@ requires-dist = [ { name = "pydantic-settings", specifier = ">=2.4.0" }, { name = "python-dotenv", specifier = ">=1.0.1" }, { name = "python-multipart", specifier = ">=0.0.9" }, + { name = "sqlmodel", specifier = ">=0.0.22" }, { name = "supabase", specifier = ">=2.7.4" }, { name = "uvicorn", specifier = ">=0.30.6" }, ] @@ -607,6 +609,57 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/18/ee/4aad2b25941393e956ccda6aea8b2e144dbd780adb893e7f5b7147f0527e/gotrue-2.8.1-py3-none-any.whl", hash = "sha256:97dff077d71cca629f046c35ba34fae132b69c55fe271651766ddcf6d8132468", size = 48388 }, ] +[[package]] +name = "greenlet" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/ff/df5fede753cc10f6a5be0931204ea30c35fa2f2ea7a35b25bdaf4fe40e46/greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", size = 186022 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/90/5234a78dc0ef6496a6eb97b67a42a8e96742a56f7dc808cb954a85390448/greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563", size = 271235 }, + { url = "https://files.pythonhosted.org/packages/7c/16/cd631fa0ab7d06ef06387135b7549fdcc77d8d859ed770a0d28e47b20972/greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83", size = 637168 }, + { url = "https://files.pythonhosted.org/packages/2f/b1/aed39043a6fec33c284a2c9abd63ce191f4f1a07319340ffc04d2ed3256f/greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0", size = 648826 }, + { url = "https://files.pythonhosted.org/packages/76/25/40e0112f7f3ebe54e8e8ed91b2b9f970805143efef16d043dfc15e70f44b/greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120", size = 644443 }, + { url = "https://files.pythonhosted.org/packages/fb/2f/3850b867a9af519794784a7eeed1dd5bc68ffbcc5b28cef703711025fd0a/greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc", size = 643295 }, + { url = "https://files.pythonhosted.org/packages/cf/69/79e4d63b9387b48939096e25115b8af7cd8a90397a304f92436bcb21f5b2/greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617", size = 599544 }, + { url = "https://files.pythonhosted.org/packages/46/1d/44dbcb0e6c323bd6f71b8c2f4233766a5faf4b8948873225d34a0b7efa71/greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7", size = 1125456 }, + { url = "https://files.pythonhosted.org/packages/e0/1d/a305dce121838d0278cee39d5bb268c657f10a5363ae4b726848f833f1bb/greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6", size = 1149111 }, + { url = "https://files.pythonhosted.org/packages/96/28/d62835fb33fb5652f2e98d34c44ad1a0feacc8b1d3f1aecab035f51f267d/greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80", size = 298392 }, + { url = "https://files.pythonhosted.org/packages/28/62/1c2665558618553c42922ed47a4e6d6527e2fa3516a8256c2f431c5d0441/greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70", size = 272479 }, + { url = "https://files.pythonhosted.org/packages/76/9d/421e2d5f07285b6e4e3a676b016ca781f63cfe4a0cd8eaecf3fd6f7a71ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159", size = 640404 }, + { url = "https://files.pythonhosted.org/packages/e5/de/6e05f5c59262a584e502dd3d261bbdd2c97ab5416cc9c0b91ea38932a901/greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e", size = 652813 }, + { url = "https://files.pythonhosted.org/packages/49/93/d5f93c84241acdea15a8fd329362c2c71c79e1a507c3f142a5d67ea435ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1", size = 648517 }, + { url = "https://files.pythonhosted.org/packages/15/85/72f77fc02d00470c86a5c982b8daafdf65d38aefbbe441cebff3bf7037fc/greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383", size = 647831 }, + { url = "https://files.pythonhosted.org/packages/f7/4b/1c9695aa24f808e156c8f4813f685d975ca73c000c2a5056c514c64980f6/greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a", size = 602413 }, + { url = "https://files.pythonhosted.org/packages/76/70/ad6e5b31ef330f03b12559d19fda2606a522d3849cde46b24f223d6d1619/greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511", size = 1129619 }, + { url = "https://files.pythonhosted.org/packages/f4/fb/201e1b932e584066e0f0658b538e73c459b34d44b4bd4034f682423bc801/greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395", size = 1155198 }, + { url = "https://files.pythonhosted.org/packages/12/da/b9ed5e310bb8b89661b80cbcd4db5a067903bbcd7fc854923f5ebb4144f0/greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39", size = 298930 }, + { url = "https://files.pythonhosted.org/packages/7d/ec/bad1ac26764d26aa1353216fcbfa4670050f66d445448aafa227f8b16e80/greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", size = 274260 }, + { url = "https://files.pythonhosted.org/packages/66/d4/c8c04958870f482459ab5956c2942c4ec35cac7fe245527f1039837c17a9/greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", size = 649064 }, + { url = "https://files.pythonhosted.org/packages/51/41/467b12a8c7c1303d20abcca145db2be4e6cd50a951fa30af48b6ec607581/greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", size = 663420 }, + { url = "https://files.pythonhosted.org/packages/27/8f/2a93cd9b1e7107d5c7b3b7816eeadcac2ebcaf6d6513df9abaf0334777f6/greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441", size = 658035 }, + { url = "https://files.pythonhosted.org/packages/57/5c/7c6f50cb12be092e1dccb2599be5a942c3416dbcfb76efcf54b3f8be4d8d/greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36", size = 660105 }, + { url = "https://files.pythonhosted.org/packages/f1/66/033e58a50fd9ec9df00a8671c74f1f3a320564c6415a4ed82a1c651654ba/greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9", size = 613077 }, + { url = "https://files.pythonhosted.org/packages/19/c5/36384a06f748044d06bdd8776e231fadf92fc896bd12cb1c9f5a1bda9578/greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", size = 1135975 }, + { url = "https://files.pythonhosted.org/packages/38/f9/c0a0eb61bdf808d23266ecf1d63309f0e1471f284300ce6dac0ae1231881/greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", size = 1163955 }, + { url = "https://files.pythonhosted.org/packages/43/21/a5d9df1d21514883333fc86584c07c2b49ba7c602e670b174bd73cfc9c7f/greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", size = 299655 }, + { url = "https://files.pythonhosted.org/packages/f3/57/0db4940cd7bb461365ca8d6fd53e68254c9dbbcc2b452e69d0d41f10a85e/greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", size = 272990 }, + { url = "https://files.pythonhosted.org/packages/1c/ec/423d113c9f74e5e402e175b157203e9102feeb7088cee844d735b28ef963/greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", size = 649175 }, + { url = "https://files.pythonhosted.org/packages/a9/46/ddbd2db9ff209186b7b7c621d1432e2f21714adc988703dbdd0e65155c77/greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", size = 663425 }, + { url = "https://files.pythonhosted.org/packages/bc/f9/9c82d6b2b04aa37e38e74f0c429aece5eeb02bab6e3b98e7db89b23d94c6/greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", size = 657736 }, + { url = "https://files.pythonhosted.org/packages/d9/42/b87bc2a81e3a62c3de2b0d550bf91a86939442b7ff85abb94eec3fc0e6aa/greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", size = 660347 }, + { url = "https://files.pythonhosted.org/packages/37/fa/71599c3fd06336cdc3eac52e6871cfebab4d9d70674a9a9e7a482c318e99/greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", size = 615583 }, + { url = "https://files.pythonhosted.org/packages/4e/96/e9ef85de031703ee7a4483489b40cf307f93c1824a02e903106f2ea315fe/greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", size = 1133039 }, + { url = "https://files.pythonhosted.org/packages/87/76/b2b6362accd69f2d1889db61a18c94bc743e961e3cab344c2effaa4b4a25/greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", size = 1160716 }, + { url = "https://files.pythonhosted.org/packages/1f/1b/54336d876186920e185066d8c3024ad55f21d7cc3683c856127ddb7b13ce/greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", size = 299490 }, + { url = "https://files.pythonhosted.org/packages/5f/17/bea55bf36990e1638a2af5ba10c1640273ef20f627962cf97107f1e5d637/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", size = 643731 }, + { url = "https://files.pythonhosted.org/packages/78/d2/aa3d2157f9ab742a08e0fd8f77d4699f37c22adfbfeb0c610a186b5f75e0/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", size = 649304 }, + { url = "https://files.pythonhosted.org/packages/f1/8e/d0aeffe69e53ccff5a28fa86f07ad1d2d2d6537a9506229431a2a02e2f15/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", size = 646537 }, + { url = "https://files.pythonhosted.org/packages/05/79/e15408220bbb989469c8871062c97c6c9136770657ba779711b90870d867/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", size = 642506 }, + { url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753 }, + { url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731 }, + { url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112 }, +] + [[package]] name = "h11" version = "0.14.0" @@ -1511,6 +1564,64 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186 }, ] +[[package]] +name = "sqlalchemy" +version = "2.0.36" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet", marker = "(python_full_version < '3.13' and platform_machine == 'AMD64') or (python_full_version < '3.13' and platform_machine == 'WIN32') or (python_full_version < '3.13' and platform_machine == 'aarch64') or (python_full_version < '3.13' and platform_machine == 'amd64') or (python_full_version < '3.13' and platform_machine == 'ppc64le') or (python_full_version < '3.13' and platform_machine == 'win32') or (python_full_version < '3.13' and platform_machine == 'x86_64')" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/65/9cbc9c4c3287bed2499e05033e207473504dc4df999ce49385fb1f8b058a/sqlalchemy-2.0.36.tar.gz", hash = "sha256:7f2767680b6d2398aea7082e45a774b2b0767b5c8d8ffb9c8b683088ea9b29c5", size = 9574485 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/72/14ab694b8b3f0e35ef5beb74a8fea2811aa791ba1611c44dc90cdf46af17/SQLAlchemy-2.0.36-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:59b8f3adb3971929a3e660337f5dacc5942c2cdb760afcabb2614ffbda9f9f72", size = 2092604 }, + { url = "https://files.pythonhosted.org/packages/1e/59/333fcbca58b79f5b8b61853d6137530198823392151fa8fd9425f367519e/SQLAlchemy-2.0.36-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37350015056a553e442ff672c2d20e6f4b6d0b2495691fa239d8aa18bb3bc908", size = 2083796 }, + { url = "https://files.pythonhosted.org/packages/6c/a0/ec3c188d2b0c1bc742262e76408d44104598d7247c23f5b06bb97ee21bfa/SQLAlchemy-2.0.36-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8318f4776c85abc3f40ab185e388bee7a6ea99e7fa3a30686580b209eaa35c08", size = 3066165 }, + { url = "https://files.pythonhosted.org/packages/07/15/68ef91de5b8b7f80fb2d2b3b31ed42180c6227fe0a701aed9d01d34f98ec/SQLAlchemy-2.0.36-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c245b1fbade9c35e5bd3b64270ab49ce990369018289ecfde3f9c318411aaa07", size = 3074428 }, + { url = "https://files.pythonhosted.org/packages/e2/4c/9dfea5e63b87325eef6d9cdaac913459aa6a157a05a05ea6ff20004aee8e/SQLAlchemy-2.0.36-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:69f93723edbca7342624d09f6704e7126b152eaed3cdbb634cb657a54332a3c5", size = 3030477 }, + { url = "https://files.pythonhosted.org/packages/16/a5/fcfde8e74ea5f683b24add22463bfc21e431d4a5531c8a5b55bc6fbea164/SQLAlchemy-2.0.36-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f9511d8dd4a6e9271d07d150fb2f81874a3c8c95e11ff9af3a2dfc35fe42ee44", size = 3055942 }, + { url = "https://files.pythonhosted.org/packages/3c/ee/c22c415a771d791ae99146d72ffdb20e43625acd24835ea7fc157436d59f/SQLAlchemy-2.0.36-cp310-cp310-win32.whl", hash = "sha256:c3f3631693003d8e585d4200730616b78fafd5a01ef8b698f6967da5c605b3fa", size = 2064960 }, + { url = "https://files.pythonhosted.org/packages/aa/af/ad9c25cadc79bd851bdb9d82b68af9bdb91ff05f56d0da2f8a654825974f/SQLAlchemy-2.0.36-cp310-cp310-win_amd64.whl", hash = "sha256:a86bfab2ef46d63300c0f06936bd6e6c0105faa11d509083ba8f2f9d237fb5b5", size = 2089078 }, + { url = "https://files.pythonhosted.org/packages/00/4e/5a67963fd7cbc1beb8bd2152e907419f4c940ef04600b10151a751fe9e06/SQLAlchemy-2.0.36-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fd3a55deef00f689ce931d4d1b23fa9f04c880a48ee97af488fd215cf24e2a6c", size = 2093782 }, + { url = "https://files.pythonhosted.org/packages/b3/24/30e33b6389ebb5a17df2a4243b091bc709fb3dfc9a48c8d72f8e037c943d/SQLAlchemy-2.0.36-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4f5e9cd989b45b73bd359f693b935364f7e1f79486e29015813c338450aa5a71", size = 2084180 }, + { url = "https://files.pythonhosted.org/packages/10/1e/70e9ed2143a27065246be40f78637ad5160ea0f5fd32f8cab819a31ff54d/SQLAlchemy-2.0.36-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ddd9db6e59c44875211bc4c7953a9f6638b937b0a88ae6d09eb46cced54eff", size = 3202469 }, + { url = "https://files.pythonhosted.org/packages/b4/5f/95e0ed74093ac3c0db6acfa944d4d8ac6284ef5e1136b878a327ea1f975a/SQLAlchemy-2.0.36-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2519f3a5d0517fc159afab1015e54bb81b4406c278749779be57a569d8d1bb0d", size = 3202464 }, + { url = "https://files.pythonhosted.org/packages/91/95/2cf9b85a6bc2ee660e40594dffe04e777e7b8617fd0c6d77a0f782ea96c9/SQLAlchemy-2.0.36-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59b1ee96617135f6e1d6f275bbe988f419c5178016f3d41d3c0abb0c819f75bb", size = 3139508 }, + { url = "https://files.pythonhosted.org/packages/92/ea/f0c01bc646456e4345c0fb5a3ddef457326285c2dc60435b0eb96b61bf31/SQLAlchemy-2.0.36-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:39769a115f730d683b0eb7b694db9789267bcd027326cccc3125e862eb03bfd8", size = 3159837 }, + { url = "https://files.pythonhosted.org/packages/a6/93/c8edbf153ee38fe529773240877bf1332ed95328aceef6254288f446994e/SQLAlchemy-2.0.36-cp311-cp311-win32.whl", hash = "sha256:66bffbad8d6271bb1cc2f9a4ea4f86f80fe5e2e3e501a5ae2a3dc6a76e604e6f", size = 2064529 }, + { url = "https://files.pythonhosted.org/packages/b1/03/d12b7c1d36fd80150c1d52e121614cf9377dac99e5497af8d8f5b2a8db64/SQLAlchemy-2.0.36-cp311-cp311-win_amd64.whl", hash = "sha256:23623166bfefe1487d81b698c423f8678e80df8b54614c2bf4b4cfcd7c711959", size = 2089874 }, + { url = "https://files.pythonhosted.org/packages/b8/bf/005dc47f0e57556e14512d5542f3f183b94fde46e15ff1588ec58ca89555/SQLAlchemy-2.0.36-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7b64e6ec3f02c35647be6b4851008b26cff592a95ecb13b6788a54ef80bbdd4", size = 2092378 }, + { url = "https://files.pythonhosted.org/packages/94/65/f109d5720779a08e6e324ec89a744f5f92c48bd8005edc814bf72fbb24e5/SQLAlchemy-2.0.36-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:46331b00096a6db1fdc052d55b101dbbfc99155a548e20a0e4a8e5e4d1362855", size = 2082778 }, + { url = "https://files.pythonhosted.org/packages/60/f6/d9aa8c49c44f9b8c9b9dada1f12fa78df3d4c42aa2de437164b83ee1123c/SQLAlchemy-2.0.36-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdf3386a801ea5aba17c6410dd1dc8d39cf454ca2565541b5ac42a84e1e28f53", size = 3232191 }, + { url = "https://files.pythonhosted.org/packages/8a/ab/81d4514527c068670cb1d7ab62a81a185df53a7c379bd2a5636e83d09ede/SQLAlchemy-2.0.36-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac9dfa18ff2a67b09b372d5db8743c27966abf0e5344c555d86cc7199f7ad83a", size = 3243044 }, + { url = "https://files.pythonhosted.org/packages/35/b4/f87c014ecf5167dc669199cafdb20a7358ff4b1d49ce3622cc48571f811c/SQLAlchemy-2.0.36-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:90812a8933df713fdf748b355527e3af257a11e415b613dd794512461eb8a686", size = 3178511 }, + { url = "https://files.pythonhosted.org/packages/ea/09/badfc9293bc3ccba6ede05e5f2b44a760aa47d84da1fc5a326e963e3d4d9/SQLAlchemy-2.0.36-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1bc330d9d29c7f06f003ab10e1eaced295e87940405afe1b110f2eb93a233588", size = 3205147 }, + { url = "https://files.pythonhosted.org/packages/c8/60/70e681de02a13c4b27979b7b78da3058c49bacc9858c89ba672e030f03f2/SQLAlchemy-2.0.36-cp312-cp312-win32.whl", hash = "sha256:79d2e78abc26d871875b419e1fd3c0bca31a1cb0043277d0d850014599626c2e", size = 2062709 }, + { url = "https://files.pythonhosted.org/packages/b7/ed/f6cd9395e41bfe47dd253d74d2dfc3cab34980d4e20c8878cb1117306085/SQLAlchemy-2.0.36-cp312-cp312-win_amd64.whl", hash = "sha256:b544ad1935a8541d177cb402948b94e871067656b3a0b9e91dbec136b06a2ff5", size = 2088433 }, + { url = "https://files.pythonhosted.org/packages/78/5c/236398ae3678b3237726819b484f15f5c038a9549da01703a771f05a00d6/SQLAlchemy-2.0.36-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b5cc79df7f4bc3d11e4b542596c03826063092611e481fcf1c9dfee3c94355ef", size = 2087651 }, + { url = "https://files.pythonhosted.org/packages/a8/14/55c47420c0d23fb67a35af8be4719199b81c59f3084c28d131a7767b0b0b/SQLAlchemy-2.0.36-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3c01117dd36800f2ecaa238c65365b7b16497adc1522bf84906e5710ee9ba0e8", size = 2078132 }, + { url = "https://files.pythonhosted.org/packages/3d/97/1e843b36abff8c4a7aa2e37f9bea364f90d021754c2de94d792c2d91405b/SQLAlchemy-2.0.36-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bc633f4ee4b4c46e7adcb3a9b5ec083bf1d9a97c1d3854b92749d935de40b9b", size = 3164559 }, + { url = "https://files.pythonhosted.org/packages/7b/c5/07f18a897b997f6d6b234fab2bf31dccf66d5d16a79fe329aefc95cd7461/SQLAlchemy-2.0.36-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e46ed38affdfc95d2c958de328d037d87801cfcbea6d421000859e9789e61c2", size = 3177897 }, + { url = "https://files.pythonhosted.org/packages/b3/cd/e16f3cbefd82b5c40b33732da634ec67a5f33b587744c7ab41699789d492/SQLAlchemy-2.0.36-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b2985c0b06e989c043f1dc09d4fe89e1616aadd35392aea2844f0458a989eacf", size = 3111289 }, + { url = "https://files.pythonhosted.org/packages/15/85/5b8a3b0bc29c9928aa62b5c91fcc8335f57c1de0a6343873b5f372e3672b/SQLAlchemy-2.0.36-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a121d62ebe7d26fec9155f83f8be5189ef1405f5973ea4874a26fab9f1e262c", size = 3139491 }, + { url = "https://files.pythonhosted.org/packages/a1/95/81babb6089938680dfe2cd3f88cd3fd39cccd1543b7cb603b21ad881bff1/SQLAlchemy-2.0.36-cp313-cp313-win32.whl", hash = "sha256:0572f4bd6f94752167adfd7c1bed84f4b240ee6203a95e05d1e208d488d0d436", size = 2060439 }, + { url = "https://files.pythonhosted.org/packages/c1/ce/5f7428df55660d6879d0522adc73a3364970b5ef33ec17fa125c5dbcac1d/SQLAlchemy-2.0.36-cp313-cp313-win_amd64.whl", hash = "sha256:8c78ac40bde930c60e0f78b3cd184c580f89456dd87fc08f9e3ee3ce8765ce88", size = 2084574 }, + { url = "https://files.pythonhosted.org/packages/b8/49/21633706dd6feb14cd3f7935fc00b60870ea057686035e1a99ae6d9d9d53/SQLAlchemy-2.0.36-py3-none-any.whl", hash = "sha256:fddbe92b4760c6f5d48162aef14824add991aeda8ddadb3c31d56eb15ca69f8e", size = 1883787 }, +] + +[[package]] +name = "sqlmodel" +version = "0.0.22" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "sqlalchemy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/39/8641040ab0d5e1d8a1c2325ae89a01ae659fc96c61a43d158fb71c9a0bf0/sqlmodel-0.0.22.tar.gz", hash = "sha256:7d37c882a30c43464d143e35e9ecaf945d88035e20117bf5ec2834a23cbe505e", size = 116392 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dd/b1/3af5104b716c420e40a6ea1b09886cae3a1b9f4538343875f637755cae5b/sqlmodel-0.0.22-py3-none-any.whl", hash = "sha256:a1ed13e28a1f4057cbf4ff6cdb4fc09e85702621d3259ba17b3c230bfb2f941b", size = 28276 }, +] + [[package]] name = "starlette" version = "0.38.2"