diff --git a/.dockerignore b/.dockerignore index ca72794..88290c2 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,11 +2,11 @@ ** # Include (don't ignore) the application code -!app +!dewy !./pyproject.toml !./poetry.lock -# Re-ignore pycache within `app`. +# Re-ignore pycache within `dewy`. **/__pycache__ # Include (don't ignore) the migrations. diff --git a/Dockerfile b/Dockerfile index 7b4b940..ced0ff3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,8 +18,8 @@ COPY --from=requirements-stage /tmp/requirements.txt /code/requirements.txt RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt # Finally, copy in the application code. -COPY ./app /code/app +COPY ./dewy /code/dewy COPY ./migrations/0001_schema.sql /code/migrations/0001_schema.sql -CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] \ No newline at end of file +CMD ["uvicorn", "dewy.main:app", "--host", "0.0.0.0", "--port", "80"] \ No newline at end of file diff --git a/README.md b/README.md index 8071014..2052688 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,10 @@ Notebook `example_notebook.ipynb` uses the REST API directly. Some skeleton code based on best practices from https://github.com/zhanymkanov/fastapi-best-practices. -In `poetry shell` or using `poetry run ${command}`: +The following commands run tests and apply linting. +If you're in a `poetry shell`, you can omit the `poetry run`: -* Linting (and formatting): `ruff check --fix` -* Formatting: `ruff format` -* Type Checking: `mypy app` +* Running tests: `poetry run pytest` +* Linting (and formatting): `poetry run ruff check --fix` +* Formatting: `poetry run ruff format` +* Type Checking: `poetry run mypy app` diff --git a/app/__init__.py b/dewy/__init__.py similarity index 100% rename from app/__init__.py rename to dewy/__init__.py diff --git a/app/chunks/__init__.py b/dewy/chunks/__init__.py similarity index 100% rename from app/chunks/__init__.py rename to dewy/chunks/__init__.py diff --git a/app/chunks/models.py b/dewy/chunks/models.py similarity index 100% rename from app/chunks/models.py rename to dewy/chunks/models.py diff --git a/app/chunks/router.py b/dewy/chunks/router.py similarity index 97% rename from app/chunks/router.py rename to dewy/chunks/router.py index 488b168..5a5b440 100644 --- a/app/chunks/router.py +++ b/dewy/chunks/router.py @@ -4,7 +4,7 @@ from llama_index.schema import NodeWithScore from loguru import logger -from app.ingest.store import StoreDep +from dewy.ingest.store import StoreDep from .models import ImageChunk, RetrieveRequest, RetrieveResponse, TextChunk diff --git a/app/collections/__init__.py b/dewy/collections/__init__.py similarity index 100% rename from app/collections/__init__.py rename to dewy/collections/__init__.py diff --git a/app/collections/models.py b/dewy/collections/models.py similarity index 100% rename from app/collections/models.py rename to dewy/collections/models.py diff --git a/app/collections/router.py b/dewy/collections/router.py similarity index 96% rename from app/collections/router.py rename to dewy/collections/router.py index 7735ab5..6514ddb 100644 --- a/app/collections/router.py +++ b/dewy/collections/router.py @@ -2,8 +2,8 @@ from fastapi import APIRouter, Path -from app.collections.models import Collection, CollectionCreate -from app.common.db import PgConnectionDep +from dewy.collections.models import Collection, CollectionCreate +from dewy.common.db import PgConnectionDep router = APIRouter(prefix="/collections") diff --git a/app/common/__init__.py b/dewy/common/__init__.py similarity index 100% rename from app/common/__init__.py rename to dewy/common/__init__.py diff --git a/app/common/db.py b/dewy/common/db.py similarity index 100% rename from app/common/db.py rename to dewy/common/db.py diff --git a/app/config.py b/dewy/config.py similarity index 93% rename from app/config.py rename to dewy/config.py index eab7cc8..3093331 100644 --- a/app/config.py +++ b/dewy/config.py @@ -1,23 +1,21 @@ from typing import Any, Optional from fastapi.routing import APIRoute -from pydantic import PostgresDsn, RedisDsn, ValidationInfo, field_validator +from pydantic import ConfigDict, PostgresDsn, RedisDsn, ValidationInfo, field_validator from pydantic_core import Url -from pydantic_settings import BaseSettings +from pydantic_settings import BaseSettings, SettingsConfigDict -from app.constants import Environment +from dewy.constants import Environment # See https://github.com/zhanymkanov/fastapi-best-practices#10-use-pydantics-basesettings-for-configs class Config(BaseSettings): """Application configuration, parsed from environment variables.""" - - class Config: - """Configurations for the pydantic BaseSettings.""" - - env_file = ".env" - env_file_encoding = "utf-8" + model_config = SettingsConfigDict( + env_file = ".env", + env_file_encoding = "utf-8", + ) DB: PostgresDsn """The Postgres database to connect to.""" diff --git a/app/constants.py b/dewy/constants.py similarity index 100% rename from app/constants.py rename to dewy/constants.py diff --git a/app/documents/__init__.py b/dewy/documents/__init__.py similarity index 100% rename from app/documents/__init__.py rename to dewy/documents/__init__.py diff --git a/app/documents/models.py b/dewy/documents/models.py similarity index 100% rename from app/documents/models.py rename to dewy/documents/models.py diff --git a/app/documents/router.py b/dewy/documents/router.py similarity index 93% rename from app/documents/router.py rename to dewy/documents/router.py index ebd289b..88245a6 100644 --- a/app/documents/router.py +++ b/dewy/documents/router.py @@ -4,11 +4,11 @@ from fastapi import APIRouter, BackgroundTasks, Body, HTTPException, Path, status, Query from loguru import logger -from app.common.db import PgConnectionDep, PgPoolDep -from app.documents.models import Document -from app.ingest.extract import extract -from app.ingest.extract.source import ExtractSource -from app.ingest.store import Store, StoreDep +from dewy.common.db import PgConnectionDep, PgPoolDep +from dewy.documents.models import Document +from dewy.ingest.extract import extract +from dewy.ingest.extract.source import ExtractSource +from dewy.ingest.store import Store, StoreDep from .models import CreateRequest diff --git a/app/ingest/__init__.py b/dewy/ingest/__init__.py similarity index 100% rename from app/ingest/__init__.py rename to dewy/ingest/__init__.py diff --git a/app/ingest/extract/__init__.py b/dewy/ingest/extract/__init__.py similarity index 100% rename from app/ingest/extract/__init__.py rename to dewy/ingest/extract/__init__.py diff --git a/app/ingest/extract/pdf.py b/dewy/ingest/extract/pdf.py similarity index 100% rename from app/ingest/extract/pdf.py rename to dewy/ingest/extract/pdf.py diff --git a/app/ingest/extract/source.py b/dewy/ingest/extract/source.py similarity index 100% rename from app/ingest/extract/source.py rename to dewy/ingest/extract/source.py diff --git a/app/ingest/store.py b/dewy/ingest/store.py similarity index 99% rename from app/ingest/store.py rename to dewy/ingest/store.py index ca74b50..fac816b 100644 --- a/app/ingest/store.py +++ b/dewy/ingest/store.py @@ -9,7 +9,7 @@ from llama_index.vector_stores import RedisVectorStore from loguru import logger -from app.config import settings +from dewy.config import settings DEFAULT_OPENAI_EMBEDDING_MODEL: str = "text-embedding-ada-002" DEFAULT_HF_EMBEDDING_MODEL: str = "BAAI/bge-small-en" diff --git a/app/main.py b/dewy/main.py similarity index 81% rename from app/main.py rename to dewy/main.py index 2a1633f..08695ee 100644 --- a/app/main.py +++ b/dewy/main.py @@ -3,11 +3,12 @@ import asyncpg from fastapi import FastAPI +from loguru import logger -from app.common import db -from app.config import app_configs, settings -from app.ingest.store import Store -from app.routes import api_router +from dewy.common import db +from dewy.config import app_configs, settings +from dewy.ingest.store import Store +from dewy.routes import api_router class State(TypedDict): @@ -15,6 +16,7 @@ class State(TypedDict): pg_pool: asyncpg.Pool + @contextlib.asynccontextmanager async def lifespan(_app: FastAPI) -> AsyncIterator[State]: """Function creating instances used during the lifespan of the service.""" @@ -23,6 +25,7 @@ async def lifespan(_app: FastAPI) -> AsyncIterator[State]: # for simple migration scripts. async with db.create_pool(settings.DB.unicode_string()) as pg_pool: if settings.APPLY_MIGRATIONS: + logger.info("Applying migrations") async with pg_pool.acquire() as conn: with open("migrations/0001_schema.sql") as schema_file: schema = schema_file.read() @@ -33,6 +36,7 @@ async def lifespan(_app: FastAPI) -> AsyncIterator[State]: "pg_pool": pg_pool, } + logger.info("Created store and db") yield state diff --git a/app/routes.py b/dewy/routes.py similarity index 52% rename from app/routes.py rename to dewy/routes.py index 0c3a64f..58b6d0a 100644 --- a/app/routes.py +++ b/dewy/routes.py @@ -1,8 +1,8 @@ from fastapi import APIRouter -from app.chunks.router import router as chunks_router -from app.collections.router import router as collections_router -from app.documents.router import router as documents_router +from dewy.chunks.router import router as chunks_router +from dewy.collections.router import router as collections_router +from dewy.documents.router import router as documents_router api_router = APIRouter(prefix="/api") diff --git a/docker-compose.yml b/docker-compose.yml index c57a7c6..7c3cb0e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.7' services: dewy: - image: app_image + image: dewy environment: ENVIRONMENT: LOCAL REDIS: "redis://default:testing123@redis:6379" diff --git a/poetry.lock b/poetry.lock index d749d30..08f6096 100644 --- a/poetry.lock +++ b/poetry.lock @@ -181,6 +181,20 @@ files = [ {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, ] +[[package]] +name = "asgi-lifespan" +version = "2.1.0" +description = "Programmatic startup/shutdown of ASGI apps." +optional = false +python-versions = ">=3.7" +files = [ + {file = "asgi-lifespan-2.1.0.tar.gz", hash = "sha256:5e2effaf0bfe39829cf2d64e7ecc47c7d86d676a6599f7afba378c31f5e3a308"}, + {file = "asgi_lifespan-2.1.0-py3-none-any.whl", hash = "sha256:ed840706680e28428c01e14afb3875d7d76d3206f3d5b2f2294e059b5c23804f"}, +] + +[package.dependencies] +sniffio = "*" + [[package]] name = "asttokens" version = "2.4.1" @@ -616,6 +630,27 @@ files = [ {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, ] +[[package]] +name = "docker" +version = "7.0.0" +description = "A Python library for the Docker Engine API." +optional = false +python-versions = ">=3.8" +files = [ + {file = "docker-7.0.0-py3-none-any.whl", hash = "sha256:12ba681f2777a0ad28ffbcc846a69c31b4dfd9752b47eb425a274ee269c5e14b"}, + {file = "docker-7.0.0.tar.gz", hash = "sha256:323736fb92cd9418fc5e7133bc953e11a9da04f4483f828b527db553f1e7e5a3"}, +] + +[package.dependencies] +packaging = ">=14.0" +pywin32 = {version = ">=304", markers = "sys_platform == \"win32\""} +requests = ">=2.26.0" +urllib3 = ">=1.26.0" + +[package.extras] +ssh = ["paramiko (>=2.4.3)"] +websockets = ["websocket-client (>=1.3.0)"] + [[package]] name = "executing" version = "2.0.1" @@ -957,24 +992,16 @@ files = [ ] [[package]] -name = "importlib-metadata" -version = "7.0.1" -description = "Read metadata from Python packages" +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, - {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] - [[package]] name = "ipykernel" version = "6.28.0" @@ -1836,6 +1863,21 @@ files = [ docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + [[package]] name = "prompt-toolkit" version = "3.0.43" @@ -1878,6 +1920,87 @@ files = [ [package.extras] test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] +[[package]] +name = "psycopg2-binary" +version = "2.9.9" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "psycopg2-binary-2.9.9.tar.gz", hash = "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win32.whl", hash = "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win32.whl", hash = "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win32.whl", hash = "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8359bf4791968c5a78c56103702000105501adb557f3cf772b2c207284273984"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:275ff571376626195ab95a746e6a04c7df8ea34638b99fc11160de91f2fef503"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f9b5571d33660d5009a8b3c25dc1db560206e2d2f89d3df1cb32d72c0d117d52"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:420f9bbf47a02616e8554e825208cb947969451978dceb77f95ad09c37791dae"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4154ad09dac630a0f13f37b583eae260c6aa885d67dfbccb5b02c33f31a6d420"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win32.whl", hash = "sha256:68fc1f1ba168724771e38bee37d940d2865cb0f562380a1fb1ffb428b75cb692"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win_amd64.whl", hash = "sha256:281309265596e388ef483250db3640e5f414168c5a67e9c665cafce9492eda2f"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win32.whl", hash = "sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win_amd64.whl", hash = "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win32.whl", hash = "sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win_amd64.whl", hash = "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957"}, +] + [[package]] name = "ptyprocess" version = "0.7.0" @@ -2138,6 +2261,67 @@ files = [ {file = "PyMuPDFb-1.23.9-py3-none-win_amd64.whl", hash = "sha256:6a2a631fbd03330347b1ecf53f5534c4f7375b44e60b5ad8f36c5c96d4e6ec35"}, ] +[[package]] +name = "pytest" +version = "7.4.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.23.3" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-asyncio-0.23.3.tar.gz", hash = "sha256:af313ce900a62fbe2b1aed18e37ad757f1ef9940c6b6a88e2954de38d6b1fb9f"}, + {file = "pytest_asyncio-0.23.3-py3-none-any.whl", hash = "sha256:37a9d912e8338ee7b4a3e917381d1c95bfc8682048cb0fbc35baba316ec1faba"}, +] + +[package.dependencies] +pytest = ">=7.0.0" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "pytest-docker-fixtures" +version = "1.3.18" +description = "pytest docker fixtures" +optional = false +python-versions = "*" +files = [ + {file = "pytest-docker-fixtures-1.3.18.tar.gz", hash = "sha256:fdbf39c26166e37defd35c97cd42c9a42b13d1be780bbda7b525f209893633c9"}, +] + +[package.dependencies] +docker = "*" +psycopg2-binary = {version = "*", optional = true, markers = "extra == \"pg\""} +pytest = "*" +requests = "*" + +[package.extras] +kafka = ["kafka-python"] +memcached = ["pymemcached"] +mysql = ["mysql-connector-python (>=8.0.17,<8.1.0)"] +pg = ["psycopg2-binary"] +rabbitmq = ["pika (==0.12.0)"] + [[package]] name = "python-dateutil" version = "2.8.2" @@ -2775,22 +2959,6 @@ files = [ pydantic = ">=1.10.13,<3.0.0" SQLAlchemy = ">=2.0.0,<2.1.0" -[[package]] -name = "sqlparse" -version = "0.4.4" -description = "A non-validating SQL parser." -optional = false -python-versions = ">=3.5" -files = [ - {file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"}, - {file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"}, -] - -[package.extras] -dev = ["build", "flake8"] -doc = ["sphinx"] -test = ["pytest", "pytest-cov"] - [[package]] name = "stack-data" version = "0.6.3" @@ -2841,20 +3009,6 @@ files = [ [package.dependencies] mpmath = ">=0.19" -[[package]] -name = "tabulate" -version = "0.9.0" -description = "Pretty-print tabular data" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, - {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, -] - -[package.extras] -widechars = ["wcwidth"] - [[package]] name = "tenacity" version = "8.2.3" @@ -3332,43 +3486,7 @@ files = [ idna = ">=2.0" multidict = ">=4.0" -[[package]] -name = "yoyo-migrations" -version = "8.2.0" -description = "Database migrations with SQL" -optional = false -python-versions = "*" -files = [ - {file = "yoyo-migrations-8.2.0.tar.gz", hash = "sha256:820606a03e262cf1cd4f59e256c28fa446425224d5b82a3d1275fd78178523e4"}, - {file = "yoyo_migrations-8.2.0-py3-none-any.whl", hash = "sha256:27dabe7432859288b0bd771093f593e3dd2ff6dd4e3b8438992a07c9a7154660"}, -] - -[package.dependencies] -importlib-metadata = ">=3.6.0" -sqlparse = "*" -tabulate = "*" - -[package.extras] -mysql = ["PyMySQL"] -postgres = ["psycopg2"] -pyodbc = ["pyodbc"] - -[[package]] -name = "zipp" -version = "3.17.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] - [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "d90641731d70d83f73caee7aacb5aaeec595f9a94dd43bab9bf8134e56d84e22" +content-hash = "cc460fe780291506e82e4feb36c43f429ed97b05c05e0984c16632a56f03b94f" diff --git a/pyproject.toml b/pyproject.toml index d04eaa0..7c6f907 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,8 +25,12 @@ pgvector = "^0.2.4" ruff = "^0.1.11" mypy = "^1.8.0" ipykernel = "^6.28.0" -yoyo-migrations = "^8.2.0" asyncpg-stubs = "^0.29.1" +httpx = "^0.26.0" +pytest = "^7.4.4" +pytest-asyncio = "^0.23.3" +pytest-docker-fixtures = {extras = ["pg"], version = "^1.3.18"} +asgi-lifespan = "^2.1.0" [build-system] requires = ["poetry-core"] @@ -62,4 +66,8 @@ strict = true warn_unreachable = true pretty = true show_column_numbers = true -show_error_context = true \ No newline at end of file +show_error_context = true + +[tool.pytest.ini_options] +testpaths = ["tests"] +asyncio_mode = "auto" \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..e5237a5 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,43 @@ +import asyncio +from asgi_lifespan import LifespanManager +from httpx import AsyncClient +import pytest + +pytest_plugins = ['pytest_docker_fixtures'] + +from pytest_docker_fixtures.images import configure as configure_image +configure_image( + "postgresql", + image = "ankane/pgvector", + version = "latest", + env = { + "POSTGRES_DB": "dewydb", + "POSTGRES_USER": "dewydbuser", + "POSTGRES_PASSWORD": "dewdbpwd", + "POSTGRES_HOST_AUTH_METHOD": "trust", + } +) + +@pytest.fixture(scope="session") +async def app(pg): + # Set environment variables before the application is loaded. + import os + (pg_host, pg_port) = pg + os.environ["DB"] = f"postgresql://dewydbuser:dewydbpwd@{pg_host}:{pg_port}/dewydb" + + from dewy.main import app + async with LifespanManager(app) as manager: + yield manager.app + +@pytest.fixture(scope="session") +async def client(app) -> AsyncClient: + async with AsyncClient(app=app, base_url="http://test"): + yield client + +# sets up a single, session-scoped async event loop. +@pytest.fixture(scope="session") +def event_loop(): + policy = asyncio.get_event_loop_policy() + loop = policy.new_event_loop() + yield loop + loop.close() \ No newline at end of file diff --git a/tests/test_collection.py b/tests/test_collection.py new file mode 100644 index 0000000..db82c29 --- /dev/null +++ b/tests/test_collection.py @@ -0,0 +1,5 @@ +async def test_read_main(client): + response = await client.get("/collections/") + print(response) + assert response.status_code == 200 + assert response.json() == {"msg": "Hello World"} \ No newline at end of file