diff --git a/README.md b/README.md index 33ac340..551c124 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ docker run --rm -it -v "$(pwd):/projects" s3rius/fastapi_template One of the coolest features is that this project is extremely configurable. You can choose between different databases and even ORMs, or you can even generate a project without a database! -Currently SQLAlchemy 2.0, TortoiseORM, Piccolo and Ormar are supported. +Currently SQLAlchemy 2.0, TortoiseORM, Piccolo, Ormar and Beanie are supported. This project can run as TUI or CLI and has excellent code documentation. @@ -82,11 +82,11 @@ Options: -n, --name TEXT Name of your awesome project -V, --version Prints current version --force Owerrite directory if it exists - --quite Do not ask for features during generation + --quiet Do not ask for features during generation --api-type [rest|graphql] Select API type for your application - --db [none|sqlite|mysql|postgresql] + --db [none|sqlite|mysql|postgresql|mongodb] Select a database for your app - --orm [none|ormar|sqlalchemy|tortoise|psycopg|piccolo] + --orm [none|ormar|sqlalchemy|tortoise|psycopg|piccolo|beanie] Choose Object–Relational Mapper lib --ci [none|gitlab_ci|github] Select a CI for your app --redis Add redis support diff --git a/fastapi_template/cli.py b/fastapi_template/cli.py index 58059f4..58f6a17 100644 --- a/fastapi_template/cli.py +++ b/fastapi_template/cli.py @@ -41,8 +41,8 @@ def disable_orm(ctx: BuilderContext) -> MenuEntry: return None -def do_not_ask_features_if_quite(ctx: BuilderContext) -> Optional[List[MenuEntry]]: - if ctx.quite: +def do_not_ask_features_if_quiet(ctx: BuilderContext) -> Optional[List[MenuEntry]]: + if ctx.quiet: return [SKIP_ENTRY] return None @@ -176,6 +176,23 @@ def checker(ctx: BuilderContext) -> bool: port=5432, ), ), + MenuEntry( + code="mongodb", + user_view="MongoDB", + description=( + "{name} is one of the most popular NoSQL databases out there.".format( + name=colored("MongoDB", color="green"), + ) + ), + additional_info=Database( + name="mongodb", + image="mongo:7.0", + async_driver="beanie", + driver_short="mongodb", + driver="mongodb", + port=27017 + ), + ) ], ) @@ -234,7 +251,7 @@ def checker(ctx: BuilderContext) -> bool: entries=[ MenuEntry( code="none", - user_view="Whithout ORMs", + user_view="Without ORMs", description=( "If you select this option, you will get only {what}.\n" "The rest {warn}.".format( @@ -246,6 +263,7 @@ def checker(ctx: BuilderContext) -> bool: MenuEntry( code="ormar", user_view="Ormar", + is_hidden=check_db(["sqlite", "mysql", "postgresql"]), pydantic_v1=True, description=( "{what} is a great {feature} ORM.\n" @@ -258,6 +276,7 @@ def checker(ctx: BuilderContext) -> bool: MenuEntry( code="sqlalchemy", user_view="SQLAlchemy", + is_hidden=check_db(["sqlite", "mysql", "postgresql"]), description=( "{what} is the most popular python ORM.\n" "It has a {feature} and a big community around it.".format( @@ -269,6 +288,7 @@ def checker(ctx: BuilderContext) -> bool: MenuEntry( code="tortoise", user_view="Tortoise", + is_hidden=check_db(["sqlite", "mysql", "postgresql"]), description=( "{what} is a great {feature} ORM.\n" "It's easy to use, it has it's own migration tooling.".format( @@ -302,6 +322,18 @@ def checker(ctx: BuilderContext) -> bool: ) ), ), + MenuEntry( + code="beanie", + user_view="Beanie", + is_hidden=check_db(["mongodb"]), + description=( + "{what} is an asynchronous object-document mapper (ODM) for MongoDB.\n" + "Data models are based on Pydantic.".format( + what=colored("Beanie", color="green"), + ) + ), + ), + ], ) @@ -310,7 +342,7 @@ def checker(ctx: BuilderContext) -> bool: code="features", description="Additional project features", multiselect=True, - before_ask=do_not_ask_features_if_quite, + before_ask=do_not_ask_features_if_quiet, entries=[ MenuEntry( code="pydanticv1", @@ -595,7 +627,7 @@ def run_command(callback: Callable[[BuilderContext], None]) -> None: help="Owerrite directory if it exists", ), Option( - ["--quite"], + ["--quiet"], is_flag=True, help="Do not ask for features during generation", ), diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/.github/workflows/tests.yml b/fastapi_template/template/{{cookiecutter.project_name}}/.github/workflows/tests.yml index 8974496..af81543 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/.github/workflows/tests.yml +++ b/fastapi_template/template/{{cookiecutter.project_name}}/.github/workflows/tests.yml @@ -67,7 +67,11 @@ jobs: MYSQL_DATABASE: "{{ cookiecutter.project_name }}" MYSQL_AUTHENTICATION_PLUGIN: "mysql_native_password" {%- endif %} - {%- if cookiecutter.db_info.name == "mysql" %} + {%- if cookiecutter.db_info.name == "mongodb" %} + MONGO_INITDB_ROOT_USERNAME: "{{ cookiecutter.project_name }}" + MONGO_INITDB_ROOT_PASSWORD: "{{ cookiecutter.project_name }}" + {%- endif %} + {%- if cookiecutter.db_info.name == "mysql" %} options: >- --health-cmd="mysqladmin ping -u root" --health-interval=15s @@ -148,6 +152,9 @@ jobs: {%- if cookiecutter.db_info.name != "sqlite" %} {{ cookiecutter.project_name | upper }}_DB_HOST: localhost {%- endif %} + {%- if cookiecutter.db_info.name == "mongodb" %} + {{ cookiecutter.project_name | upper }}_DB_BASE: admin + {%- endif %} {%- endif %} {%- if cookiecutter.enable_rmq == "True" %} {{ cookiecutter.project_name | upper }}_RABBIT_HOST: localhost diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/.gitlab-ci.yml b/fastapi_template/template/{{cookiecutter.project_name}}/.gitlab-ci.yml index 9fc0c30..f05bfa1 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/.gitlab-ci.yml +++ b/fastapi_template/template/{{cookiecutter.project_name}}/.gitlab-ci.yml @@ -69,6 +69,16 @@ pytest: MYSQL_DATABASE: {{ cookiecutter.project_name }} ALLOW_EMPTY_PASSWORD: yes {%- endif %} + + {%- if cookiecutter.db_info.name == "mongodb" %} + + # MongoDB variables + {{ cookiecutter.project_name | upper }}_DB_HOST: database + {{ cookiecutter.project_name | upper }}_DB_BASE: admin + MONGO_INITDB_ROOT_USERNAME: {{ cookiecutter.project_name }} + MONGO_INITDB_ROOT_PASSWORD: {{ cookiecutter.project_name }} + {%- endif %} + {%- if cookiecutter.enable_rmq == "True" %} # Rabbitmq variables diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/README.md b/fastapi_template/template/{{cookiecutter.project_name}}/README.md index c1d06fe..7033146 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/README.md +++ b/fastapi_template/template/{{cookiecutter.project_name}}/README.md @@ -130,6 +130,7 @@ By default it runs: You can read more about pre-commit here: https://pre-commit.com/ + {%- if cookiecutter.enable_kube == 'True' %} ## Kubernetes diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/conditional_files.json b/fastapi_template/template/{{cookiecutter.project_name}}/conditional_files.json index 54b3875..2d25b9f 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/conditional_files.json +++ b/fastapi_template/template/{{cookiecutter.project_name}}/conditional_files.json @@ -56,13 +56,18 @@ "alembic.ini", "{{cookiecutter.project_name}}/web/api/dummy", "{{cookiecutter.project_name}}/web/gql/dummy", - "{{cookiecutter.project_name}}/db_sa", "{{cookiecutter.project_name}}/tests/test_dummy.py", "deploy/kube/db.yml" ] }, + "Beanie support": { + "enabled": "{{cookiecutter.db_info.name == 'mongodb'}}", + "resources": [ + "{{cookiecutter.project_name}}/db_beanie" + ] + }, "Postgres and MySQL support": { - "enabled": "{{cookiecutter.db_info.name != 'sqlite'}}", + "enabled": "{{cookiecutter.db_info.name not in ['sqlite', 'mongodb']}}", "resources": [ "deploy/kube/db.yml" ] @@ -136,6 +141,7 @@ "{{cookiecutter.project_name}}/tests/test_dummy.py", "{{cookiecutter.project_name}}/db_piccolo/dao", "{{cookiecutter.project_name}}/db_piccolo/models/dummy_model.py", + "{{cookiecutter.project_name}}/db_beanie/models/dummy_model.py", "{{cookiecutter.project_name}}/db_sa/migrations/versions/2021-08-16-16-55_2b7380507a71.py", "{{cookiecutter.project_name}}/db_ormar/migrations/versions/2021-08-16-16-55_2b7380507a71.py", "{{cookiecutter.project_name}}/db_tortoise/migrations/models/1_20210928165300_init_dummy_pg.sql", @@ -182,6 +188,12 @@ "{{cookiecutter.project_name}}/piccolo_conf.py" ] }, + "Beanie": { + "enabled": "{{cookiecutter.orm == 'beanie'}}", + "resources": [ + "{{cookiecutter.project_name}}/db_beanie" + ] + }, "Postgresql DB": { "enabled": "{{cookiecutter.db_info.name == 'postgresql'}}", "resources": [ diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/deploy/docker-compose.dev.yml b/fastapi_template/template/{{cookiecutter.project_name}}/deploy/docker-compose.dev.yml index 3a2c37c..9a80fb8 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/deploy/docker-compose.dev.yml +++ b/fastapi_template/template/{{cookiecutter.project_name}}/deploy/docker-compose.dev.yml @@ -6,6 +6,7 @@ services: # Exposes application port. - "8000:8000" build: + context: . target: dev volumes: # Adds current directory as volume. diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/deploy/docker-compose.yml b/fastapi_template/template/{{cookiecutter.project_name}}/deploy/docker-compose.yml index 36270cf..fbd74e1 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/deploy/docker-compose.yml +++ b/fastapi_template/template/{{cookiecutter.project_name}}/deploy/docker-compose.yml @@ -56,9 +56,13 @@ services: {{cookiecutter.project_name | upper}}_DB_PORT: {{cookiecutter.db_info.port}} {{cookiecutter.project_name | upper}}_DB_USER: {{cookiecutter.project_name}} {{cookiecutter.project_name | upper}}_DB_PASS: {{cookiecutter.project_name}} + {%- if cookiecutter.db_info.name == "mongodb" %} + {{cookiecutter.project_name | upper}}_DB_BASE: admin + {%- else %} {{cookiecutter.project_name | upper}}_DB_BASE: {{cookiecutter.project_name}} {%- endif %} {%- endif %} + {%- endif %} {%- if cookiecutter.enable_rmq == 'True' %} {{cookiecutter.project_name | upper }}_RABBIT_HOST: {{cookiecutter.project_name}}-rmq {%- endif %} @@ -103,6 +107,24 @@ services: retries: 40 {%- endif %} + {%- if cookiecutter.db_info.name == "mongodb"%} + db: + image: {{cookiecutter.db_info.image}} + hostname: {{cookiecutter.project_name}}-db + restart: always + environment: + MONGO_INITDB_ROOT_USERNAME: "{{cookiecutter.project_name}}" + MONGO_INITDB_ROOT_PASSWORD: "{{cookiecutter.project_name}}" + command: "mongod" + volumes: + - {{cookiecutter.project_name}}-db-data:/data/db + healthcheck: + test: echo 'db.runCommand("ping").ok' | mongosh localhost:27017/test --quiet + interval: 10s + timeout: 5s + retries: 40 + {%- endif %} + {%- if cookiecutter.db_info.name == "mysql" %} db: diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/pyproject.toml b/fastapi_template/template/{{cookiecutter.project_name}}/pyproject.toml index 1f3e922..2816560 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/pyproject.toml +++ b/fastapi_template/template/{{cookiecutter.project_name}}/pyproject.toml @@ -86,6 +86,11 @@ aiofiles = "^23.1.0" psycopg = { version = "^3.1.9", extras = ["binary", "pool"] } {%- endif %} httptools = "^0.6.0" +{%- if cookiecutter.orm == "beanie" %} +beanie = "^1.21.0" +{%- else %} +pymongo = "^4.5.0" +{%- endif %} {%- if cookiecutter.api_type == "graphql" %} strawberry-graphql = { version = "^0.194.4", extras = ["fastapi"] } {%- endif %} @@ -213,6 +218,8 @@ env = [ "{{cookiecutter.project_name | upper}}_ENVIRONMENT=pytest", {%- if cookiecutter.db_info.name == "sqlite" %} "{{cookiecutter.project_name | upper}}_DB_FILE=test_db.sqlite3", + {%- elif cookiecutter.db_info.name == "mongodb" %} + "{{cookiecutter.project_name | upper}}_DB_BASE=admin", {%- else %} "{{cookiecutter.project_name | upper}}_DB_BASE={{cookiecutter.project_name}}_test", {%- endif %} diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/replaceable_files.json b/fastapi_template/template/{{cookiecutter.project_name}}/replaceable_files.json index 58edafb..29d2c07 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/replaceable_files.json +++ b/fastapi_template/template/{{cookiecutter.project_name}}/replaceable_files.json @@ -4,6 +4,7 @@ "{{cookiecutter.project_name}}/db_ormar", "{{cookiecutter.project_name}}/db_tortoise", "{{cookiecutter.project_name}}/db_psycopg", - "{{cookiecutter.project_name}}/db_piccolo" + "{{cookiecutter.project_name}}/db_piccolo", + "{{cookiecutter.project_name}}/db_beanie" ] } diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/conftest.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/conftest.py index 848f1fa..c150815 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/conftest.py +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/conftest.py @@ -68,6 +68,10 @@ from piccolo.conf.apps import Finder from piccolo.table import create_tables, drop_tables +{%- elif cookiecutter.orm == "beanie" %} +import beanie +from motor.motor_asyncio import AsyncIOMotorClient + {%- endif %} @@ -255,7 +259,7 @@ async def create_tables(connection: AsyncConnection[Any]) -> None: @pytest.fixture -async def dbpool() -> AsyncGenerator[AsyncConnectionPool, None]: +async def dbpool() -> AsyncGenerator[AsyncConnectionPool[Any], None]: """ Creates database connections pool to test database. @@ -332,6 +336,23 @@ async def setup_db() -> AsyncGenerator[None, None]: await drop_database(engine) {%- endif %} +{%- elif cookiecutter.orm == "beanie" %} +@pytest.fixture(autouse=True) +async def setup_db() -> AsyncGenerator[None, None]: + """ + Fixture to create database connection. + + :yield: nothing. + """ + client = AsyncIOMotorClient(settings.db_url.human_repr()) # type: ignore + from {{cookiecutter.project_name}}.db.models import load_all_models # noqa: WPS433 + await beanie.init_beanie( + database=client[settings.db_base], + document_models=load_all_models(), + ) + yield + + {%- endif %} {%- if cookiecutter.enable_rmq == 'True' %} @@ -456,7 +477,7 @@ def fastapi_app( {%- if cookiecutter.orm == "sqlalchemy" %} dbsession: AsyncSession, {%- elif cookiecutter.orm == "psycopg" %} - dbpool: AsyncConnectionPool, + dbpool: AsyncConnectionPool[Any], {%- endif %} {% if cookiecutter.enable_redis == "True" -%} fake_redis_pool: ConnectionPool, diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_beanie/dao/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_beanie/dao/__init__.py new file mode 100644 index 0000000..db62a0a --- /dev/null +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_beanie/dao/__init__.py @@ -0,0 +1 @@ +"""DAO classes.""" diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_beanie/dao/dummy_dao.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_beanie/dao/dummy_dao.py new file mode 100644 index 0000000..1f9ccac --- /dev/null +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_beanie/dao/dummy_dao.py @@ -0,0 +1,55 @@ +from typing import List, Optional + +from {{cookiecutter.project_name}}.db.models.dummy_model import DummyModel + + +class DummyDAO: + """Class for accessing dummy table.""" + + async def create_dummy_model(self, name: str) -> None: + """ + Add single dummy to session. + + :param name: name of a dummy. + """ + await DummyModel.insert_one(DummyModel(name=name)) + + async def get_all_dummies(self, limit: int, offset: int) -> List[DummyModel]: + """ + Get all dummy models with limit/offset pagination. + + :param limit: limit of dummies. + :param offset: offset of dummies. + :return: stream of dummies. + """ + return await DummyModel.find_all(skip=offset, limit=limit).to_list() + + async def filter( + self, + name: Optional[str] = None + ) -> List[DummyModel]: + """ + Get specific dummy model. + + :param name: name of dummy instance. + :return: dummy models. + """ + if name is None: + return [] + return await DummyModel.find(DummyModel.name == name).to_list() + + async def delete_dummy_model_by_name( + self, + name: str, + ) -> Optional[DummyModel]: + """ + Delete a dummy model by name. + + :param name: name of dummy instance. + :return: option of a dummy model. + """ + res = await DummyModel.find_one(DummyModel.name == name) + if res is None: + return res + await res.delete() + return res diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_beanie/models/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_beanie/models/__init__.py new file mode 100644 index 0000000..50c4bdc --- /dev/null +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_beanie/models/__init__.py @@ -0,0 +1,14 @@ +"""{{cookiecutter.project_name}} models.""" + +{%- if cookiecutter.add_dummy == "True" %} +from {{cookiecutter.project_name}}.db.models.dummy_model import DummyModel +{%- endif %} + + +def load_all_models(): # type: ignore + """Load all models from this folder.""" # noqa: DAR201 + return [ +{%- if cookiecutter.add_dummy == "True" %} + DummyModel, # type: ignore +{%- endif %} + ] diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_beanie/models/dummy_model.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_beanie/models/dummy_model.py new file mode 100644 index 0000000..ea18756 --- /dev/null +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_beanie/models/dummy_model.py @@ -0,0 +1,5 @@ +from beanie import Document + +class DummyModel(Document): + """Model for demo purpose.""" + name: str \ No newline at end of file diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_psycopg/dao/dummy_dao.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_psycopg/dao/dummy_dao.py index eb3e6b6..fee89ed 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_psycopg/dao/dummy_dao.py +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_psycopg/dao/dummy_dao.py @@ -13,7 +13,7 @@ class DummyDAO: def __init__( self, - db_pool: AsyncConnectionPool = Depends(get_db_pool), + db_pool: AsyncConnectionPool[Any] = Depends(get_db_pool), ): self.db_pool = db_pool diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_psycopg/dependencies.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_psycopg/dependencies.py index ecfd2b6..149a839 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_psycopg/dependencies.py +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db_psycopg/dependencies.py @@ -1,4 +1,5 @@ from psycopg_pool import AsyncConnectionPool +from typing import Any from starlette.requests import Request {%- if cookiecutter.enable_taskiq == "True" %} @@ -6,7 +7,7 @@ {%- endif %} -async def get_db_pool(request: Request {%- if cookiecutter.enable_taskiq == "True" %} = TaskiqDepends(){%- endif %}) -> AsyncConnectionPool: +async def get_db_pool(request: Request {%- if cookiecutter.enable_taskiq == "True" %} = TaskiqDepends(){%- endif %}) -> AsyncConnectionPool[Any]: """ Return database connections pool. diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/settings.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/settings.py index 534045c..8e54067 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/settings.py +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/settings.py @@ -56,8 +56,12 @@ class Settings(BaseSettings): db_port: int = {{cookiecutter.db_info.port}} db_user: str = "{{cookiecutter.project_name}}" db_pass: str = "{{cookiecutter.project_name}}" + {%- if cookiecutter.db_info.name != "sqlite" %} + db_base: str = "admin" + {%- else %} db_base: str = "{{cookiecutter.project_name}}" {%- endif %} + {%- endif %} db_echo: bool = False {%- endif %} diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/tests/test_dummy.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/tests/test_dummy.py index 868035e..417e702 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/tests/test_dummy.py +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/tests/test_dummy.py @@ -25,7 +25,7 @@ async def test_creation( {%- if cookiecutter.orm == "sqlalchemy" %} dbsession: AsyncSession, {%- elif cookiecutter.orm == "psycopg" %} - dbpool: AsyncConnectionPool, + dbpool: AsyncConnectionPool[Any], {%- endif %} ) -> None: """Tests dummy instance creation.""" @@ -53,12 +53,18 @@ async def test_creation( dao = DummyDAO(dbsession) {%- elif cookiecutter.orm == "psycopg" %} dao = DummyDAO(dbpool) - {%- elif cookiecutter.orm in ["tortoise", "ormar", "piccolo"] %} + {%- else %} dao = DummyDAO() {%- endif %} + instances = await dao.filter(name=test_name) assert instances[0].name == test_name + {%- if cookiecutter.orm == "beanie" %} + # Clean up the object we just inserted + await dao.delete_dummy_model_by_name(name=test_name) + {%- endif %} + @pytest.mark.anyio async def test_getting( @@ -67,7 +73,7 @@ async def test_getting( {%- if cookiecutter.orm == "sqlalchemy" %} dbsession: AsyncSession, {%- elif cookiecutter.orm == "psycopg" %} - dbpool: AsyncConnectionPool, + dbpool: AsyncConnectionPool[Any], {%- endif %} ) -> None: """Tests dummy instance retrieval.""" @@ -75,7 +81,7 @@ async def test_getting( dao = DummyDAO(dbsession) {%- elif cookiecutter.orm == "psycopg" %} dao = DummyDAO(dbpool) - {%- elif cookiecutter.orm in ["tortoise", "ormar", "piccolo"] %} + {%- else %} dao = DummyDAO() {%- endif %} test_name = uuid.uuid4().hex @@ -101,3 +107,8 @@ async def test_getting( assert response.status_code == status.HTTP_200_OK assert len(dummies) == 1 assert dummies[0]['name'] == test_name + + {%- if cookiecutter.orm == "beanie" %} + # Clean up the object we just inserted + await dao.delete_dummy_model_by_name(name=test_name) + {%- endif %} diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/dummy/schema.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/dummy/schema.py index a6553e7..82238eb 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/dummy/schema.py +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/dummy/schema.py @@ -4,6 +4,14 @@ from pydantic import ConfigDict {%- endif %} +{%- if cookiecutter.db_info.name == "mongodb" %} +{%- if cookiecutter.pydanticv1 == "True" %} +from pydantic import validator, Field +{%- else %} +from pydantic import field_validator +{%- endif %} +from bson import ObjectId +{%- endif %} class DummyModelDTO(BaseModel): @@ -13,9 +21,34 @@ class DummyModelDTO(BaseModel): It returned when accessing dummy models from the API. """ + {%- if cookiecutter.db_info.name != "mongodb" %} id: int + {%- else %} + {%- if cookiecutter.pydanticv1 == "True" %} + id: str = Field(alias="_id") + {%- else %} + id: str + {%- endif %} + {%- endif %} name: str + {%- if cookiecutter.db_info.name == "mongodb" %} + {%- if cookiecutter.pydanticv1 == "True" %} + @validator("id", pre=True) + {%- else %} + @field_validator("id", mode="before") + {%- endif %} + @classmethod + def parse_object_id(cls, document_id: ObjectId) -> str: + """ + Validator that converts `ObjectId` to json serializable `str`. + + :param document_id: Bson Id for this document. + :return: The converted str. + """ + return str(document_id) + {%- endif %} + {%- if cookiecutter.pydanticv1 == "True" %} class Config: diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/gql/context.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/gql/context.py index 23e1ddf..df5a23a 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/gql/context.py +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/gql/context.py @@ -27,6 +27,7 @@ from {{cookiecutter.project_name}}.db.dependencies import get_db_session {%- elif cookiecutter.orm == "psycopg" %} +from typing import Any from psycopg_pool import AsyncConnectionPool from {{cookiecutter.project_name}}.db.dependencies import get_db_pool @@ -47,7 +48,7 @@ def __init__( {%- if cookiecutter.orm == "sqlalchemy" %} db_connection: AsyncSession = Depends(get_db_session), {%- elif cookiecutter.orm == "psycopg" %} - db_pool: AsyncConnectionPool = Depends(get_db_pool), + db_pool: AsyncConnectionPool[Any] = Depends(get_db_pool), {%- endif %} {%- if cookiecutter.enable_kafka == "True" %} kafka_producer: AIOKafkaProducer = Depends(get_kafka_producer), diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/gql/dummy/schema.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/gql/dummy/schema.py index 3a02ed2..db26bf5 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/gql/dummy/schema.py +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/gql/dummy/schema.py @@ -9,5 +9,9 @@ class DummyModelDTO: It returned when accessing dummy models from the API. """ + {%- if cookiecutter.db_info.name != "mongodb" %} id: int + {%- else %} + id: str + {%- endif %} name: str diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/lifetime.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/lifetime.py index f99fa2e..bda7725 100644 --- a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/lifetime.py +++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/lifetime.py @@ -120,6 +120,19 @@ def _setup_db(app: FastAPI) -> None: # pragma: no cover app.state.db_session_factory = session_factory {%- endif %} +{%- if cookiecutter.orm == "beanie" %} +import beanie +from motor.motor_asyncio import AsyncIOMotorClient +from {{cookiecutter.project_name}}.db.models import load_all_models +async def _setup_db(app: FastAPI) -> None: + client = AsyncIOMotorClient(str(settings.db_url)) # type: ignore + app.state.db_client = client + await beanie.init_beanie( + database=client[settings.db_base], + document_models=load_all_models(), + ) +{%- endif %} + {%- if cookiecutter.enable_migrations != "True" %} {%- if cookiecutter.orm in ["ormar", "sqlalchemy"] %} async def _create_tables() -> None: # pragma: no cover @@ -276,7 +289,7 @@ async def _startup() -> None: # noqa: WPS430 _setup_db(app) {%- elif cookiecutter.orm == "ormar" %} await database.connect() - {%- elif cookiecutter.orm == "psycopg" %} + {%- elif cookiecutter.orm in ["beanie", "psycopg"] %} await _setup_db(app) {%- endif %} {%- if cookiecutter.db_info.name != "none" and cookiecutter.enable_migrations != "True" %} diff --git a/fastapi_template/tests/test_generator.py b/fastapi_template/tests/test_generator.py index 37da491..ffdbf18 100644 --- a/fastapi_template/tests/test_generator.py +++ b/fastapi_template/tests/test_generator.py @@ -68,6 +68,22 @@ def test_default_with_db(default_context: BuilderContext, db: str, orm: str): run_default_check(init_context(default_context, db, orm)) +@pytest.mark.parametrize( + "db", + [ + "mongodb", + ], +) +@pytest.mark.parametrize( + "orm", + [ + "beanie", + ], +) +def test_default_with_nosql_db(default_context: BuilderContext, db: str, orm: str): + run_default_check(init_context(default_context, db, orm)) + + @pytest.mark.parametrize("api", ["rest", "graphql"]) @pytest.mark.parametrize( "orm", @@ -82,6 +98,17 @@ def test_default_for_apis(default_context: BuilderContext, orm: str, api: str): run_default_check(init_context(default_context, "postgresql", orm, api)) +@pytest.mark.parametrize("api", ["rest", "graphql"]) +@pytest.mark.parametrize( + "orm", + [ + "beanie", + ] +) +def test_default_for_apis_with_nosql_db(default_context: BuilderContext, orm: str, api: str): + run_default_check(init_context(default_context, "mongodb", orm, api)) + + @pytest.mark.parametrize( "orm", [ @@ -108,6 +135,12 @@ def test_without_routers(default_context: BuilderContext, orm: str): run_default_check(context) +def test_without_routers_with_nosql_db(default_context: BuilderContext): + context = init_context(default_context, "mongodb", "beanie") + context.enable_routers = False + run_default_check(context) + + @pytest.mark.parametrize( "orm", [ @@ -123,6 +156,12 @@ def test_without_migrations(default_context: BuilderContext, orm: str): run_default_check(context) +def test_without_migrations_with_nosql_db(default_context: BuilderContext): + context = init_context(default_context, "mongodb", "beanie") + context.enable_migrations = False + run_default_check(context) + + def test_with_selfhosted_swagger(default_context: BuilderContext): default_context.self_hosted_swagger = True run_default_check(default_context) @@ -144,6 +183,12 @@ def test_without_dummy(default_context: BuilderContext, orm: str): run_default_check(context) +def test_without_dummy_with_nosql_db(default_context: BuilderContext): + context = init_context(default_context, "mongodb", "beanie") + context.add_dummy = False + run_default_check(context) + + @pytest.mark.parametrize( "api", [ diff --git a/poetry.lock b/poetry.lock index da7c968..039017b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -302,6 +302,79 @@ files = [ docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] +[[package]] +name = "greenlet" +version = "2.0.2" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" +files = [ + {file = "greenlet-2.0.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:bdfea8c661e80d3c1c99ad7c3ff74e6e87184895bbaca6ee8cc61209f8b9b85d"}, + {file = "greenlet-2.0.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9d14b83fab60d5e8abe587d51c75b252bcc21683f24699ada8fb275d7712f5a9"}, + {file = "greenlet-2.0.2-cp27-cp27m-win32.whl", hash = "sha256:6c3acb79b0bfd4fe733dff8bc62695283b57949ebcca05ae5c129eb606ff2d74"}, + {file = "greenlet-2.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:283737e0da3f08bd637b5ad058507e578dd462db259f7f6e4c5c365ba4ee9343"}, + {file = "greenlet-2.0.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d27ec7509b9c18b6d73f2f5ede2622441de812e7b1a80bbd446cb0633bd3d5ae"}, + {file = "greenlet-2.0.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:30bcf80dda7f15ac77ba5af2b961bdd9dbc77fd4ac6105cee85b0d0a5fcf74df"}, + {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26fbfce90728d82bc9e6c38ea4d038cba20b7faf8a0ca53a9c07b67318d46088"}, + {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9190f09060ea4debddd24665d6804b995a9c122ef5917ab26e1566dcc712ceeb"}, + {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d75209eed723105f9596807495d58d10b3470fa6732dd6756595e89925ce2470"}, + {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a51c9751078733d88e013587b108f1b7a1fb106d402fb390740f002b6f6551a"}, + {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:76ae285c8104046b3a7f06b42f29c7b73f77683df18c49ab5af7983994c2dd91"}, + {file = "greenlet-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:2d4686f195e32d36b4d7cf2d166857dbd0ee9f3d20ae349b6bf8afc8485b3645"}, + {file = "greenlet-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c4302695ad8027363e96311df24ee28978162cdcdd2006476c43970b384a244c"}, + {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c48f54ef8e05f04d6eff74b8233f6063cb1ed960243eacc474ee73a2ea8573ca"}, + {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1846f1b999e78e13837c93c778dcfc3365902cfb8d1bdb7dd73ead37059f0d0"}, + {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a06ad5312349fec0ab944664b01d26f8d1f05009566339ac6f63f56589bc1a2"}, + {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:eff4eb9b7eb3e4d0cae3d28c283dc16d9bed6b193c2e1ace3ed86ce48ea8df19"}, + {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5454276c07d27a740c5892f4907c86327b632127dd9abec42ee62e12427ff7e3"}, + {file = "greenlet-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:7cafd1208fdbe93b67c7086876f061f660cfddc44f404279c1585bbf3cdc64c5"}, + {file = "greenlet-2.0.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:910841381caba4f744a44bf81bfd573c94e10b3045ee00de0cbf436fe50673a6"}, + {file = "greenlet-2.0.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:18a7f18b82b52ee85322d7a7874e676f34ab319b9f8cce5de06067384aa8ff43"}, + {file = "greenlet-2.0.2-cp35-cp35m-win32.whl", hash = "sha256:03a8f4f3430c3b3ff8d10a2a86028c660355ab637cee9333d63d66b56f09d52a"}, + {file = "greenlet-2.0.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4b58adb399c4d61d912c4c331984d60eb66565175cdf4a34792cd9600f21b394"}, + {file = "greenlet-2.0.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:703f18f3fda276b9a916f0934d2fb6d989bf0b4fb5a64825260eb9bfd52d78f0"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:32e5b64b148966d9cccc2c8d35a671409e45f195864560829f395a54226408d3"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dd11f291565a81d71dab10b7033395b7a3a5456e637cf997a6f33ebdf06f8db"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0f72c9ddb8cd28532185f54cc1453f2c16fb417a08b53a855c4e6a418edd099"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd021c754b162c0fb55ad5d6b9d960db667faad0fa2ff25bb6e1301b0b6e6a75"}, + {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:3c9b12575734155d0c09d6c3e10dbd81665d5c18e1a7c6597df72fd05990c8cf"}, + {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b9ec052b06a0524f0e35bd8790686a1da006bd911dd1ef7d50b77bfbad74e292"}, + {file = "greenlet-2.0.2-cp36-cp36m-win32.whl", hash = "sha256:dbfcfc0218093a19c252ca8eb9aee3d29cfdcb586df21049b9d777fd32c14fd9"}, + {file = "greenlet-2.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:9f35ec95538f50292f6d8f2c9c9f8a3c6540bbfec21c9e5b4b751e0a7c20864f"}, + {file = "greenlet-2.0.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:d5508f0b173e6aa47273bdc0a0b5ba055b59662ba7c7ee5119528f466585526b"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:f82d4d717d8ef19188687aa32b8363e96062911e63ba22a0cff7802a8e58e5f1"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9c59a2120b55788e800d82dfa99b9e156ff8f2227f07c5e3012a45a399620b7"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2780572ec463d44c1d3ae850239508dbeb9fed38e294c68d19a24d925d9223ca"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:937e9020b514ceedb9c830c55d5c9872abc90f4b5862f89c0887033ae33c6f73"}, + {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:36abbf031e1c0f79dd5d596bfaf8e921c41df2bdf54ee1eed921ce1f52999a86"}, + {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:18e98fb3de7dba1c0a852731c3070cf022d14f0d68b4c87a19cc1016f3bb8b33"}, + {file = "greenlet-2.0.2-cp37-cp37m-win32.whl", hash = "sha256:3f6ea9bd35eb450837a3d80e77b517ea5bc56b4647f5502cd28de13675ee12f7"}, + {file = "greenlet-2.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:7492e2b7bd7c9b9916388d9df23fa49d9b88ac0640db0a5b4ecc2b653bf451e3"}, + {file = "greenlet-2.0.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:b864ba53912b6c3ab6bcb2beb19f19edd01a6bfcbdfe1f37ddd1778abfe75a30"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:ba2956617f1c42598a308a84c6cf021a90ff3862eddafd20c3333d50f0edb45b"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3a569657468b6f3fb60587e48356fe512c1754ca05a564f11366ac9e306526"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8eab883b3b2a38cc1e050819ef06a7e6344d4a990d24d45bc6f2cf959045a45b"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acd2162a36d3de67ee896c43effcd5ee3de247eb00354db411feb025aa319857"}, + {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0bf60faf0bc2468089bdc5edd10555bab6e85152191df713e2ab1fcc86382b5a"}, + {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0ef99cdbe2b682b9ccbb964743a6aca37905fda5e0452e5ee239b1654d37f2a"}, + {file = "greenlet-2.0.2-cp38-cp38-win32.whl", hash = "sha256:b80f600eddddce72320dbbc8e3784d16bd3fb7b517e82476d8da921f27d4b249"}, + {file = "greenlet-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:4d2e11331fc0c02b6e84b0d28ece3a36e0548ee1a1ce9ddde03752d9b79bba40"}, + {file = "greenlet-2.0.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:88d9ab96491d38a5ab7c56dd7a3cc37d83336ecc564e4e8816dbed12e5aaefc8"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:561091a7be172ab497a3527602d467e2b3fbe75f9e783d8b8ce403fa414f71a6"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:971ce5e14dc5e73715755d0ca2975ac88cfdaefcaab078a284fea6cfabf866df"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be4ed120b52ae4d974aa40215fcdfde9194d63541c7ded40ee12eb4dda57b76b"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94c817e84245513926588caf1152e3b559ff794d505555211ca041f032abbb6b"}, + {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1a819eef4b0e0b96bb0d98d797bef17dc1b4a10e8d7446be32d1da33e095dbb8"}, + {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7efde645ca1cc441d6dc4b48c0f7101e8d86b54c8530141b09fd31cef5149ec9"}, + {file = "greenlet-2.0.2-cp39-cp39-win32.whl", hash = "sha256:ea9872c80c132f4663822dd2a08d404073a5a9b5ba6155bea72fb2a79d1093b5"}, + {file = "greenlet-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:db1a39669102a1d8d12b57de2bb7e2ec9066a6f2b3da35ae511ff93b01b5d564"}, + {file = "greenlet-2.0.2.tar.gz", hash = "sha256:e7c8dc13af7db097bed64a051d2dd49e9f0af495c26995c00a9ee842690d34c0"}, +] + +[package.extras] +docs = ["Sphinx", "docutils (<0.18)"] +test = ["objgraph", "psutil"] + [[package]] name = "identify" version = "2.5.24" @@ -429,6 +502,78 @@ files = [ {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, ] +[[package]] +name = "msgpack" +version = "1.0.5" +description = "MessagePack serializer" +optional = false +python-versions = "*" +files = [ + {file = "msgpack-1.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:525228efd79bb831cf6830a732e2e80bc1b05436b086d4264814b4b2955b2fa9"}, + {file = "msgpack-1.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f8d8b3bf1ff2672567d6b5c725a1b347fe838b912772aa8ae2bf70338d5a198"}, + {file = "msgpack-1.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdc793c50be3f01106245a61b739328f7dccc2c648b501e237f0699fe1395b81"}, + {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cb47c21a8a65b165ce29f2bec852790cbc04936f502966768e4aae9fa763cb7"}, + {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e42b9594cc3bf4d838d67d6ed62b9e59e201862a25e9a157019e171fbe672dd3"}, + {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:55b56a24893105dc52c1253649b60f475f36b3aa0fc66115bffafb624d7cb30b"}, + {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1967f6129fc50a43bfe0951c35acbb729be89a55d849fab7686004da85103f1c"}, + {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20a97bf595a232c3ee6d57ddaadd5453d174a52594bf9c21d10407e2a2d9b3bd"}, + {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d25dd59bbbbb996eacf7be6b4ad082ed7eacc4e8f3d2df1ba43822da9bfa122a"}, + {file = "msgpack-1.0.5-cp310-cp310-win32.whl", hash = "sha256:382b2c77589331f2cb80b67cc058c00f225e19827dbc818d700f61513ab47bea"}, + {file = "msgpack-1.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:4867aa2df9e2a5fa5f76d7d5565d25ec76e84c106b55509e78c1ede0f152659a"}, + {file = "msgpack-1.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9f5ae84c5c8a857ec44dc180a8b0cc08238e021f57abdf51a8182e915e6299f0"}, + {file = "msgpack-1.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9e6ca5d5699bcd89ae605c150aee83b5321f2115695e741b99618f4856c50898"}, + {file = "msgpack-1.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5494ea30d517a3576749cad32fa27f7585c65f5f38309c88c6d137877fa28a5a"}, + {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ab2f3331cb1b54165976a9d976cb251a83183631c88076613c6c780f0d6e45a"}, + {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28592e20bbb1620848256ebc105fc420436af59515793ed27d5c77a217477705"}, + {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe5c63197c55bce6385d9aee16c4d0641684628f63ace85f73571e65ad1c1e8d"}, + {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed40e926fa2f297e8a653c954b732f125ef97bdd4c889f243182299de27e2aa9"}, + {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b2de4c1c0538dcb7010902a2b97f4e00fc4ddf2c8cda9749af0e594d3b7fa3d7"}, + {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bf22a83f973b50f9d38e55c6aade04c41ddda19b00c4ebc558930d78eecc64ed"}, + {file = "msgpack-1.0.5-cp311-cp311-win32.whl", hash = "sha256:c396e2cc213d12ce017b686e0f53497f94f8ba2b24799c25d913d46c08ec422c"}, + {file = "msgpack-1.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c4c68d87497f66f96d50142a2b73b97972130d93677ce930718f68828b382e2"}, + {file = "msgpack-1.0.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a2b031c2e9b9af485d5e3c4520f4220d74f4d222a5b8dc8c1a3ab9448ca79c57"}, + {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f837b93669ce4336e24d08286c38761132bc7ab29782727f8557e1eb21b2080"}, + {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1d46dfe3832660f53b13b925d4e0fa1432b00f5f7210eb3ad3bb9a13c6204a6"}, + {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:366c9a7b9057e1547f4ad51d8facad8b406bab69c7d72c0eb6f529cf76d4b85f"}, + {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:4c075728a1095efd0634a7dccb06204919a2f67d1893b6aa8e00497258bf926c"}, + {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:f933bbda5a3ee63b8834179096923b094b76f0c7a73c1cfe8f07ad608c58844b"}, + {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:36961b0568c36027c76e2ae3ca1132e35123dcec0706c4b7992683cc26c1320c"}, + {file = "msgpack-1.0.5-cp36-cp36m-win32.whl", hash = "sha256:b5ef2f015b95f912c2fcab19c36814963b5463f1fb9049846994b007962743e9"}, + {file = "msgpack-1.0.5-cp36-cp36m-win_amd64.whl", hash = "sha256:288e32b47e67f7b171f86b030e527e302c91bd3f40fd9033483f2cacc37f327a"}, + {file = "msgpack-1.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:137850656634abddfb88236008339fdaba3178f4751b28f270d2ebe77a563b6c"}, + {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c05a4a96585525916b109bb85f8cb6511db1c6f5b9d9cbcbc940dc6b4be944b"}, + {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56a62ec00b636583e5cb6ad313bbed36bb7ead5fa3a3e38938503142c72cba4f"}, + {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef8108f8dedf204bb7b42994abf93882da1159728a2d4c5e82012edd92c9da9f"}, + {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1835c84d65f46900920b3708f5ba829fb19b1096c1800ad60bae8418652a951d"}, + {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e57916ef1bd0fee4f21c4600e9d1da352d8816b52a599c46460e93a6e9f17086"}, + {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:17358523b85973e5f242ad74aa4712b7ee560715562554aa2134d96e7aa4cbbf"}, + {file = "msgpack-1.0.5-cp37-cp37m-win32.whl", hash = "sha256:cb5aaa8c17760909ec6cb15e744c3ebc2ca8918e727216e79607b7bbce9c8f77"}, + {file = "msgpack-1.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:ab31e908d8424d55601ad7075e471b7d0140d4d3dd3272daf39c5c19d936bd82"}, + {file = "msgpack-1.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b72d0698f86e8d9ddf9442bdedec15b71df3598199ba33322d9711a19f08145c"}, + {file = "msgpack-1.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:379026812e49258016dd84ad79ac8446922234d498058ae1d415f04b522d5b2d"}, + {file = "msgpack-1.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:332360ff25469c346a1c5e47cbe2a725517919892eda5cfaffe6046656f0b7bb"}, + {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:476a8fe8fae289fdf273d6d2a6cb6e35b5a58541693e8f9f019bfe990a51e4ba"}, + {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9985b214f33311df47e274eb788a5893a761d025e2b92c723ba4c63936b69b1"}, + {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48296af57cdb1d885843afd73c4656be5c76c0c6328db3440c9601a98f303d87"}, + {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:addab7e2e1fcc04bd08e4eb631c2a90960c340e40dfc4a5e24d2ff0d5a3b3edb"}, + {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:916723458c25dfb77ff07f4c66aed34e47503b2eb3188b3adbec8d8aa6e00f48"}, + {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:821c7e677cc6acf0fd3f7ac664c98803827ae6de594a9f99563e48c5a2f27eb0"}, + {file = "msgpack-1.0.5-cp38-cp38-win32.whl", hash = "sha256:1c0f7c47f0087ffda62961d425e4407961a7ffd2aa004c81b9c07d9269512f6e"}, + {file = "msgpack-1.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:bae7de2026cbfe3782c8b78b0db9cbfc5455e079f1937cb0ab8d133496ac55e1"}, + {file = "msgpack-1.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:20c784e66b613c7f16f632e7b5e8a1651aa5702463d61394671ba07b2fc9e025"}, + {file = "msgpack-1.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:266fa4202c0eb94d26822d9bfd7af25d1e2c088927fe8de9033d929dd5ba24c5"}, + {file = "msgpack-1.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18334484eafc2b1aa47a6d42427da7fa8f2ab3d60b674120bce7a895a0a85bdd"}, + {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57e1f3528bd95cc44684beda696f74d3aaa8a5e58c816214b9046512240ef437"}, + {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:586d0d636f9a628ddc6a17bfd45aa5b5efaf1606d2b60fa5d87b8986326e933f"}, + {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a740fa0e4087a734455f0fc3abf5e746004c9da72fbd541e9b113013c8dc3282"}, + {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3055b0455e45810820db1f29d900bf39466df96ddca11dfa6d074fa47054376d"}, + {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a61215eac016f391129a013c9e46f3ab308db5f5ec9f25811e811f96962599a8"}, + {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:362d9655cd369b08fda06b6657a303eb7172d5279997abe094512e919cf74b11"}, + {file = "msgpack-1.0.5-cp39-cp39-win32.whl", hash = "sha256:ac9dd47af78cae935901a9a500104e2dea2e253207c924cc95de149606dc43cc"}, + {file = "msgpack-1.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:06f5174b5f8ed0ed919da0e62cbd4ffde676a374aba4020034da05fab67b9164"}, + {file = "msgpack-1.0.5.tar.gz", hash = "sha256:c075544284eadc5cddc70f4757331d99dcbc16b2bbd4849d15f8aae4cf36d31c"}, +] + [[package]] name = "nodeenv" version = "1.8.0" @@ -710,6 +855,24 @@ files = [ [package.extras] plugins = ["importlib-metadata"] +[[package]] +name = "pynvim" +version = "0.4.3" +description = "Python client to neovim" +optional = false +python-versions = "*" +files = [ + {file = "pynvim-0.4.3.tar.gz", hash = "sha256:3a795378bde5e8092fbeb3a1a99be9c613d2685542f1db0e5c6fd467eed56dff"}, +] + +[package.dependencies] +greenlet = "*" +msgpack = ">=0.5.0" + +[package.extras] +pyuv = ["pyuv (>=1.0.0)"] +test = ["pytest (>=3.4.0)"] + [[package]] name = "pytest" version = "6.2.5" @@ -1070,4 +1233,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "ca51e2183f25683e533c30357685e4bcf8c0d75d92586fca2dbb0dfc20833908" +content-hash = "0a368636e28b463e5fc8e4935db409a8e1870996c19f185585305ade47beee84"